【问题标题】:Entity Framework code first. Find primary key首先是实体框架代码。查找主键
【发布时间】:2011-11-07 09:51:57
【问题描述】:

如何找到类的哪个属性是Entity Framework Code First实体POCO的主键?

请注意 Id / 类名 +“Id”的字符串匹配是一个不好的选择。必须有某种方法来挖掘实体框架使用的约定并可靠地获取关键属性。

提前致谢。

【问题讨论】:

    标签: entity-framework-4.1 ef-code-first


    【解决方案1】:

    您可以要求映射元数据来获取关键属性的名称(可能不止一个):

    ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
    ObjectSet<YourEntity> set = objectContext.CreateObjectSet<YourEntity>();
    IEnumerable<string> keyNames = set.EntitySet.ElementType
                                                .KeyMembers
                                                .Select(k => k.Name);
    

    一旦你有了键名,你就可以使用反射来访问它们的值。

    如您所见,该方法恢复为 ObjectContext API,因为 DbContext API 仅适用于您无需关心映射元数据等细节的简单场景。

    【讨论】:

    • 谢谢。标记为答案。我想评论一下,将接口添加到您的上下文类(例如,IYourDataContext)并将 IObjectContextAdapter 继承到该 IYourDataContext 是一个更好的主意。这样你就不必投射它了。
    • 鉴于现在已经 5 岁了,有没有办法做到这一点而无需大量反思?我的问题的上下文是尝试在不进行跟踪的情况下实现 Find 方法,例如 SingleOrDefault() 但在不能假定知道实体键名的基类中。谢谢大家。
    • 有没有办法找出哪些属性有(唯一)索引,而不是找到主键?
    【解决方案2】:

    如果它对任何人有帮助,我需要能够在事先不知道类型的情况下做到这一点(所以我不能轻易做到 CreateObjectSet&lt;YourEntity&gt;(),因为我不知道 YourEntity),所以我能够将@Ladislav 的解决方案改编为以下内容:

    // variable "type" is a System.Type passed in as a method parameter
    ObjectContext objectContext = ((IObjectContextAdapter)this.context).ObjectContext;
    IEnumerable<string> retval = (IEnumerable<string>)objectContext.MetadataWorkspace
        .GetType(type.Name, type.Namespace, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace)
        .MetadataProperties
        .Where(mp => mp.Name == "KeyMembers")
        .First()
        .Value;
    

    MetadataWorkspace.GetType 需要类型名称和命名空间的字符串而不是 System.Type 似乎有点奇怪,但这是我能找到的最好的。

    【讨论】:

    • 谢谢,你救了我的命!
    • 有没有办法找出哪些属性有(唯一)索引,而不是找到主键?
    【解决方案3】:

    由于 Table Per Type 继承,我对上述两种方法都有问题。我的工作版本(基于@S'pht'Kr 的解决方案,但出于这个原因使用 DataSpace.OSpace 而不是 DataSpace.CSpace)如下:

            protected IEnumerable<string> GetKeyPropertyNames()
            {
                var objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter) this.Context).ObjectContext;
    
                return GetKeyPropertyNames(typeof (TEntity), objectContext.MetadataWorkspace);
            }
    
            private static IEnumerable<string> GetKeyPropertyNames(Type type, MetadataWorkspace workspace)
            {
                EdmType edmType;
    
                if (workspace.TryGetType(type.Name, type.Namespace, DataSpace.OSpace, out edmType))
                {
                    return edmType.MetadataProperties.Where(mp => mp.Name == "KeyMembers")
                        .SelectMany(mp => mp.Value as ReadOnlyMetadataCollection<EdmMember>)
                        .OfType<EdmProperty>().Select(edmProperty => edmProperty.Name);
                }
    
                return null;
            }
    

    【讨论】:

    • 太棒了。这对我有帮助
    【解决方案4】:

    在 EF 6.1 中,有一个 Db() 扩展方法使这更容易。

    例子:

    public static IEnumerable<string> GetPrimaryKeyPropertyNames(DbContext db, Type entityType)
    {
        return db.Db(entityType).Pks.Select(x => x.PropertyName);
    }
    

    【讨论】:

    • 它的性能如何?底层反射很重?
    • 我不知道,据我所知,EntityFramework 是基于大量反思开始的。
    • 链接的包已过时,不再在 NuGet 上列出
    • 感谢您指出这一点,它可能已被较新版本的 EF 淘汰。
    • @anjdreas 或者是因为now costs $700
    【解决方案5】:

    像用户 rashleighp 一样,我也想要用户 Ladislav Mrnka 的答案的变体,它只需要在运行时知道类型,而不需要在编译时知道类型。就像用户 rashleighp 一样,用户 S'pht'Kr 的解决方案对我不起作用,但他的解决方案确实有效。下面,我通过提供对我有用的他的答案的更简单版本来为这次对话做出贡献。但是,我刚刚了解了用户 anjdreas 的解决方案,这就是我将要使用的解决方案。

    // variable "type" is a System.Type passed in as a method parameter
    ((IObjectContextAdapter)context)
        .ObjectContext
        .MetadataWorkspace
        .GetItem<EntityType>(type.FullName, DataSpace.OSpace)
        .KeyProperties
        .Select(p => p.Name);
    

    【讨论】:

      猜你喜欢
      • 2015-09-02
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 2012-04-09
      相关资源
      最近更新 更多