EntityFramework 可以支持 EAV 模型吗?

发布于 2024-10-29 07:28:07 字数 99 浏览 5 评论 0原文

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

Can EntityFramework support an EAV model? Is this a workable scenario, or a nightmare? I want to use an EAV model for a system, and I'd like to embrace EF if possible, but I'm concerned that these two philosophies are in conflict.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

仅一夜美梦 2024-11-05 07:28:07

这取决于您希望如何在应用程序中使用 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();
}

现在您将处理 ObjectContext< 上的 ObjectMaterializedSavingChanges 事件/代码>。在第一个处理程序中,如果物化对象实现了 IEavEntity,则您将执行 Initialize;在第二个处理程序中,您将迭代 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();
                }
            }
        }
    }
}

It depends how do you expect to use EAV in the application. EF can be used to map this:

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; }
}

This is what can be persisted and what can be queried by Linq-to-entities. Now you can make your entity usable by defining helper properties (can be used only in your application but not by persistance or querying). These helper properties can be used only for well known attributes which will always exists for entity type - optional attributes must be still accessed in collection:

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();
        }
    }
}

This is not very nice because of conversions and collection searching. If you access data multiple times they will be executed multiple times.

I didn't tried it but I think this can be avoided by implementing a similar interface by each entity:

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();
}

Now you will handle ObjectMaterialized and SavingChanges events on ObjectContext. In the first handler you will execute Initialize if materialized object implements IEavEntity in the second handler you will iterate ObjectStateManager to get all updated or inserted entities implementing IEavEntity and you will execute Finalize. Something like:

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();
                }
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文