How do you wrap a circular reference operation in a single transaction using Entity Framework 4? - entity-framework

I have a table of Persons and a table of Things, where each Thing is owned by a Person and each Person has a FavoriteThing.
Persons
PersonID int <PK>
FavoriteThingID int <FK>
Things
ThingID int <PK>
PersonID int <FK>
I would like to be able to add a Person and his/her favorite Thing, as well as setting that Thing's PersonID to the new Person, in a single transaction, without having DTC promote the transaction to distributed. Wrapping the operation in a TransactionScope() and manually managing the Entity connection does not appear to work:
ThingEntities ent = new ThingEntities();
using (TransactionScope scope = new TransactionScope())
{
ent.Connection.Open();
Thing t = ent.CreateObject<Thing>();
ent.Things.AddObject(t);
ent.SaveChanges(false);
Person p = ent.CreateObject<Person>();
t.Person = p;
ent.Persons.AddObject(p);
p.FavoriteThing = t;
ent.SaveChanges(false);
scope.Complete();
ent.AcceptAllChanges();
ent.Connection.Close();
}
This results in a "Unable to determine a valid ordering for dependent operations." exception on the second SaveChanges() call.
Is there a simple way to do this?
Thanks

You don't need TransactionScope for this. You can do it in one call to SaveChanges, which means one transaction.
Person p = ent.CreateObject<Person>();
p.FavoriteThing = ent.CreateObject<Thing>();
ent.Persons.AddObject(p);
ent.SaveChanges();

Related

how to insert parent child while doing entityframework.bulkinsert?

I am using Entityframework 6, I am trying to insert a parent-child kind of data in the database.
I am using Entityframework.BulkInsert to insert data. I have autoIncrement int primary key in all the tables
My object is as follows :
var parentObjects= new List<parentObject>();
var childObjects= new List<childObject>();
for (int i = 0; i <= 100; i++)
{
var parentObj= new parentObject()
{
Name="p1",
Address="a1"
};
childObjects= SeedInitializer.ChildItems.OrderBy(x => new Random().Next()).Take(2).ToList();//this gets 2 child objects
foreach (var childObj in childObjects)
{
childObj .ParentObject= parentObj;
//childObj .CommissionPlanId = i; //tried this still not working
parentObj.ChildObjects.Add(childObj );
}
parentObjects.Add(parentObj);
}
//when I do a quickwatch on parentObjects, i see child objects in each parentObject, but
//with the last id of parentObject
context.BulkInsert(parentObjects, 1000);
context.SaveChanges();
On save only 2 records are created in the childObject are created with a wrong parentObject id i.e. 0
I am not able to understand why child items are not getting created, while parent objects are getting created. Can someone help me understand where I am doing the mistake ?
Disclaimer: I'm the owner of EntityFramework.BulkInsert
You cannot.
This feature has never been implemented.
Disclaimer: I'm the owner of Entity Framework Extensions
However, this new library (not free), can easily handle this kind of scenario.
The BulkSaveChanges work exactly like SaveChanges (handle parent/child) but way faster!
All methods are supported:
Bulk SaveChanges
Bulk Insert
Bulk Delete
Bulk Update
Bulk Merge
Example
// Easy to use
context.BulkSaveChanges();
// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);
I do not think there is an easy way to accomplish this task, because in order to insert the children, you have to actually finish inserting the parents and get their ids. Normal EF inserts have the advantage that each INSERT will also embed a SELECT to fetch just generated identifier, so that it can use to push it for children (if any).
One possible solution is the following:
Add a Guid RefProperty to the ParentObject type which is also persisted
Add a Guid BatchId to the ParentObject type which is also persisted
Add a Guid RefProperty to the ChildObject type which is not persisted
Save the whole structure by using the following (mainly pseudocode) sequence
var batchId = new Guid();
parentObjects.ForEach(item => item.BatchId = batchId);
// set RefProperty for all parents and children to reflect proper parentation
TransactionScope scope = null;
try
{
context.BulkInsert(parentObjects, 1000);
var newParents = context.ParentObjects.Where(_ => _.BatchId = batchId);
var refPropMap = newParents.ToDictionary(_ => _.RefProperty, _ => ParentId);
var childObjects.ForEach(item => item.ParentId = refPropMap[item.RefProperty]);
context.BulkInsert(childObjects, 1000);
DataAccess.SaveChanges();
scope.Complete();
}
catch (Exception exc)
{
scope?.Dispose();
}
Note: this is not tested
This is quite ugly, but it should do the trick: minimize round-trips to SQL Server and still be one single transaction.
In order to make the SELECT faster, an index on ParentObject table should be placed on BatchId including (covering) its key.
Alternative: change design for these tables to not use auto-increments, but UNIQUEIDENTIFIER columns. This way, all identifiers can be set before making the inserts.

Having a hard time with Entity Framework detached POCO objects

I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);

Loading particular peoperties for entity and assigning it as reference to another entity

