实体框架 5 MaxLength

16

我使用EF4和一段代码来获取实体中MaxLength的值,代码如下:

public static int? GetMaxLength(string entityTypeName, string columnName)
        {
            int? result = null;
            using (fooEntities context = new fooEntities())
            {
                Type entType = Type.GetType(entityTypeName);
                var q = from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
                                  .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
                        from p in (meta as EntityType).Properties
                        .Where(p => p.Name == columnName
                                    && p.TypeUsage.EdmType.Name == "String")
                        select p;

                var queryResult = q.Where(p =>
                {
                    bool match = p.DeclaringType.Name == entityTypeName;
                    if (!match && entType != null)
                    {
                        match = entType.Name == p.DeclaringType.Name;
                    }

                    return match;

                }).Select(sel => sel.TypeUsage.Facets["MaxLength"].Value);
                if (queryResult.Any())
                {
                    result = Convert.ToInt32(queryResult.First());
                }

                return result;
            }
        }

但是,我升级到EF5后就出现了这个错误消息:

...fooEntities'  does not contain a definition for 'MetadataWorkspace' and no
extension method 'MetadataWorkspace' accepting a first argument of type
'...fooEntities' could be found (are you missing a using directive or an assembly
 reference?)

如何从EF5中获取元数据?


我在使用EF5时遇到了各种与程序集引用有关的问题。您尝试过使用软件包管理器控制台卸载并重新安装EF5吗?这在以前解决了我的一些问题。请确保对解决方案中的所有项目都执行此操作。值得一试。 - Automatico
这是一个全新的VS2012安装和一个全新的项目。我正在将代码从一个旧项目中导入。 - Eonasdan
注意:它不会从数据库中获取列的 MaxLength 值。如果您没有在模型属性中注释长度,例如:[Column("SomeColumn", TypeName="varchar(10)")],则此代码仅返回 property.TypeUsage.Facets["MaxLength"].Value 的最大值,这将导致转换异常。如果您想直接从数据库获取长度,即在此情况下的 10,请查看我的答案:https://stackoverflow.com/a/71330103/8644294 - Ash K
4个回答

30

这段代码非常方便实用。我进行了一些重构,发现非常有用,所以想在这里发布。

public static int? GetMaxLength<T>(Expression<Func<T, string>> column)
    {
        int? result = null;
        using (var context = new EfContext())
        {
            var entType = typeof(T);
            var columnName = ((MemberExpression) column.Body).Member.Name;

            var objectContext = ((IObjectContextAdapter) context).ObjectContext;
            var test = objectContext.MetadataWorkspace.GetItems(DataSpace.CSpace);

            if(test == null)
                return null;

            var q = test
                .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
                .SelectMany(meta => ((EntityType) meta).Properties
                .Where(p => p.Name == columnName && p.TypeUsage.EdmType.Name == "String"));

            var queryResult = q.Where(p =>
                                          {
                                              var match = p.DeclaringType.Name == entType.Name;
                                              if (!match)
                                                  match = entType.Name == p.DeclaringType.Name;

                                              return match;

                                          })
                .Select(sel => sel.TypeUsage.Facets["MaxLength"].Value)
                .ToList();

            if (queryResult.Any())
                result = Convert.ToInt32(queryResult.First());

            return result;
        }
    }

您可以这样调用:

GetMaxLength<Customer>(x => x.CustomerName);

假设您的DbContext中定义了一个类型为Customer的DbSet,并且该Customer类型具有一个名为CustomerName的属性,其定义了MaxLength。
这对于创建模型属性非常有用,可以将文本框的最大长度设置为数据库字段的最大长度,始终确保两者相同。

什么是EFContext?如果它是你的应用程序特定生成的类,你应该将其作为参数传递,而不是嵌入静态函数中-请参见IOC。 - Michael Freidgeim
老兄,你太牛了!我刚花了一个小时都没搞明白怎么运行它。你的代码几乎可以直接使用,只需记得检查一下上下文,这真是太棒了。 - Termiux
1
完美运行!我将其更改为抛出异常而不是返回null。 - Jowen
1
我不得不包含:System.Linq,System.Linq.Expressions,System.Data.Entity.Infrastructure和System.Data.Metadata.Edm。将其添加到我的MVC应用程序中的抽象祖先控制器中,它可以很好地完成工作。 - Mark Micallef
1
还将其改为抛出异常而不是返回null,并且我不再调用EfContext(),而是从调用者传递DbContext。方法签名如下:protected static int GetMaxColumnLength<T>(DbContext db, Expression<Func<T, string>> column) - Mark Micallef
注意:它不会从数据库中获取列的 MaxLength 值。如果您没有在模型属性中注释长度,例如:[Column("SomeColumn", TypeName="varchar(10)")],则此代码仅返回 property.TypeUsage.Facets["MaxLength"].Value 的最大值,这将导致转换异常。如果您想直接从数据库获取长度,即在此示例中为 10,请查看我的答案:https://stackoverflow.com/a/71330103/8644294 - Ash K

