EntityFramework可以支持EAV模型吗?

时间:2011-03-31 08:32:29

标签: entity-framework entity-attribute-value

EntityFramework可以支持EAV模型吗?这是一个可行的场景,还是一场噩梦?我想在系统中使用EAV模型,如果可能的话我想拥抱EF,但我担心这两种哲学是冲突的。

1 个答案:

答案 0 :(得分:9)

这取决于您希望如何在应用程序中使用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上的SavingChangesObjectContext个事件。在第一个处理程序中,如果物化对象在第二个处理程序中实现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();
                }
            }
        }
    }
}