EntityFramework可以支持EAV模型吗?

Jou*_*man 6 entity-framework entity-attribute-value

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

Lad*_*nka 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; }
}
Run Code Online (Sandbox Code Playgroud)

这是可以持久化的内容以及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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于转换和集合搜索,这不是很好.如果多次访问数据,它们将被多次执行.

我没有尝试过,但我认为可以通过每个实体实现类似的接口来避免这种情况:

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();
}
Run Code Online (Sandbox Code Playgroud)

现在你将处理ObjectMaterializedSavingChanges事件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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)