Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework 5 - Looking for Central Point to Execute Custom Code after Entity is Loaded from Database

I am using Entity Framework 5 with Code First approach and using Fluent API for Entity configuration. My project has one particular Product Entity which gets half of its data from the database and the other half from a Data Contract retrieved via a WCF Client (its a 3rd party system used to manage product inventory). The Data Contract is a member of the Product Entity class (property or method I haven't decided yet).

I prefer not to have any WCF Client logic contain within the Entities. I'd prefer to keep this logic in Repository code (DbContext, DbSet, etc.).

So is there a technique to hook into Entity Framework (or intercept) just after a Product Entity is retrieved from the database? I should note the Product Entity appears as a navigation property on other Entities. If a hook or intercept is possible then what that means is I can retrieve the Data Contract from the SOAP service immediately after EF loaded the Product Entity from the database. The benefit for my project is the WCF Client retrieval code does not need to be repeated throughout the application.

One idea I had was to implement IDbSet for the Data Contract and the IDbSet would be responsible for retrieving it. And then somehow trick EF into thinking its a navigation property on the Product Entity. But I wasn't sure if a database DbSet can be mixed with a non-database IDbSet all within the same DbContext. And also the other question - how would EF know to retrieve a navigation property from the IDbSet implantation? I'd prefer to know if this idea is possible before investing time into it. I'd also prefer to know where to start looking.

Please note I've been working with .NET for over 10 years but this EF5 stuff is still relatively new to me.

Thanks in advance.

-Sam

like image 381
Sam Changtum Avatar asked Feb 05 '13 16:02

Sam Changtum


2 Answers

Today I found an event in the Entity Framework that seems to be what I am looking for. ObjectContext.ObjectMaterialized Event. Apparently, DbContext implements IObjectContextAdapter which in-turn exposes the ObjectContext. From there I can subscribe to the ObjectMaterialized event.

MSDN Reads: Occurs when a new entity object is created from data in the data source as part of a query or load operation.

The following code demonstrates how I used the ObjectMaterialized event to solve my problem in which one of my preferences was to have a central point to place the WCF client access logic.

// seperate assembly - does not use Domain.Repositories assembly
namespace Domain.Models
{
    // the data contract
    [DataContract]
    public class ProductInventoryState
    {
        [DataMember]
        public int StockStatus { get; set; }

        [DataMember]
        public IEnumerable<String> SerialNumbers { get; set; }

        // etc....
    }

    // the entity
    public class Product
    {
        public Guid Key { get; set; }
        public string ProductCode { get; set; }
        public ProductInventoryState InventoryState { get; set; }
        // etc....
    }
}

// seperate assembly - uses Domain.Models assembly
namespace Domain.Repositories
{
    public class MainRepository : DbContext
    {
        public MainRepository()
        {
            ((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += ObjectContext_ObjectMaterialized;
        }

        protected void ObjectContext_ObjectMaterialized(object sender, ObjectMaterializedEventArgs e)
        {
            if (e.Entity == null)
                return;

            if (e.Entity is Product)
            {
                Product product = (Product)e.Entity;

                // retrieve ProductInventoryState from 3rd party SOAP API
                using (ThirdPartyInventorySystemClient client = new ThirdPartyInventorySystemClient())
                {
                    // use ProductCode to retrieve the data contract
                    product.InventoryState = client.GetInventoryState(product.ProductCode);
                }
            }
        }    
    }
}
like image 196
Sam Changtum Avatar answered Nov 01 '22 18:11

Sam Changtum


1.) You can write your own EF Provider (but that is no small task)

2.) You can attach items to the context but not save them.

The entity.State can be set as Not modified after attaching. You could also remove such entries from Context prior to save changes

3) You can Write a repository fascade that check EF and Checks location 2 and combines the result.

On the question of navigation properties. You would need to specify these very carefully to avoid issues. Not lazy loaded or not even modelled.

I wouldnt try and mix them personally.
You can tell EF to ignore some properties. So you can have a Nice original POCO, but only model the bits that are on the DB.

The POCO would then collect the rest.

I use a fascade with events myself to act on KEY methods on a context/DBset. So I can trigger events on attach , get, save etc.

good luck

like image 20
phil soady Avatar answered Nov 01 '22 19:11

phil soady