Entity Framework Insert Error When 2 EntityKeys Being Used - c#

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!

Related

Dynamics CRM: How to access parent entity metadata after creating child?

I am new to not only C#, but also Microsoft Dynamics and I am having difficult accessing parent entity data of my Contact via a plug in. My goal is to auto fill my contact's address with the parent account address if the contact address fields are empty. I feel this is a relatively simple process that I am over complicating it.
public void SetContactAddress()
{
// TODO: Go and get the contact's parent account record and retrieve the
// address fields. Then update the contact's address with the fields from the
// values from the account. Do not overrride the contact's address if one is provided/exists
var item = _baseModel.TargetEntity as Entity;
var parent = _baseModel.TargetEntity as Entity;
Contact contact = new Contact(); //New blank contact in memory
contact.Id = item.Id; //assign Id to the new local contact
var service = _targetContact as IOrganizationService;
Guid parentId = ((EntityReference)contact["new_parentid"]).Id;
Entity parentEntity = service.Retrieve("new_parent", parentId, new ColumnSet(true)); //retrieve parent entity
string[] addressAtrributes = {"Address1_City","Address1_Country","Address1_Line1","Address1_Line2",
"Address1_Line3", "Address1_PostalCode","Address1_StateOrProvince"};
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = key;
}
}
//contact.Address1_City = "My City"; //I can easily hard code a city in
_baseModel.OrganizationService.Update(contact);
}
First of all, you should change:
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = key;
}
}
To
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = parentEntity[key];
}
}
Because right now your code is assigning SchemaNames of addresss fields as values.
And your list of attributes is wrong, they should be all lowercase. It's SchemaName that is camelcase, so you can go with:
contact.Address1_City //Address1_City is SchemaName, so name of column in DB
or
contact["address1_city"] //this is name of the field, you can check it in Customizations area in Dynamics 365
The other way around it will not work. The list of address fields is overcomplication, better way (more maintaneable and easier to understand for the person reading code would simply be:
var parentContact = parentEntity.ToEntity<Contact>();
if(isAddressCloned) // you should only clone address if all the fields are empty, not cloning single fields if are empty
{
contact.Address1_City = parentContact.Address1_City;
contact.Address1_Country = parentContact.Address1_Country;
//etc
}
As stated in other answer, this should be done on Pre-Create of contact, so contact should be your Target (you don't have it in your sample code, but I guess you have it somewhere in your plugin) and you should skip the last update in such case.
Approach 1: (your approach)
I guess you are explicitly updating just created contact once again on post create plugin. That too code is incomplete like you are not checking empty address fields, not sure about assignment contact[key] = key;
Approach 2: (what I recommend)
on pre-create contact plugin, you can check address attributes of contact (target entity) for emptiness, then populate data for those fields in target entity itself from parent entity (custom account?) address fields retrieved.
targetEntity.Attributes["Address1_City"] = parentEntity["Address1_City"];
This way explicit update of contact can be avoided.
My proposal would be to do this using workflows instead. There are workflow extensions you can get (for free) that will enable you to execute a workflow for all associated records (in this case an account's contacts).
Then just use a workflow to get the Account's address details and overwrite the contact's address.
I don't recommend a code-first approach for anything in CRM because it requires specialist knowledge to modify. If you use a workflow it can easily be modified; for example, maybe you also want to copy business phone and/or fax number.

Error in CodeFirst Seed with migrations : Modifying a column with the 'Identity' pattern is not supported. Column: 'CreatedAt'.

I have activated migrations on my Azure Mobile Services project. I filled the new seed function Inside the Configuration.cs class of the migrations. If the tables are empty, the seed function is going without any problems. When my AddorUpdate tries to update the first object I get the error in the inner exception : "Modifying a column with the 'Identity' pattern is not supported. Column: 'CreatedAt'. Table: 'CodeFirstDatabaseSchema.Category'."
Part of my code is as follows:
context.categories.AddOrUpdate(
new Category { Id="1", Code="GEN", Text="General"},
new Category { Id="2", Code="POL", Text="Politics"},
new Category { Id="3", Code="FAS", Text="Fashion"},
new Category { Id="4", Code="PEO", Text="People"},
new Category { Id="5", Code="TEC", Text="Technology"},
new Category { Id="6", Code="SPO", Text="Sport"},
new Category { Id="7", Code="LIV", Text="Living"}
);
Here's my generic implementation of Nikatlas' solution.
Short version of the answer: You can't modify CreatedAt with a null value, so you can use this function instead:
private void AddOrUpdatePreservingCreatedAt<T> (DbSet<T> set, T item) where T : EntityData
{
var existing = set.Where(i => i.Id == item.Id).FirstOrDefault();
if (existing != null)
{
item.CreatedAt = existing.CreatedAt;
}
set.AddOrUpdate(i => i.Id, item);
}
Call it like this
AddOrUpdatePreservingCreatedAt(context.YourItems, itemToBeUpdatedOrAdded);
It seems i have found a solution to this problem.
The reason this error occurs is because of the AddOrUpdate Method.
As stated in this post : http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/
More importantly, if a match is found then the update will update all and null out any that weren’t in your AddOrUpdate.
What this means is that after the first seed, whenever your code runs it tries to update your entities correctly but it tries to pass the value null on CreatedAt field. If you look at EntityData class the CreateAt field has these attributes :
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] //Here they mark this as IDENTITY
[Index(IsClustered = true)] // Cluster index. i really dont know why ?
[TableColumn(TableColumnType.CreatedAt)]
public DateTimeOffset? CreatedAt { get; set; }
So the error occurs beacause you try to modify the CreatedAt column.
My solution was to Create a List, lookup to set CreatedAt to correct value and then addOrUpdate:
// Create List
List<Permission> permissions = new List<Permission>(new Permission[]{
new Permission { Id = "ID1" , Name = "Send SMS"},
new Permission { Id = "ID2", Name = "Send Email"} });
// Iterate through list to set CreatedAt to correct value
foreach (Permission p in permissions){
// Get the record from the db if it exists
var t = context.PermissionSet.Where(s => s.Id == p.Id).FirstOrDefault();
if (t != null){
p.CreatedAt = t.CreatedAt; //SET CreatedAt to correct Value if the record already exists
}
context.PermissionSet.AddOrUpdate(a => a.Id, p); // NOW I CAN UPDATE WITH NO PROBLEM
}
Hope this helps. :)
If you have a integer column named Id, then Entity Framework will assume that is the primary key and that it is database generated - so it is created as an IDENTITY column in the database.
You cannot specify the Id for IDENTITY columns, so you stop doing that by removing the Id = 1, Id = 2, etc
I am a bit thrown by the fact that the column you have a problem with is named "CreatedAt". It sounds like it should be a DateTime and might also be database generated, but it surely shouldn't be IDENTITY?
Anyways, the usage you probably want is the one where you specify the natural key of the entity, so that EF can identify any records that already exist. So, if CODE is the natural key then you should be writing the Seed like this:
context.categories.AddOrUpdate(
x => x.Code,//the natural key is Code
new Category { Code="GEN", Text="General"},
new Category { Code="POL", Text="Politics"},
new Category { Code="FAS", Text="Fashion"},
new Category { Code="PEO", Text="People"},
new Category { Code="TEC", Text="Technology"},
new Category { Code="SPO", Text="Sport"},
new Category { Code="LIV", Text="Living"}
);
Reference:
Take care with the AddOrUpdate method
This question, its answers and comments might help you a bit, but not much.
You can do Inserts on identity column using the solutions provided by the question. But you cannot update values of Identity Column. The only way to do updates on identity column is to mark it as not being identity. Probably by adding manual migrations.
This SO question and its answers may also be helpful.
Read here also on general MSSQL Server constraints on Updating Identity Column.
SAME PROBLEM AND SOLUTION
We had the same problem and it was a trigger in another table that had impact on the table that we had the problem on.
DETAILS OF OUR DEVELOPMENT
We developed a Xamarin App connected to an Azure Web Service. When we used the method PushAsync from iMobileServices, it gave us the error: Modifying a column with the 'Identity' pattern is not supported. Column: 'CreatedAt'.
It was strange for us as some tables did not have the problem with the web service
REASON
It seems that the trigger update got in conflict with the pushasync from the mobile device.
We disabled the trigger, switched the responsibility to the front end and it worked fine. At least for us.
We hope this soltion helps someone.

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).

