1

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

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

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

谢谢

4

4 回答 4

2

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

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

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

<#=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;
        }
    } 

希望这可以帮助某人

于 2011-10-12T23:45:45.510 回答
1

我很难尝试做几乎相同的事情,当类型未知时,在运行时从 DbContext 获取主键名称和值。我刚刚开始尝试实施删除审计方案,而我发现的每个解决方案都涉及我不太了解的多余代码。EntityKey 在 DbContext 中不可用,这也令人困惑和烦人。最后 5 行可能会为您节省 5 小时和 1 年的秃顶时间。我没有尝试对插入进行此操作,因此如果您这样做,则需要仔细检查这些值,因为它们可能为 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;
于 2012-01-25T19:50:13.447 回答
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);
}
于 2012-07-05T13:51:43.690 回答
0

在做了类似于@bzamfir 的事情后偶然发现了这篇文章。我想我会发布我所做的事情,希望它可以减轻@Bill 提到的一些我也经历过的头痛!
使用 DBContext,我还修改了模板。
1) 添加Imports System.ComponentModel.DataAnnotations到文件中已有的导入列表 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 标记的属性,则此函数将失败,但是,对于我的情况,情况并非如此。

于 2012-07-03T20:17:46.813 回答