Using ExecuteTransactionRequest to create related entities - c#

I've got Entity types that are in a parent-child relationship.
Since ExecuteTransactionRequest executes multiple message requests in one tranasaction, would the following work as I intend it to?
There are 3 parents with no children to start with:
//Create a 4th parent
cs_parent parent4 = new cs_parent{ cs_name = "p4" };
CreateRequest createParentRequest = new CreateRequest { Target = parent4 };
request.Requests.Add(createParentRequest);
EntityCollection parents
= context.RetrieveMultiple(/*fetchExpression to get all parents (I'm expecting 4 now)*/);
//Create a child for each parent
foreach (var p in parents.Entities)
{
cs_child child = new cs_child
{
cs_parentid = p.ToEntityReference();
}
CreateRequest createChildRequest = new CreateRequest { Target = child };
request.Requests.Add(createChildRequest);
}
response = (ExecuteTransactionResponse)context.Execute(request);
Would I be getting 4 parents with one child each then, or only 3 since when I'm retrieving multiple, the 4th one hasn't been created yet (?)?
If not, how do I revise my code ideally with still one Execute command at the end?

I haven't actually run your code for myself to be 100% sure, but it looks like it's going to error out because the fourth parent record doesn't have the necessary info on it at the time you assign it as an EntityReference on the child entity. You can work around this easily though. CRM allows for this type of situation where inter-dependent records can all be submitted within one batch Create request. Normally when you create a record in CRM, the system assigns it a unique identifier (guid), but you can override this simply by assigning the guid yourself, then you have what you need to set it as a EntityReference on other objects. So when you create the fourth parent, you would have something like this:
cs_parent parent4 = new cs_parent { cs_name = "p4",cs_parentId = Guid.NewGuid());
Just guessing at the Id field on your entity, but you get the idea.
One thing I'm not sure from your code sample, what context is, so I can't say for sure if doing a retrieve on that will return your parent4 object. You might need to have two loops, one for existing cs_parent records to create child records for them, and another loop to create child records for parent records in the request.Requests list that are not yet in the system... Food for thought.

Edit: I realise I misread part of the question, but the following still applies to the new parent record you want to create. Add it to the ExecuteTransactionRequest Requests.
Add the children to the parent Entity's RelatedEntities collection (pseudo-example):
// Create parent object
var invoice = new Entity("invoice");
// Create list of child objects
var invoiceDetailList = new List<Entity>() { new Entity("invoicedetail"), new Entity("invoicedetail") };
// Add child records to parent record's RelatedEntities
invoice.RelatedEntities.Add(new Relationship("invoice_invoicedetails"), new EntityCollection(invoiceDetailList));
// Add to ExecuteTransactionRequest.
transactionRequest.Requests.Add(new CreateRequest { Target = invoice });
This way you don't need to know the parent record's GUID up front.

Related

EF6 Code First: When does EF magic populate collection after parent relation set?

In earlier EF, in a 1-parent : many-child relation, if the parent reference is set on the child, then the child is also added to the parent's collection of children. This isn't the same in code first?
Using the example below, one parent Entry can have many child FindKeys:
EfTestModel eft = new EfTestModel();
Entry en = eft.Entries.First(); //Get an entry to work with
int cnt1 = en.FindKeys.Count(); //=0 en's FindKeys collection is empty
FindKey fk = eft.FindKeys.Create(); //Create new entity with proxies
fk.Entry = en; //Set the parent relation
//other fields updated here
int cnt2 = en.FindKeys.Count(); //=0 en's FindKeys collection still empty
eft.FindKeys.Add(fk); //Add the new FindKey to the context
int cnt3 = en.FindKeys.Count(); //=1 fk now added to FindKeys collection
Why is cnt2=0 instead of 1, since parent relation was set? cnt3=1: why does adding it to the context populate the FindKeys set? Is there a manual way to trigger the relational magic?
If I'm updating rather than adding a new FindKey (therefore .Add(fk) can't be called) is there another way to populate the relations and therefore make sure all the relations are correct?

