实体框架4.1 - 数据库第一种方法 - 如何查找主键字段的列名?

时间:2011-10-12 21:28:41

标签: entity-framework entity-framework-4 primary-key dbcontext

我正在为应用程序使用EF4.1 for DAL,并且我将DbContext模板生成器与POCP实体一起使用。模型是从数据库创建的,因此所有字段/ PK / FK /关系都已在数据库中定义。 我需要在代码中找到实体表的字段。 某些表可能具有单个字段PK,而其他表可能具有复合PK。我需要的是有一个方法,它将返回一个实体的List,所有字段名称组成主键。它可以来自DbContext,也可以来自实体,无所谓。 我甚至可以自定义模板以在POCO实体中生成方法,如下所示:

public List<string> PrimaryKey()
    {
        List<string> pk = new List<string>();
        pk.AddRange(
            new string[] {"Field1", "Field2"});
        return pk;
    }

但我不知道如何找到组成PK的字段名称 有什么建议吗?

谢谢

4 个答案:

答案 0 :(得分:2)

我做了一些研究,我修改了模板以生成一个属性,为我返回这个。

首先,我自定义模板以生成字段名称的强类型名称(我讨厌在代码中使用字符串,这可能会在重构时导致问题)。然后,它用于生成将主键字段作为List

返回的属性

以下是对模板的更改(我使用的是ADO.NET DbContext Template Generator,但对于任何其他模板,它应该非常相似):

<#=Accessibility.ForType(entity)#>
<#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#> 
<#=code.StringBefore(" : ", code.Escape(entity.BaseType))#>
{   
<#
WriteStrongTypedPropertyNames(code, entity);    // <-- Insert this
WritePrimaryKeyProperty(code, entity);          // <-- and this

// .....

在模板文件末尾添加:

<#+
void WriteStrongTypedPropertyNames(CodeGenerationTools code, EntityType entity)
{

#>  /// <summary>
    /// Strong typed property names
    /// </summary>
    public class PropertyNames
    {
<#+
    foreach (var property in entity.Properties)
    {
#>
        public const string <#=code.Escape(property)#> = "<#=property#>";
<#+
    }
#>
    }

<#+

}

void WritePrimaryKeyProperty(CodeGenerationTools code, EntityType entity)
{

#>    /// <summary>
    /// Returns primary key as List
    /// </summary>
    public List<string> PrimaryKey
    {
        get
        {
            List<string> pk = new List<string>();
            pk.AddRange(
                new string[] {                  
<#+
            foreach (var member in entity.KeyMembers)
            {
                string delim = "";
#>
                    <#=delim#> PropertyNames.<#=code.Escape(member.Name)#>
<#+
                delim=",";
            }
#>              });
            return pk;
        }
    }

<#+

}
#>

它在实体中生成如下代码:

    /// <summary>
    /// Strong typed property names
    /// </summary>
    public class PropertyNames
    {
        public const string AppID = "AppID";
        public const string AppName = "AppName";
        public const string AppCode = "AppCode";
    }

    /// <summary>
    /// Returns primary key as List
    /// </summary>
    public List<string> PrimaryKey
    {
        get
        {
            List<string> pk = new List<string>();
            pk.AddRange(
                new string[] {                  
                     PropertyNames.AppID
                });
            return pk;
        }
    } 

希望这有助于某人

答案 1 :(得分:1)

我很难尝试做几乎相同的事情,在类型未知时从DbContext获取运行时的主键名和值。我只是试图实现删除审计方案,我找到的每个解决方案都涉及到我不太了解的多余代码。 DbContext不提供EntityKey,这也令人困惑和恼人。最后5行可以为您节省5小时和1年的秃头。我没有尝试使用Inserts,所以如果你这样做,你需要仔细检查这些值,因为它们可能是0或null。

foreach(var entry in ChangeTracker.Entries<IAuditable>()) { 
...  
case EntityState.Deleted:
...  
var oc = ((IObjectContextAdapter.this).ObjectContext; //.this is a DbContext EntityKey
ek = oc.ObjectStateManager.GetObjectStateEntry(entry.Entity).EntityKey;
var tablename= ek.EntitySetName; 
var primaryKeyField = ek.EntityKeyValues[0].Key;     //assumes only 1 primary key var
primaryKeyValue = ek.EntityKeyValues[0].Value;

答案 2 :(得分:0)

在完成与@bzamfir类似的事情后,

偶然发现了这篇文章。我以为我会发布我所做的,希望它可以减轻@Bill提到我也经历过的一些令人头疼的问题!
使用DBContext,我还修改了模板 1)将Imports System.ComponentModel.DataAnnotations添加到文件中已有的Imports列表中 2)将代码添加到每个循环的原始属性,将KeyAttribute添加到主键属性。它应该看起来像:

For Each edmProperty As EdmProperty In primitiveProperties
    If ef.IsKey(edmProperty) Then  
       #><#= code.StringBefore("<KeyAttribute()>",Environment.NewLine & CodeRegion.GetIndent(region.CurrentIndentLevel + 2))#><#
End If
    WriteProperty(code, edmProperty)
Next

您可以将此扩展方法添加到您的代码中,该代码将根据KeyAttribute找到主键。

<Extension()>
Public Function FindPrimaryKeyProperty(Of T As Class)(context As DbContext, TEntity As T) As PropertyInfo
    Return TEntity.GetType().GetProperties().Single(Function(p) p.GetCustomAttributes(GetType(KeyAttribute), True).Count > 0)
End Function

我确实认识到,如果你有多个属性用KeyAttribute标记,这个函数会失败,但是,对于我的情况,情况并非如此。

答案 3 :(得分:0)

或者,我刚刚看到这个解决方案似乎工作正常,不需要任何模板编辑(+1) http://blog.oneunicorn.com/2012/05/03/the-key-to-addorupdate/

public static IEnumerable<string> KeysFor(this DbContext context, Type entityType)
{
    Contract.Requires(context != null);
    Contract.Requires(entityType != null);

    entityType = ObjectContext.GetObjectType(entityType);

    var metadataWorkspace =
    ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
    var objectItemCollection = 
    (ObjectItemCollection)metadataWorkspace.GetItemCollection(DataSpace.OSpace);

    var ospaceType = metadataWorkspace
        .GetItems<EntityType>(DataSpace.OSpace)
        .SingleOrDefault(t => objectItemCollection.GetClrType(t) == entityType);

    if (ospaceType == null)
    {
        throw new ArgumentException(
            string.Format(
                "The type '{0}' is not mapped as an entity type.",
                entityType.Name),
            "entityType");
    }

    return ospaceType.KeyMembers.Select(k => k.Name);
}