我正在为应用程序使用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的字段名称 有什么建议吗?
谢谢
答案 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)
偶然发现了这篇文章。我以为我会发布我所做的,希望它可以减轻@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);
}