I have to build a number of small independent applications, that can be copied to an USB device and run from there out of the box. So I was thinking to use WPF, that uses EF code first to connect to a SQL Server CE database.
My question is about what architecture I should use. Although the apps are standalone, I would still like to decouple UI from domain from data, to have a clean separation of layers. But I also don't want to make it too complex.
So, I want to have a UI layer (WPF/MVVM) that uses the underlying domain layer (domain objects with domain logic) and repositories (that use EF code first).
My question is: what pattern should I use to make EF work in this case? Is there somewhere an example that demonstrates how to implement CRUD operations in such scenario? For example, should I create one context and leave it open; or should I implement the unit of work pattern and attach objects to other context if needed?
Or would you do it in a totally different way?
Thanks for the advice!
The EF context should be open for as short time as possible. Preferably use it within a using statement.
private static void ApplyItemUpdates(SalesOrderDetail originalItem,
SalesOrderDetail updatedItem)
{
using (AdventureWorksEntities context =
new AdventureWorksEntities())
{
context.SalesOrderDetails.Attach(updatedItem);
// Check if the ID is 0, if it is the item is new.
// In this case we need to chage the state to Added.
if (updatedItem.SalesOrderDetailID == 0)
{
// Because the ID is generated by the database we do not need to
// set updatedItem.SalesOrderDetailID.
context.ObjectStateManager.ChangeObjectState(updatedItem, System.Data.EntityState.Added);
}
else
{
// If the SalesOrderDetailID is not 0, then the item is not new
// and needs to be updated. Because we already added the
// updated object to the context we need to apply the original values.
// If we attached originalItem to the context
// we would need to apply the current values:
// context.ApplyCurrentValues("SalesOrderDetails", updatedItem);
// Applying current or original values, changes the state
// of the attached object to Modified.
context.ApplyOriginalValues("SalesOrderDetails", originalItem);
}
context.SaveChanges();
}
}
There is a method called Attach, which attachs entities to a context:
private static void AttachRelatedObjects(
ObjectContext currentContext,
SalesOrderHeader detachedOrder,
List<SalesOrderDetail> detachedItems)
{
// Attach the root detachedOrder object to the supplied context.
currentContext.Attach(detachedOrder);
// Attach each detachedItem to the context, and define each relationship
// by attaching the attached SalesOrderDetail object to the EntityCollection on
// the SalesOrderDetail navigation property of the now attached detachedOrder.
foreach (SalesOrderDetail item in detachedItems)
{
currentContext.Attach(item);
detachedOrder.SalesOrderDetails.Attach(item);
}
}
http://msdn.microsoft.com/en-us/library/bb896271.aspx
Related
I am maintaining an application which uses EF Core to persist data to a SQL database.
I am trying to implement a new feature which requires me to retrieve an object from the database (Lets pretend its an order) manipulate it and some of the order lines which are attached to it and save it back into the database. Which wouldn't be a problem but I have inherited some of this code so need to try to stick to the existing way of doing things.
The basic process for data access is :
UI -> API -> Service -> Repository -> DataContext
The methods in the repo follow this pattern (Though I have simplified it for the purposes of this question)
public Order GetOrder(int id)
{
return _context.Orders.Include(o=>o.OrderLines).FirstOrDefault(x=>x.Id == id);
}
The service is where business logic and mapping to DTOs are applied, this is what the GetOrder method would look like :
public OrderDTO GetOrder(int id)
{
var ord = _repo.GetOrder(id);
return _mapper.Map<OrderDto>(ord);
}
So to retrieve and manipulate an order my code would look something like this
public void ManipulateAnOrder()
{
// Get the order DTO from the service
var order = _service.GetOrder(3);
// Manipulate the order
order.UpdatedBy = "Daneel Olivaw";
order.OrderLines.ForEach(ol=>ol.UpdatedBy = "Daneel Olivaw");
_service.SaveOrder(order);
}
And the method in the service which allows this to be saved back to the DB would look something like this:
public void SaveOrder(OrderDTO order)
{
// Get the original item from the database
var original = _repo.GetOrder(order.Id);
// Merge the original and the new DTO together
_mapper.Map(order, original);
_repo.Save(original);
}
Finally the repositories save method looks like this
public void Save(Order order){
_context.Update(order)
_context.SaveChanges();
}
The problem that I am encountering is using this method of mapping the Entities from the context into DTOs and back again causes the nested objects (in this instance the OrderLines) to be changed (or recreated) by AutoMapper in such a way that EF no longer recognises them as being the entities that it has just given to us.
This results in errors when updating along the lines of
InvalidOperationException the instance of ProductLine cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.
Now to me, its not that there is ANOTHER instance of the object being tracked, its the same one, but I understand that the mapping process has broken that link and EF can no longer determine that they are the same object.
So, I have been looking for ways to rectify this, There are two ways that have jumped out at me as being promising,
the answer mentioned here EF & Automapper. Update nested collections
Automapper.Collection
Automapper.collection seems to be the better route, but I cant find a good working example of it in use, and the implementation that I have done doesn't seem to work.
So, I'm looking for advice from anyone who has either used automapper collections before successfully or anyone that has any suggestions as to how best to approach this.
Edit, I have knocked up a quick console app as an example, Note that when I say quick I mean... Horrible there is no DI or anything like that, I have done away with the repositories and services to keep it simple.
I have also left in a commented out mapper profile which does work, but isn't ideal.. You will see what I mean when you look at it.
Repo is here https://github.com/DavidDBD/AutomapperExample
Ok, after examining every scenario and counting on the fact that i did what you're trying to do in my previous project and it worked out of the box.
Updating your EntityFramework Core nuget packages to the latest stable version (3.1.8) solved the issue without modifying your code.
AutoMapper in fact "has broken that link" and the mapped entities you are trying to save are a set of new objects, not previously tracked by your DbContext. If the mapped entities were the same objects, you wouldn't have get this error.
In fact, it has nothing to do with AutoMapper and the mapping process, but how the DbContext is being used and how the entity states are being managed.
In your ManipulateAnOrder method after getting the mapped entities -
var order = _service.GetOrder(3);
your DbContext instance is still alive and at the repository layer it is tracking the entities you just retrieved, while you are modifying the mapped entities -
order.UpdatedBy = "Daneel Olivaw";
order.OrderLines.ForEach(ol=>ol.UpdatedBy = "Daneel Olivaw");
Then, when you are trying to save the modified entities -
_service.SaveOrder(order);
this mapped entities reach the repository layer and DbContext tries to add them to its tracking list, but finds that it already has entities of same type with same Ids in the list (the previously fetched ones). EF can track only one instance of a specific type with a specific key. Hence, the complaining message.
One way to solve this, is when fetching the Order, tell EF not to track it, like at your repository layer -
public Order GetOrder(int id, bool tracking = true) // optional parameter
{
if(!tracking)
{
return _context.Orders.Include(o=>o.OrderLines).AsNoTracking().FirstOrDefault(x=>x.Id == id);
}
return _context.Orders.Include(o=>o.OrderLines).FirstOrDefault(x=>x.Id == id);
}
(or you can add a separate method for handling NoTracking calls) and then at your Service layer -
var order = _repo.GetOrder(id, false); // for this operation tracking is false
My .net web service reads an entity from the DB and sends it to a client application.
The client application modifies some fields in the entity and then submits the entity back to the server to be updated in the DB.
The surefire but laborious way to do this goes something like:
public void Update(MyEntity updatedEntity)
{
using (var context = new MyDataContext())
{
var existingEntity = context .MyEntities.Single(e => e.Id == updatedEntity.Id);
existingEntity.FirstName = updatedEntity.Name;
existingEntity.MiddleName = updatedEntity.MiddleName;
existingEntity.LastName = updatedEntity.LastName;
// Rinse, repeat for all members of MyEntity...
context.SubmitChanges();
}
}
I don't want to go down this path because it forces me to specify each and every member property in MyEntity. This is will likely break in case MyEntity's structure is changed.
How can I take the incoming updatedEntity and introduce it to LINQ to SQL whole for update?
I've tried achieving this with the DataContext's Attach() method and entered a world of pain.
Is Attach() the right way to do it? Can someone point to a working example of how to this?
Attach is indeed one way to do it.
That said...
The surefire but laborious way to do this goes something like
The right way if you ask me.
This is will likely break in case MyEntity's structure is changed
I personally would expect to modify my Update business method in case the database schema has changed:
if it's an internal change that doesn't change the business, then there is just no reason to modify the code that calls your business method. Let your business method be in charge of the internal stuff
if it's some change that require you to modify your consumers, then so be it, it was required to update the calling code anyway (at least to populate for instance the new properties you added to the entity)
Basically, my opinon on this subject is that you shouldn't try to pass entities to your business layer. I explained why I think that in a previous answer.
I am currently having problems getting my Update function working, the function first loads an entity using session.Load() and then uses session.SaveorUpdate().
My problem is that if I do not load the session first nhibernate will not know the the relationships and therefore try and insert data which is already there and when I do load the entity first, the updated entity is overwritten by the data already in the database.
public void Update(T Entity, bool load)
{
using(ISession session = this.helper.GetSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
if(load)
{
session.Load(Entity, Entity.ID);
}
session.SaveOrUpdate(Entity);
transaction.Commit();
session.Flush();
}
}
}
In a nutshell:
load object and then bind it with new values (changes will be persisted on session.Flush() without any explicit Update() call) or
create new C# instance with bounded values, including ID, and call session.Update(myInstance)
The more complex answer could be found in one of the doc chapters:
9.4.2. Updating detached objects
Many applications need to retrieve an object in one transaction, send it to the UI layer for manipulation, then save the changes in a new transaction. (Applications that use this kind of approach in a high-concurrency environment usually use versioned data to ensure transaction isolation.) This approach requires a slightly different programming model to the one described in the last section. NHibernate supports this model by providing the method ISession.Update().
// in the first session
Cat cat = firstSession.Load<Cat>(catId);
Cat potentialMate = new Cat();
firstSession.Save(potentialMate);
// in a higher tier of the application
cat.Mate = potentialMate;
// later, in a new session
secondSession.Update(cat); // update cat
secondSession.Update(mate); // update mate
The usage and semantics of SaveOrUpdate() seems to be confusing for new users. Firstly, so long as you are not trying to use instances from one session in another new session, you should not need to use Update() or SaveOrUpdate(). Some whole applications will never use either of these methods.
Usually Update() or SaveOrUpdate() are used in the following scenario:
the application loads an object in the first session
the object is passed up to the UI tier
some modifications are made to the object
the object is passed back down to the business logic tier
the application persists these modifications by calling Update() in a second session
So, we can get an instance of some entity in one session... and close that session. Such object could be even totally brand new C# instance - with all its properties being bounded by some upper layer (e.g. MVC binder, or Web API formatter)
Later, we can use that instance and call session.Update(myInstance). NHibernate will take the ID of that entity and issue the proper update statement.
Another way could be to call Merge:
The last case can be avoided by using Merge(Object o). This method copies the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. The method returns the persistent instance. If the given instance is unsaved or does not exist in the database, NHibernate will save it and return it as a newly persistent instance. Otherwise, the given instance does not become associated with the session. In most applications with detached objects, you need both methods, SaveOrUpdate() and Merge().
read more in the doc
I have a Data Access Layer which creates a context and retrieves data (with no object tracking) and passes the information back to UI layer:-
My unit of work is a method and I release appdatacontext after executing the particular method. So I am not keeping track of the data context anywhere..
public LinqObject GetObject(){
using (appdatacontext = new DataContext()){
---code to select and return object
}
}
I will modify data using the form in UI and submit back my data to DB.
Two approaches are:-
1. Detach and reattach to a different data context using [Detach..Serialise and Attach]
*I have to do a lot of plumping code to enable this functionality*
2. Get the DB object using primary key and make changes in the selected object and SubmitChanges.
Which one is a better approach for doing this task?
I am completely against moving the unit of work to Data Access Layer wise or Web Application Life cycle (httpcontext), because I dont want to track the changes and complicate the entire application structure with unwanted plumping code . I am just using LINQ for making my retrieval and updates to DB easy.
I have never seen anyone discuss these two approaches in LINQ context, that is why I am asking for best practice.
If you don't want to go with (2) because of performance: another option is to attach a new object to submit the updates.
Foo foo=new Foo { FooId=fooId }; // create obj and set keys
context.Foos.Attach(foo);
foo.Name="test";
context.SubmitChanges();
See my answer here.
As per #Chris's comments. I have finally arrived at the solution like:-
function void SaveRow(Table.RowObject object) {
var original=null;
using (context= new DataContext())
{
context.ObjectTrackingEnabled = false;
original = {query}.Single();
}
using(context=new DataContext()){
try
{
context.Table.Attach(object, original);
context.SubmitChanges();
}
catch (Exception exception) {
saveStatus = false;
}
}
}
I kept the update checks to ensure there is concurrency checking, if I disable that I can reduce the amount of where statement generated by Linq.
This question has been asked 500 different times in 50 different ways...but here it is again, since I can't seem to find the answer I'm looking for:
I am using EF4 with POCO proxies.
A.
I have a graph of objects I fetched from one instance of an ObjectContext. That ObjectContext is disposed.
B.
I have an object I fetched from another instance of an ObjectContext. That ObjectContext has also been disposed.
I want to set a related property on a bunch of things from A using the entity in B....something like
foreach(var itemFromA in collectionFromA)
{
itemFromA.RelatedProperty = itemFromB;
}
When I do that, I get the exception:
System.InvalidOperationException occurred
Message=The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
Source=System.Data.Entity
StackTrace:
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints)
at System.Data.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value)
at System.Data.Objects.DataClasses.EntityReference`1.set_Value(TEntity value)
at
I guess I need to detach these entities from the ObjectContexts when they dispose in order for the above to work... The problem is, detaching all entities from my ObjectContext when it disposes seems to destroy the graph. If I do something like:
objectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)
.Select(i => i.Entity).OfType<IEntityWithChangeTracker>().ToList()
.ForEach(i => objectContext.Detach(i));
All the relations in the graph seem to get unset.
How can I go about solving this problem?
#Danny Varod is right. You should use one ObjectContext for the whole workflow. Moreover because your workflow seems as one logical feature containing multiple windows it should probably also use single presenter. Then you would follow recommended approach: single context per presenter. You can call SaveChanges multiple times so it should not break your logic.
The source of this issue is well known problem with deficiency of dynamic proxies generated on top of POCO entities combined with Fixup methods generated by POCO T4 template. These proxies still hold reference to the context when you dispose it. Because of that they think that they are still attached to the context and they can't be attached to another context. The only way how to force them to release the reference to the context is manual detaching. In the same time once you detach an entity from the context it is removed from related attached entities because you can't have mix of attached and detached entities in the same graph.
The issue actually not occures in the code you call:
itemFromA.RelatedProperty = itemFromB;
but in the reverse operation triggered by Fixup method:
itemFromB.RelatedAs.Add(itemFromA);
I think the ways to solve this are:
Don't do this and use single context for whole unit of work - that is the supposed usage.
Remove reverse navigation property so that Fixup method doesn't trigger that code.
Don't use POCO T4 template with Fixup methods or modify T4 template to not generate them.
Turn off lazy loading and proxy creation for these operations. That will remove dynamic proxies from your POCOs and because of that they will be independent on the context.
To turn off proxy creation and lazy loading use:
var context = new MyContext();
context.ContextOptions.ProxyCreationEnabled = false;
You can actually try to write custom method to detach the whole object graph but as you said it was asked 500 times and I haven't seen working solution yet - except the serialization and deserialization to the new object graph.
I think you have a few different options here, 2 of them are:
Leave context alive until you are done with the process, use only 1 context, not 2.
a. Before disposing of context #1, creating a deep clone of graph, using BinaryStreamer or a tool such as ValueInjecter or AutoMapper.
b. Merge changes from context #2 into cloned graph.
c. Upon saving, merge changes from cloned graph into graph created by new ObjectContext.
For future reference, this MSDN blogs link can help decide you decide what to do when:
http://blogs.msdn.com/b/dsimmons/archive/2008/02/17/context-lifetimes-dispose-or-reuse.aspx
I don't think you need to detach to solve the problem.
We do something like this:
public IList<Contact> GetContacts()
{
using(myContext mc = new mc())
{
return mc.Contacts.Where(c => c.City = "New York").ToList();
}
}
public IList<Sale> GetSales()
{
using(myContext mc = new mc())
{
return mc.Sales.Where(c => c.City = "New York").ToList();
}
}
public void SaveContact(Contact contact)
{
using (myContext mc = new myContext())
{
mc.Attach(contact);
contact.State = EntityState.Modified;
mc.SaveChanges();
}
}
public void Link()
{
var contacts = GetContacts();
var sales = GetSales();
foreach(var c in contacts)
{
c.AddSales(sales.Where(s => s.Seller == c.Name));
SaveContact(c);
}
}
This allows us to pull the data, pass it to another layer, let them do whatever they need to do, and then pass it back and we update or delete it. We do all of this with a separate context (one per method) (one per request).
The important thing to remember is, if you're using IEnumerables, they are deferred execution. Meaning they don't actually pull the information until you do a count or iterate over them. So if you want to use it outside your context you have to do a ToList() so that it gets iterated over and a list is created. Then you can work with that list.
EDIT Updated to be more clear, thanks to #Nick's input.
Ok I get it that your object context has long gone.
But let's look at it this way, Entity Framework implements unit of work concept, in which it tracks the changes you are making in your object graph so it can generate the SQL corresponding to the changes you have made. Without attached to context, there is no way it can tack changes.
If you have no control over context then I don't think there is anything you can do.
Otherwise there are two options,
Keep your object context alive for longer lifespan like session of user logged in etc.
Try to regenerate your proxy classes using self tracking text template that will enable change tracking in disconnected state.
But even in case of self tracking, you might still get little issues.