Entity Framework Insert Error When 2 EntityKeys Being Used

Following is a sample of the error I'm getting. The code names and such have been changed to make it more readible and it has been 'dumbed down' to just show the parts I'm having trouble with.
TABLE STRUCTURE:
SUMMARY
I'm trying to add a new CHILD item along with an ADDRESS using Entity Framework 3.5 and I'm getting duplicate entry errors when I use 2 entitykey references in the process.
CODE EFFORTS
First I tried to use parent references while creating my entity's:
public bool AddChild(Guid parentId, string firstName, string lastName, string address, string city, int provinceCode)
{
bool success = false;
using (Entities e = new Entities("connection string")
{
// prep address record
ADDRESS a = new ADDRESS
{
City = city,
Address = address,
PROVINCEReference = new System.Data.Objects.DataClasses.EntityReference<PROVINCE> { EntityKey = new System.Data.EntityKey("Entities.PROVINCEs", "ID", provinceCode) },
};
// prep owner record
CHILD c = new CHILD
{
PARENTReference = new System.Data.Objects.DataClasses.EntityReference<PARENT> { EntityKey = new EntityKey("Entities.PARENTs", "ID", new Guid(parentId)) },
ADDRESS = a,
FirstName = firstName,
LastName = lastName,
};
using (System.Transactions.TransactionScope transaction = new System.Transactions.TransactionScope())
{
try
{
e.AddToCHILDs(c);
e.AddToADDRESSes(a);
// Save changes pessimistically. This means that changes must be accepted manually once the transaction succeeds.
e.SaveChanges(false);
}
catch { }
}
}
return success;
}
This doesn't work if I try to reference both foreign keys; however, if I just try to reference one or the other it works fine. The first one will always work. The second one will give the error. It doesn't matter what order they are in. If I use them by themselves or in other methods just like this, they work fine.
I also tried to get actual items rather than just references such as:
PARENT o = e.PARENTs.First(x => x.ID.Equals(id));
PROVINCE p = e.PROVINCEs.First(x => x.ID.Equals(1));
a.PROVINCE = p;
c.ADDRESS = a
c.PARENT = o;
And, I tried playing with the .Attach() method after finding similar errors:
e.Attach(p);
e.Attach(o);
When I do it the first way (my preferred manner), whichever item is added first will work with no issues. When I try the second one I'm getting an error (An item with the same key has already been added). If I try to do it by pulling the actual records rather than just using a reference I'm getting the same type of error directly from the SQL Server 2005 database.
It makes sense after reading about this that it has to do with the Context and the fact that the code may be referencing 2 different versions of the parent records; however, I can't find a good way around it.
All I want to do in the end, is to insert a child record with a new address. The child record must reference the parent record and the address record must reference the province record. Preferrably, I'd like to do this via references to cut down on the sql transactions.
Thanks!
EDIT
Tried to create 2 of my own seperate entityKey objects and 2 seperate EntityReference<> objects and now I'm getting the following error when I try to use the second EntityReference<> object to assign the relationship/reference:
The EntityReference could not be initialized, because the relationship
manager for object to which the entity reference belongs is already
attached to an ObjectContext.
EDIT
Using a workaround that doesn't need any relationship/reference information from Alex's answer at: Entity Framework EntityKey / Foreign Key problem
I would still prefer not to use this because it forces you to have to do checks to see what info is already in the context and then it creates "fake" records to get through it as a work-around. If anyone has any more info I'd love to hear!

LINQ to SharePoint 2010 getting error "All new entities within an object graph must be added/attached before changes are submitted."

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.
I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.
I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:
"All new entities within an object graph must be added/attached before changes are submitted."
this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.
With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.
I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,
I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.
Here is my code:
using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
{
SPSite sitecollection = new SPSite("http://dev");
SPWeb web = sitecollection.OpenWeb();
SPList theOldList = web.Lists.TryGetList("OldList_Physicians");
//Create new Physician entities.
foreach(SPListItem l in theOldList.Items)
{
PhysiciansItem p = new PhysiciansItem()
{
FirstName = (String)l["First Name"],
Title = (String)l["Last Name"],
MiddleInitial = (String)l["Middle Init"],
ProviderNumber = Convert.ToInt32(l["Provider No"]),
Gender = ConvertGender(l),
UndergraduateSchool =(String)l["UG_School"],
MedicalSchool = (String)l["Med_School"],
Residency = (String)l["Residency"],
Fellowship = (String)l["Fellowship"],
Internship = (String)l["Internship"],
PhysicianType = ConvertToPhysiciantype(l),
Specialty = ConvertSpecialties(l),
InsurancesAccepted = ConvertInsurance(l),
};
ctx.Physicians.InsertOnSubmit(p);
}
ctx.SubmitChanges(); //this is where it flakes out
}
}
//Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.
private Gender ConvertGender(SPListItem l)
{
Gender g = new Gender();
if ((String)l["Sex"] == "M")
{
g = Gender.M;
}
else g = Gender.F;
return g;
}
//Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and
//DO (Doctor of Osteopathic Medicine). State Regualtions require this information to be attached
//to a physician's profile.
private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
{
ProviderTypesItem p = new ProviderTypesItem();
p.Title = (String)l["Provider_Title:Title"];
p.Intials = (String)l["Provider_Title"];
return p;
}
//Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
private EntitySet<Item> ConvertSpecialties(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
Item i = new Item();
i.Title = (String)l["Provider Specialty"];
theEntityList.Add(i);
if ((String)l["Provider SubSpecialty"] != null)
{
Item theSubSpecialty = new Item();
theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
theEntityList.Add(theSubSpecialty);
}
return theEntityList;
}
//Process and add insurance accepted.
//Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
//list.
private EntitySet<Item> ConvertInsurance(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
if ((bool)l["TennCare"] == true)
{
Item TenncareItem = new Item();
TenncareItem.Title = "TennCare";
theEntityList.Add(TenncareItem);
}
if ((bool)l["Medicare"] == true)
{
Item MedicareItem = new Item();
MedicareItem.Title = "Medicare";
theEntityList.Add(MedicareItem);
}
if ((bool)l["Commercial"] == true)
{
Item CommercialItem = new Item();
CommercialItem.Title = "Commercial";
theEntityList.Add(CommercialItem);
}
return theEntityList;
}
}
So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).
You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)
Generate the entity for just that one list (Physicians) in some temporary folder
Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
Paste the lookup id & value properties in this partial Physicians class.
Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:
PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.
It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

