Может ли EntityFramework поддерживать модель EAV?

может ли EntityFramework поддерживать модель EAV? Это осуществимый сценарий или кошмар? Я хочу использовать модель EAV для системы, и я хотел бы принять EF, если это возможно, но я обеспокоен тем, что эти две философии находятся в конфликте.

1 ответов


это зависит от того, как вы ожидаете использовать EAV в приложении. EF можно использовать для отображения этого:

public partial class Entity
{
    // Key
    public virtual int Id { get; set; }
    // Other common properties 

    // Attributes
    public virtual ICollection<EavAttriubte> Attributes { get; set; }
}

// The simplest implementation
public class EavAttribute
{
    // Key
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual string Value { get; set; }
}

это то, что может быть сохранено и что может быть запрошено LINQ-to-entities. Теперь вы можете использовать свою сущность, определив вспомогательные свойства (можно использовать только в приложении, но не путем сохранения или запроса). Эти вспомогательные свойства можно использовать только для хорошо известных атрибутов, которые всегда будут существовать для типа сущности-дополнительные атрибуты должны быть доступны в коллекции:

public partial class Entity
{
    // Just example without error handling
    public decimal Price
    {
        get
        {
            return Int32.Parse(Attributes.Single(a => a.Name == "Price"));
        }
        set
        {
            Attributes.Single(a => a.Name == "Price").Value = value.ToString();
        }
    }
}

это не очень приятно из-за конверсий и поиска коллекции. Если вы получаете доступ к данным несколько раз, они будут выполняться несколько раз.

я не пробовал, но я думаю, что этого можно избежать, реализуя аналогичный интерфейс каждым объектом:

public interface IEavEntity
{
    // loads attribute values from Attributes collection to local fields
    // => conversion will be done only once
    void Initialize();
    // saves local values back to Attributes collection
    void Finalize();
}

теперь вы будете обрабатывать ObjectMaterialized и SavingChanges событий ObjectContext. В первом обработчике вы выполните Initialize если материализованный объект реализует IEavEntity в второй обработчик вы будете повторять ObjectStateManager чтобы получить все обновленные или вставленные объекты, реализующие IEavEntity и вы будете выполнять Finalize. Что-то вроде:

public void OnMaterialized(object sender, ObjectMaterializedEventArgs e)
{
    var entity = e.Entity as IEavEntity;
    if (entity != null)
    {
        entity.Initialize();
    } 
}

public void SavingChanges(object sender, EventArgs e)
{
    var context = sender as ObjectContext;
    if (context != null)
    {
        foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(
            EntityState.Added | EntityState.Modified))
        {
            if (!entry.IsRelationship)
            {
                var entity = entry.Entity as IEavEntity;
                if (entity != null)
                {
                    entity.Finalize();
                }
            }
        }
    }
}