Entity Framework 4.1 - База данных Первый подход - Как найти имена столбцов для полей первичного ключа? - PullRequest
1 голос
/ 13 октября 2011

Я использую 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;
    }

но я не знаю, как найти имена полей, составляющих ПК.
Есть предложения?

Спасибо

Ответы [ 4 ]

2 голосов
/ 13 октября 2011

Я провел небольшое исследование и изменил шаблон, чтобы сгенерировать свойство, которое возвращает это для меня.

Сначала я настроил шаблон для генерации строго типизированных имен для имен полей (я ненавижу использовать строки в коде, которые могут вызвать проблемы при рефакторинге). Затем это используется для генерации свойства, которое возвращает поля первичного ключа в виде 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 голос
/ 25 января 2012

Мне было нелегко пытаться сделать почти то же самое, получить имя и значение первичного ключа во время выполнения, когда тип неизвестен, из DbContext.Я просто пытался реализовать схему аудита удалений, и каждое решение, которое я нахожу, включает в себя лишний код, который я действительно не понимаю.EntityKey недоступен из DbContext, что также сбивает с толку и раздражает.Последние 5 строк могут сэкономить вам 5 часов и 1 год облысения.Я не пытаюсь сделать это для вставок, поэтому, если вы это сделаете, вам нужно тщательно проверить эти значения, так как они могут быть 0 или нулевыми.

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;
0 голосов
/ 05 июля 2012

В качестве альтернативы, я только что натолкнулся на это решение, которое работает нормально и не требует редактирования шаблонов (+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);
}
0 голосов
/ 04 июля 2012

наткнулся на этот пост после того, как сделал что-то похожее на @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, однако для моей ситуации это было не так.

...