Save a relation with between two entities an N-N association

I've a Entity Framework 4.0, with poco object. the edmx model file is generated from the database.
This datacontext is accessed through WCF service, it's only mean that I receive some objects and I need to attach them to the current datacontext(or reload them with the key correspondance).
Everything seems to work fine, except for one case:
I've a N-N relationship between two table, so I've an association table, without any field other than ID of two tables:
LINQ transform this into the following schema, this seems to be right.
When I retrieve data, there is no problem, data I've inserted myself in the Right_group are correctly transformed into "new object in my collection of Rights/Groups".
But if I try to modify something and save, it doesn't work
public void SaveRights(Group group, List<Rights> rights){
//here, group and rights are objects attached to the database
group.Rights.Clear();
group.Rights.AddRange(rights);
_dataContext.SaveChanges();
}
So my question is: How to save this "relationship" of two objects ?
Thank you!
If you want to avoid loading the objects from the database first you can do it like this(Code taken from one of my aplications so you will have to adapt it):
public void AddAndRemovePersons(int id, int[] toAdd, int[] toDelete)
{
var mailList = new MailList { ID = id, ContactInformations = new List<ContactInformation>() };
this.db.MailLists.Attach(mailList);
foreach (var item in toAdd)
{
var ci = new ContactInformation { ID = item };
this.db.ContactInformations.Attach(ci);
this.db.ObjectStateManager.ChangeRelationshipState(mailList, ci, ml => ml.ContactInformations, System.Data.EntityState.Added);
}
foreach (var item in toDelete)
{
var ci = new ContactInformation { ID = item };
this.db.ContactInformations.Attach(ci);
this.db.ObjectStateManager.ChangeRelationshipState(mailList, ci, ml => ml.ContactInformations, System.Data.EntityState.Deleted);
}
}
I found deleting the relationship as hard as creating it so I left that code in there. One thing about this solution is that both the maillist and the contacts exist prior to this function being run. I attach them to make the state manager track them.
If you are adding new objects that you also want to save you would use the
this.db.MailLists.AddObject(you new item here)
I hope that helps!
Just a thought... how are the keys setup in the Right_Group table? If you use both IDRight and IDGroup together as primary key - this problem might occur. One suggetion is to add a new column (ID) into the Right_Group table, and having this ID as the primary key. Then use foreign keys on the other columns (IDRight, IDGroup) respectivly.