Example:
I am entering a new invoice. For this invoice I need to enter a customer. Lets assume that we retrieved a list of customers:
var list = Context.Set<Customer>().ToList();
Here I see two issues:
1) I do not need to bring all information for customer, I only need Id, Code and Name
2) Customer in current DbContext is read-only, so it would be nice if it is possible to tell DbContext not to monitor their states, to improve performance.
Questions:
1) Can we load only partial data for customer, but still be able to assign it to Invoice (see code bellow)?
2) Can we tell DbContext not to monitor Customers for changes, and still be able to do this:
Invoice.Customer = CustomerList[10];
There's not a direct way to do exactly what you want, but you might be able to achieve your goals with some compromise.
I do not need to bring all information for customer, I only need Id,
Code and Name
There isn't a way for EF to create a partially loaded entity, but you could create an anonymous type:
Context.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name}).Tolist()
If you could live with the new anonymous type then use that, or you could then iterate through that list, creating actual customer objects.
Customer in current DbContext is read-only, so it would be nice if it
is possible to tell DbContext not to monitor their states, to improve
performance.
EF provides an Extension of AsNoTracking() which will do exactly what you're looking for:
var list = Context.Set<Customer>().AsNoTracking().ToList();
Depending on what you choose from above, the following code may change, but this code does achieve what you're looking for. Partially loads the customer, but still allows you to attach the customer to the invoice.
Note: You'll need to attach the customer to your context before you can use it, and then setting it to a state of Unchanged will prevent it from overwriting exiting data.
m = new Model();
var list = m.Customers.Select(c => new {Id = c.CustomerId, Code = c.Code, Name = c.Name});
List<Customer> customerList = new List<Customer>();
foreach (var item in list)
{
customerList.Add(new Customer()
{
CustomerId = item.Id,
Code = item.Code,
Name = item.Name
});
}
Invoice i = new Invoice();
var customer = customerList.First();
m.Customers.Attach(customer);
m.Entry(customer).State = EntityState.Unchanged;
i.Customer = customer;
m.Invoices.Add(i);
m.SaveChanges();

TransactionScope with Object context on dependant objects

I'm working on a MVC3 application and i'm using the Entity Framework linked to an Oracle database (11G R2).
I'm encountering an issue when i'm trying to use a single object context inside a TransactionScope.
Here is the code :
using (TransactionScope scope = new TransactionScope())
{
using (Entities context = new Entities())
{
// Right insert
T_RIGRIGHT entity1 = new T_RIGRIGHT()
{
RIGCODE = "test1",
RIGINSERTLOGIN = "aco",
RIGINSERTDATE = DateTime.Now,
RIGUPDATELOGIN = "aco",
RIGUPDATEDATE = DateTime.Now
};
context.AddToT_RIGRIGHT(entity1);
context.SaveChanges();
// Right/Profile insert
T_RIPRIGHTPROFILE entity2 = new T_RIPRIGHTPROFILE()
{
PROID = 3,
RIGID = entity1.RIGID,
RIPINSERTLOGIN = "aco",
RIPINSERTDATE = DateTime.Now,
RIPUPDATELOGIN = "aco",
RIPUPDATEDATE = DateTime.Now
};
context.AddToT_RIPRIGHTPROFILE(entity2);
context.SaveChanges(); // SaveChanges fails due to the FK constraint on table
}
scope.Complete();
}
Let me explain the code...
First I create an entity called entity1 as a T_RIGRIGHT element.
The I instanciate a T_RIPRIGHTPROFILE element that uses the id of the T_RIGRIGHT element created before.
The execution fails on the second context.SaveChanges() and the exception concerns the Foreign Key constraint on the table T_RIPRIGHTPROFILE (requires a T_RIGRIGHT).
Hope my explanations are clear enough
Is there any way to make it works ?
P.S. : I apologize for my english as it's not my native language.
You are trying to assign the FK entity1.RIGID of an entity that has not been committed to the DB:
RIGID = entity1.RIGID,
If you look at entity1 closely you will see that RIGID is 0 by default - instead you should set the navigation property representing the FK relationship:
RIG = entity1,
This will enable EF to properly relate these entities, for this entity1 does not have to be committed to the DB yet, so you do not even need the extra SaveChanges() call.
Also in your scenario you do not need a TransactionScope - EF uses a transaction internally already in SaveChanges() - based on the suggested changes you only need one SaveChanges() call and hence no outer transaction scope is needed.

EF 4.0 - Many to Many relationship - problem with deletes

My Entity Model is as follows:
Person , Store and PersonStores Many-to-many child table to store PeronId,StoreId
When I get a person as in the code below, and try to delete all the StoreLocations, it deletes them from PersonStores as mentioned but also deletes it from the Store Table which is undesirable.
Also if I have another person who has the same store Id, then it fails saying
"The DELETE statement conflicted with the REFERENCE constraint \"FK_PersonStores_StoreLocations\". The conflict occurred in database \"EFMapping2\", table \"dbo.PersonStores\", column 'StoreId'.\r\nThe statement has been terminated" as it was trying to delete the StoreId but that StoreId was used for another PeronId and hence exception thrown.
Person p = null;
using (ClassLibrary1.Entities context = new ClassLibrary1.Entities())
{
p = context.People.Where(x=> x.PersonId == 11).FirstOrDefault();
List<StoreLocation> locations = p.StoreLocations.ToList();
foreach (var item in locations)
{
context.Attach(item);
context.DeleteObject(item);
context.SaveChanges();
}
}
The problem is that you don't actually want to delete the store itself, just the relation between the store and the person. Try something like this instead:
Person p = null;
using (ClassLibrary1.Entities context = new ClassLibrary1.Entities())
{
p = context.People.Where(x=> x.PersonId == 11).FirstOrDefault();
p.StoreLocations.Clear();
context.SaveChanges();
}
That will get your person, remove all the stores from his list of stores, and save the changes. Note that you might need an include statement on the first row in the using block, depending on how your ObjectContext is configured.