Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Entity Framework ObjectContext correct implementation of Unit Of Work Pattern?

Entity Framework 4 - STE - simple DB with single table Blogs having BlogID PK column...

var samplesDbEntities = new SamplesDBEntities();
var blogId = Guid.NewGuid();
samplesDbEntities.Blogs.AddObject(new Blog() { BlogID = blogId });
var objectSetResult = samplesDbEntities.Blogs
                                       .Where(p => p.BlogID == blogId)
                                       .SingleOrDefault();

(result of code execution => objectSetResult == null after the last line)

AFAIK, ObjectContext is implementation of UoW pattern and in which case I guess I should get the result back from ObjectSet (Repository) just "marked as transient" Can someone explain me what I am doing wrong and why objectSetResult has null value here?

(Yes, I am aware of ObjectStateManager, but to me it is more of a patch for the upper mentioned architectural problem)

like image 581
Nikola Malovic Avatar asked Jun 25 '10 14:06

Nikola Malovic


2 Answers

You need to call

samplesDbEntities.SaveChanges();

before requerying for the object.

var samplesDbEntities = new SamplesDBEntities(); 
var blogId = Guid.NewGuid(); 
samplesDbEntities.Blogs.AddObject(new Blog() { BlogID = blogId }); 

samplesDbEntities.SaveChanges();

var objectSetResult = samplesDbEntities.Blogs 
                                   .Where(p => p.BlogID == blogId) 
                                   .SingleOrDefault(); 

Update

The reason why you are not getting the added user back in objectSetResult is that calling the SingleOrDefault method of an IQueryable object results in a database query (an actual SQL query string is generated accoding to the "where" condition, etc.), and since the object is not (yet) in the database, it doesn't get returned. The new object is, however, attached to the context, and it's EntityState is set to "Added". According to MSDN, objects in the Added state do not have original values in the ObjectStateEntry. The state of objects inside an object context is managed by the ObjectStateManager. So if you want to check that the object has really been attached, you can fetch it by calling GetObjectStateEntry:

var samplesDbEntities = new SamplesDBEntities();
Blog blog = new Blog() { BlogID = Guid.NewGuid() };
samplesDbEntities.Blogs.AddObject("Blogs", blog);

Blog addedBlog = (Blog)context.ObjectStateManager.GetObjectStateEntry(blog).Entity;

Also note that the EntityState of the retrieved object is "Added".

To sum up - regarding your initial question about whether it is a correct implementation of UnitOfWork, I don't see why not. It does indeed maintain a list of objects and tracks the changes, etc, etc. The issue you encountered, however, is related to the fact that the you are fetching data from the underlying provider, rather from the list of objects currently attached to the context.

like image 54
Yakimych Avatar answered Oct 18 '22 02:10

Yakimych


The pattern violated in your example is not Unit Of Work pattern but Identity Mapping.

Unit of Work track changes made to objects by your code instead of you take care about that manually.

Identity Mapping pattern enacts object context to have single entity instance for single value of primary key.

It is strange for me but Entity Framework (as well as LINQ 2 SQL) does not map object identity in every situation, and situation described above is one of such cases.

like image 42
STO Avatar answered Oct 18 '22 01:10

STO