How to add or remove a many-to-many relationship in Entity Framework?

There's a many-to-many UserFeed table that stands between User and Feed and denotes a twitter-like follow relationship.
It only has two fields, which form a composite key: UserID and FeedID.
I need to write a method that will subscribe or unsubscribe a user from a feed based on a boolean flag.
public void SetSubscriptionFlag (int userId, int storeId, bool subscribe)
{
}
I'm new to Entity Framework so I'm trying to find and follow an "EF-ish" way to accomplish this.
My initial thoughts are:
Instead of working with the middle UserFeed class, I should create a many-to-many Subscriptions property (EDIT: hit limitations here);
After I've done so, I'll need to fetch a User instance by ID, check whether it has given Feed in its Subscriptions and add/delete it depending on the flag and current existence;
Figure out how to avoid racing conflicts when there is a time interval before the check and adding/deleting and user manages to submit two adding or deletion requests;
Optimize my code as to avoid unneccessary SELECTs, if any occur, because all I really want to do is a single SELECT and single INSERT/DELETE.
A relevant code snippet and comment on my points is highly appreciated.
Thanks!
You can use dummy objects - it definitely works for insert and I hope it can be used for delete as well:
Create new relation:
var user = new User() { Id = userId };
context.Users.Attach(user);
var store = new Store() { Id = storeId };
context.Stores.Attach(store);
// Now context trackes both entities as "existing"
// and you can build a new relation
user.Subscriptions.Add(store);
context.SaveChanges();
Remove existing relation:
var user = new User() { Id = userId };
var store = new Store() { Id = storeId };
user.Subscriptions.Add(store);
context.Users.Attach(user);
// Now context trackes both entities as "existing"
// with "existing" relation so you can try to remove it
user.Subscriptions.Remove(store);
context.SaveChanges();

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.

Categories