Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can EntityFramework support an EAV model?

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.

like image 833
Journeyman Avatar asked Mar 31 '11 08:03

Journeyman


People also ask

Should I use EAV model?

EAV model is excellent for rapidly evolving applications because it protects us against consequences of constant change. We can simply record new data of any structure without the need to modify the database schema.

Why Magento use EAV database model?

EAV is used because it is much more scalable than the usual normalised database structure. Developers can add attributes to any entity (product, category, customer, order etc) without modifying the core database structure.

What is EAV model in Magento?

EAV (Entity-attribute-value) is a model of storing the values of entity attributes in a certain data storage. As a data storage, Magento 2 supports MySQL-compatible databases (like MySQL, MySQL NDB Cluster, MariaDB, Percona and others).

Which Entity Framework approach is better?

As in this diagram, if we already have domain classes, the Code First approach is best suited for our application. The same as if we have a database, Database First is a good option. If we don't have model classes and a database and require a visual entity designer tool then Model First is best suited.


1 Answers

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();
                }
            }
        }
    }
}
like image 113
Ladislav Mrnka Avatar answered Oct 11 '22 10:10

Ladislav Mrnka