How to update a second ObjectContext with deletions made on first one

I have a many-to-many relation Patients - PatientDevices - Devices and a basic edmx-model of it (no poco, automatic generation). PatientDevices is generated as an entity, because it has more columns than the foreign keys.
When I create two ObjectContexts and add a new PatientDevice into the first one, the second one has it also. When deleting this relation from the first one, it is still in the second context:
var entities1 = new TherapyDatabaseDevEntities();
var entities2 = new TherapyDatabaseDevEntities();
entities1.PatientDevices.AddObject(new PatientDevice
{
Patient = entities1.Patients.First(),
Device = entities1.Devices.First()
});
entities1.SaveChanges();
var relation1a = entities1.Patients.First().PatientDevices.ToList();
var relation2a = entities2.Patients.First().PatientDevices.ToList();
entities1.PatientDevices.DeleteObject(entities1.PatientDevices.ToList().Last());
entities1.SaveChanges();
var relation1b = entities1.Patients.First().PatientDevices.ToList();
var relation2b = entities2.Patients.First().PatientDevices.ToList();
relation1a and relation2a both have one entry. relation1b has no entry, but relation2b has one entry. Even if working with refreshes before the query:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients);
entities2.Refresh(RefreshMode.StoreWins, entities2.PatientDevices);
entities2.Refresh(RefreshMode.StoreWins, entities2.Devices);
var relation1b = entities1.Patients.First().PatientDevices.ToList();
// still 1 entry
var relation2b = entities2.Patients.First().PatientDevices.ToList();
Is there a possibility to bring the second context up to date or do I have to create another ObjectContext?
Edit
I found out that if I do this:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients.First().PatientDevices);
the relation gets updated properly. It's a pity that without the refresh entities2.PatientDevices does not contain the deleted object anymore, but entities2.Patients.First().PatientDevices still has it.
Is this intended behavior?
If you must have multiple contexts and work directly with your entities, have a look at the Attach and Detach methods, which as the names suggest are used to associate/dissociate an object from the context it was retrieved from. Note though that these methods only detatch the object you pass as an argument, not associated objects, so you'd probably have to walk through the connected objects detatching each one, which is messy.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
entities1.Detach(patient1);
//loop through associated entities calling Detach on each
var entities2 = new TherapyDatabaseDevEntities();
entities2.Attach(patient1);
//loop through associated entities calling Attach on each
My preference would be to use viewmodels so that you aren't editing the entities directly, but representations of them. When a user explicitly saves an object, retrieve that object and update the changes from that object only on a fresh context.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
... dispose of your context, it's no longer needed, and make your changes here
var entities2 = new TherapyDatabaseDevEntities();
var patient2 = entities2.Patients.Single(p => p.Id = 12345);
patient2.Property1 = patient1.Property1;
... update with other changes (there's ways to make this code cleaner, just showing simplest example)
entities2.SaveChanges();
entities2.Dispose();
Some reference material on Attach/Detach here - http://msdn.microsoft.com/en-us/library/bb896271.aspx
Do a search on entity framework context lifetimes, there's a lot of discussion on this subject which might help you decide on a route that suits your needs.

Categories