11

我将mccow002的示例重构为一个可直接复制粘贴的扩展方法类:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;

public static class DbContextExtensions
{

    // get MaxLength as an extension method to the DbContext
    public static int? GetMaxLength<T>(this DbContext context, Expression<Func<T, string>> column)
    {
        return (int?)context.GetFacets<T>(column)["MaxLength"].Value;
    }

    // get MaxLength as an extension method to the Facets (I think the extension belongs here)
    public static int? GetMaxLength(this ReadOnlyMetadataCollection<Facet> facets)
    {
        return (int?)facets["MaxLength"].Value;            
    }

    // just for fun: get all the facet values as a Dictionary 
    public static Dictionary<string,object> AsDictionary(this ReadOnlyMetadataCollection<Facet> facets) {
        return facets.ToDictionary(o=>o.Name,o=>o.Value);
    }


    public static ReadOnlyMetadataCollection<Facet> GetFacets<T>(this DbContext context, Expression<Func<T, string>> column)
    {
        ReadOnlyMetadataCollection<Facet> result = null;

        var entType = typeof(T);
        var columnName = ((MemberExpression)column.Body).Member.Name;

        var objectContext = ((IObjectContextAdapter)context).ObjectContext;
        var test = objectContext.MetadataWorkspace.GetItems(DataSpace.CSpace);

        if (test == null)
            return null;

        var q = test
            .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
            .SelectMany(meta => ((EntityType)meta).Properties
            .Where(p => p.Name == columnName && p.TypeUsage.EdmType.Name == "String"));

        var queryResult = q.Where(p =>
        {
            var match = p.DeclaringType.Name == entType.Name;
            if (!match)
                match = entType.Name == p.DeclaringType.Name;

            return match;

        })
            .Select(sel => sel)
            .FirstOrDefault();

        result = queryResult.TypeUsage.Facets;

        return result;

    }

}

1
底部的.Select(sel => sel)没有任何作用,对吧?我把它删掉了,好像也没关系。 - David
你是正确的,在这种情况下,Select确实没有做任何事情。但这只是因为lambda表达式返回了自己的输入参数。如果它是像.Select(sel => sel.SomeProperty)这样的东西,那么删除Select将会返回一个不同的result - Walter Stabosz

10

这意味着你不仅升级了EF,还更改了API。有两种API - 核心的ObjectContext API和精简的DbContext API。您的代码依赖于ObjectContext API(这是EF4中唯一可用的API),但EF5使用DbContext API(自EF4.1以来添加到单独的EntityFramework.dll程序集中)。如果您想使用新的EF功能和之前的代码,只需升级到.NET 4.5即可。

如果您还想使用新的API,您需要更新很多现有代码,但仍然可以从DbContext获取ObjectContext并使您的方法再次工作。您只需使用以下代码片段:

var objectContext = ((IObjectContextAdapter)context).ObjectContext;

在您的代码中使用objectContext而不是context


是的,我应该讲得更明白。我创建了一个新项目来升级到“最新版本”。我进行了更改,但是出现了错误Project.Model.fooEntities是“类型”,但像“变量”一样使用 - Eonasdan
抱歉,是我的错。我错误地写了这个例子。请检查已更正的版本。 - Ladislav Mrnka
这解决了那个错误,但现在我又遇到了一个无关的错误。叹气 :) 谢谢 - Eonasdan

0

我曾遇到类似的问题,解决方法在这里;

  MyDBEntities ctx = new MyDBEntities();
        var objectContext = ((IObjectContextAdapter)ctx).ObjectContext;

        var cols = from meta in objectContext.MetadataWorkspace.GetItems(DataSpace.CSpace)
                   .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
                   from p in (meta as EntityType).Properties
                      .Where(p => p.DeclaringType.Name == "TableName")
                   select new
                   {
                       PropertyName = p.Name                          
                   };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接