This problem is similar to my previously asked question. When I query data using the Entity Framework (EF) I always use the MergeOption.NoTracking option because I end up taking my EF generated objects and mapping them to view models which have lovely attributes decorated on the property to enforce validation and so on.
I am trying to add a foreign key relationship using the EF, but anytime I do I am getting the following exception:
The object being attached to the source object is not attached to the same ObjectContext as the source object
Here is my code:
public static void UpdateDownloadFileVersion(DownloadFile downloadFile, int[] selectedVersions) {
using (SupportEntity supportContext = new SupportEntity()) {
supportContext.DownloadFiles.Attach(downloadFile);
var productVersionIdsToAdd = (from v in selectedVersions
where (downloadFile.ProductVersions.Any(pv => pv.Id == v) == false)
select v).ToList();
foreach (var productVersionId in productVersionIdsToAdd) {
var productVersion = new ProductVersion() { Id = productVersionId };
downloadFile.ProductVersions.Attach(productVersion); //Exception happens here.
downloadFile.ProductVersions.Add(productVersion);
}
supportContext.SaveChanges();
}
}
This is where Stub Entities become very very useful...
var productVersion = new ProductVersion() { Id = productVersionId };
supportContext.AttachTo("ProductVersions", productVersion);
Here is a good article
In above case, when attached productVersion is assigned to product versions' entity, productversion entity gets attached to context, with EntityState=Added. Entire graph will be in or out of context.
Related
I have this EF query:
var records = mydata.Where(a => some condition).ToList();
Records is a list of Record objects. Record is a database table that has a one to many relationship called "relation".
The EF object represents that member variable as Collection<Relation>.
After the where above, I see that "relation" member variable contains a collection of 18 entities.
I need to duplicate those Record objects, so that I am detaching them first, this way:
var detached = this.DetachEntities(records, db, "RecordId");
Where this.DetachEntities is defined this way:
private T DetachEntity<T>(T entity, Repositories.GestionActivosEntities db, string keyName) where T : class
{
db.Entry(entity).State = EntityState.Detached;
if (entity.GetType().GetProperty(keyName) != null)
{
entity.GetType().GetProperty(keyName).SetValue(entity, 0);
}
return entity;
}
private List<T> DetachEntities<T>(List<T> entities, Repositories.GestionActivosEntities db, string keyName) where T : class
{
foreach (var entity in entities)
{
this.DetachEntity(entity, db, keyName);
}
return entities;
}
After I detached the entities, I am doing:
db.Record.AddRange(detached);
The problem, is that, after I detached the list, the relation is not copied also, resulting that "relation" member variable contains no elements.
How can I keep the relation elements after detaching?
EDIT:
This shows the debugging session:
The breakpoint is placed in the line where the query is done. The cursor shows the line after the query was executed.
This screenshot shows the child records ValorCampo and Workflow
This screenshot shows the entity after detaching it. Note the child records are not present in the detached object
As I have told, the problem only is when detaching. Child records are not kept. Database rows remain untouched.
I had the same problem, unfortunately navigation properties are lost after detaching an Item or when entity state is changed to detached.
what you can do is clone the entity
one way to do this is : Context.Entry(your_entity).CurrentValues.ToObject();
however this will not clone the navigation properties either
if you fully want to clone an object among with navigation properties
the easiest way for me to achieve it was using automapper library for c#
Below is a sample usage:
var config = new MapperConfiguration(cfg => cfg.CreateMap<originalObject, T>());
var mapper = new Mapper(config);
// or
var mapper = config.CreateMapper();
T clonedObject = mapper.Map<T>(originalObject);
after you clone and detach the original object, you can add
db.Record.AddRange(clonedObject );
and below is a generic extension to do it
public static object Map<T>(this T source)
{
var fullName = source.GetType().FullName;
var sourceType = source.GetType();
var baseType = ObjectContext.GetObjectType(source.GetType());
var config = new MapperConfiguration(cfg =>
cfg.CreateMap(sourceType, baseType));
var mapper = config.CreateMapper();
var entity = mapper.Map(source, sourceType, baseType);
return entity;
}
where you can call it like
var clonedObject = originalObject.Map();
Hope this helps!
I know the title wasn't the greatest, but here is what I actually want to accomplish.
I have a details view that represents Entity1 and its associations. I am capturing the property names and values in a key/value pair. I am currently using reflection to set the Entity properties to the corresponding values for non associations. I doubt this is the most efficient way, but I have been unable to find a better way using Expression Trees. So now I need to set the associations of Entity1 to their corresponding entities based on the primary key of those entity associations, call them Entity2-4.
When iterating the Properties of Entity1, I don't know how to construct a dynamic query to Entity2-4 and set Entity1.association to the corresponding entity. Here is the code I have so far:
foreach (string k in e.Values.Keys)
{
if (e.Values[k] != null && !String.IsNullOrEmpty(e.Values[k].ToString()))
{
System.Type objectType = Entity1.GetType();
PropertyInfo[] p = objectType.GetProperties();
foreach (PropertyInfo pi in p)
{
// set Entity1.Property for non associations (works just fine)
if (pi.Name == k)
{
System.Type t = pi.PropertyType;
pi.SetProperty(e.Values[k].ToString(), Entity1);
break;
}
// when i see pi.Name contain Reference, I know I'm working on an association
else if (pi.Name.Contains("Reference"))
{
// k is in the form of Entity.Property
var name = pi.Name.Left("Reference");
var keys = k.Split('.');
var ent = keys[0];
var prop = keys[1];
if (name == ent)
{
// here I need to obtain an instance from the db
// ie generate my dynamic query to the Entity with the name
// contained within the var "ent"
// I tried using reflection and could instantiate the entity
// but it did me no good as I needed the entity from the db
var entityInstance = some dynamic query;
// here I need to set the association of Entity1 to entityInstance from above
// normally I would use reflection, but I'm not sure that would work
// since EntityReference is the actual property returned by reflection
Entity1.SetAssocation(prop, Entity2);
break;
}
}
}
}
}
EDIT
I basically need construct the entity and its association entities so that I can submit them to the data context. Entity 2 through 4 exist in the db, I need to query the db to obtain the instances to that I may associate them to the new Entity1 I am creating and going to submit.
My basic model:
Entity1
Entity1.ID
Entity1.Prop1
Entity1.Prop2
Entity1.Prop3
Entity1.Entity2
Entity1.Entity3
Entity1.Entity4
Entity2
Entity2.ID
Entity2.Name
Entity3
Entity3.ID
Entity3.Name
Entity4
Entity4.ID
Entity4.Name
We've had an in-depth discussion about how to get the metadata out of the DbContext. Here are a few links to get you started. And I'll add some specific comments.
How I can read EF DbContext metadata programmatically?
A short summary (but you should check in there for more):
using (var db = new MyDbContext())
{
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);
var dependents = ((EntitySet)(set)).ForeignKeyDependents;
var principals = ((EntitySet)(set)).ForeignKeyPrincipals;
var navigationProperties = ((EntityType)(set.ElementType)).NavigationProperties;
// and e.g. for many-to-many (there is more for other types)
ManyToManyReferences = navigationProperties.Where(np =>
np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
.Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
.ToList();
}
#Goran Obradovic did a great job to wrap up what I started into a set of reusable queries (my due credits to him:).
I have worked out all other sorts of information that's in there. This is just for the DataSpace.CSpace (which is the most useful to you), but there is also DataSpace.SSpace etc. - which are more for creating SQL queries etc. I'll put most links at the bottom.
Specifics:
In your case following might be helpful:
(note: I'm not entirely sure what you're after, but I'm trying to guess here what's the direction that you're heading)
db.Set<Worker>().Find(1);
Is the generic method for accessing DbSet for a specific entity.
You could also construct it from a Type if you need it to be fully dynamic, e.g....
(I always wanted to do this:)
MethodInfo setMethod = typeof(DbContext).GetMethod("Set", new Type[]{});
MethodInfo genericSetMethod = setMethod.MakeGenericMethod(new Type[] { typeof(YourEntity) });
var set = genericSetMethod.Invoke(db, new object[] {});
Put your entity - or your Type instead of typeof(YourEntity).
You could then proceed and query that for e.g. Find(id) - for that entity - to get concrete values etc.
That's as dynamic as it gets - I'm not sure if that's what you want - but I'm just throwing things out here in case you need it.
That should get you started at least I hope.
links:
(all are posts of mine - some may be more or less relevant but might help)
How I can read EF DbContext metadata programmatically?
How check by unit test that properties mark as computed in ORM model?
Get Model schema to programmatically create database using a provider that doesn't support CreateDatabase
Programmatic data transformation in EF5 Code First migration
So, I wasn't able to perform this dynamically. But here is my working solution. Can anyone advise on how to perform this dynamically?
foreach (string k in e.Values.Keys)
{
if (e.Values[k] != null && !String.IsNullOrEmpty(e.Values[k].ToString()))
{
System.Type objectType = roster.GetType();
PropertyInfo[] p = objectType.GetProperties();
foreach (PropertyInfo pi in p)
{
if (pi.Name == k)
{
System.Type t = pi.PropertyType;
pi.SetProperty(e.Values[k].ToString(), roster);
break;
}
else if (pi.Name.Contains("Reference"))
{
var name = pi.Name.Left("Reference");
var keys = k.Split('.');
var entityName = keys[0];
var prop = keys[1];
if (name == entityName )
{
var val = e.Values[k].ToString();
switch (pi.Name)
{
case "Entity2Reference":
Entity1.Entity2Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, val);
break;
case "Entity3Reference":
Entity1.Entity3Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, val);
break;
case "Entity4Reference":
Entity1.Entity4Reference.EntityKey = new EntityKey("MyEntities." + entityName + "s", prop, Int64.Parse(val));
break;
}
}
}
}
}
}
Referring - How to get original values of an entity in Entity Framework? - I tried to extract the original value(s) of an entity in EF. But the ObjectStateManager.GetOBjectStateEntry giving the modified value of the entity. What am I missing?
I am using EF 4.0 (POCO Entities) in a multi-layered environment.
public bool Update(IMessage objMessage)
{
object ob = objMessage.GetMaster();
appSancAdvice _entity = ob as appSancAdvice;
using (var context = new BISEntities())
{
context.appSancAdvices.Attach(_entity);
ObjectStateEntry objectState = context.ObjectStateManager.GetObjectStateEntry(_entity);
objectState.ChangeState(System.Data.EntityState.Modified);
// this is giving the modified value of _entity
var originalValues = context.ObjectStateManager.GetObjectStateEntry(_entity).OriginalValues["sancstatus_id"];
int _i = context.SaveChanges();
return (_i > 0) ? true : false;
}
}
The context does not know the original value because you attach the entity. If you want the original values, you must fetch the object from the database. It is not that EF does that automatically when you get OriginalValues from a newly attached object.
Hey I have an application with a bunch of inproc caching and entity framework. When I want to write an update to an entity I reattach the cached copy. I track all things I've attached in the life cycle of the context so I don't try to attach them twice.
I have an error occurring on attach (very rarely in most cases this works fine and is really fast) which says the following:
A referential integrity constraint violation occurred: The property
values that define the referential constraints are not consistent
between principal and dependent objects in the relationship.
I've taken a really careful look at the entity which looks normal. I think this issue is due to the attachment/detachment of a foreign key when fixups runs.
Is there a good way to get any more info on this error or can it occur for reasons other than that the entity was in a state which EF wasnt expecting?
EDIT:
DB Diagram (note i'm using codefirst I just used the EDMX tool to make the diagram, I've also chopped a bunch of regular properties off the model for simplicity)
The error could occur for the one-to-many relationship between Person and Location you apparently have in your model in addition to the many-to-many relationship. For example the following code would throw the exception:
using (var context = new MyContext())
{
var person = new Person
{
CurrentLocationId = 1,
CurrentLocation = new Location { Id = 2 }
};
context.People.Attach(person); // Exception
}
"The property values that define the referential constraints" are the foreign key property value CurrentLocationId and the primary key value CurrentLocation.Id. If those values are different the exception is thrown. (Having CurrentLocation as null though is allowed.)
In my opinion this exception can only be thrown for foreign key associations because only for this type of association you have properties that define referential constraints at all in your model. It cannot be thrown for independent associations. Since every many-to-many relationship is an independent association (no foreign key property in the model) my guess is that the error is not related to your many-to-many relationship, but to the one-to-many.
I came across a very similar exception:
"A referential integrity constraint violation occurred:
The property value(s) of 'ObjectA.PropertyX' on one end of a relationship
do not match the property value(s) of 'ObjectB.PropertyY' on the other end."
The reason was this:
The client side of the web API sent a PUT request with the entire object including the navigation property (in this example ObjectA (more correctly ObjectB.ObjectA) was a navigation property and was fully supplied by the client). This occurs because the client receives the entire object from the server and bounces it as-is back to the server with minor changes.
On the other hand, the ObjectB.PropertyY had just been changed (this was the reason for the PUT request in the first place).
Since ObjectB.PropertyY was a reference to the same object ObjectA (a foreign key), EF tried to reconcile this and failed with the above exception.
The solution was simple:
ObjectB.ObjectA = null;
before the SaveChanges() solved this completely.
I hope this helps someone.
I have just been experiencing the same issue and the resolution to mine was that I had added mappings to the association and then setup the referential contstraints.
Inorder to resolve the issue I had to open the mappings window for the association and there was a link to delete the mappings. Once done the Mapping Details window then said Mappings are not allowed.. It appears that adding the referential constraint leaves any mappings in place.
Thought it may be worth posting in case anyone else is looking for solutions to this error message in the future.
#LukeMcGregor hi,
I think I can offer a different perspective as someone who has the same problem.
After I have performed all the necessary checks, I can say that I prefer to get this error.
Because in my scenario: I wanted to include an object that caused a mismatch error. It's the location object in your scenario. If I add an object with an ID, I get this error because the ID in the previous object (the one that is not updated) does not match the updated ID.
But it's not a big problem. As a solution; If it is still on the UI side, the object may still be included if it still exists.
You will either empty the object when you receive the update request from the user. (= Null) Or you will update the object with the ID updated by the user before the service-side update (attach, modified ... whatever) and update it in this way.
That's it. It can remain as it is in the database and diagrams.
To add to #Slauma's answer, it isn't just when adding objects to your context. For your example, if you edit the CurrentLocationId in Person, you also need to edit the CurrentLocation object embedded in the Person object. EF will automatically populate the CurrentLocation object because CurrentLocationId has a foreign key in the CurrentLocation's table. When you edit the CurrentLocationId without updating the CurrentLocation object as well, they become out of sync. This is what causes the exception in this case.
So let's say you needed to update the Person object's CurrentLocationId. We'll assume you pre-fetched the Person data and the Location data.
public class DbData
{
List<Person> PersonList;
List<Location> LocationList;
public DbData()
{
using (var context = new MyContext())
{
PersonList = context.Persons.ToList();
LocationList = context.Locations.ToList();
}
}
public void UpdatePersonLocation(Person person, int newLocationId)
{
using (var context = new MyContext())
{
var location = LocationList.Where(l=>l.id==newLocationId).Single();
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == newLocationId;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
//or if you're giving it the location object...
public void UpdatePersonLocation(Person person, Location location)
{
using (var context = new MyContext())
{
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == location.id;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
}
I just had this problem and came up with a pretty quick solution. My issue was with a many-many table.
Public class Pictures_Tag
{
[Key]
[Column(Order = 0)]
[ForeignKey("Picture")]
public Int16 Picture_ID { get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("ImageTag")]
public Int16 ImageTag_ID { get; set; }
public virtual Picture Picture { get; set; }
public virtual ImageTag ImageTag { get; set; }
}
I added the line where I assigned Picture = db.Pictures... and then it worked fine (not exactly sure why)
[HttpPost]
public ActionResult Edit2(WebSiteEF2017C.Models.Pictures_Tag p)
{
using (var db = new thisModel(Session["org"].ToString())
{
p.Picture = db.Pictures.Where(z => z.ID == p.Picture_ID).FirstOrDefault();
db.Pictures_Tags.Attach(p);
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return View(db.Pictures_Tags.Include(x => x.Picture)
.Where(n => n.Picture_ID == p.Picture_ID & n.ImageTag_ID == p.ImageTag_ID).FirstOrDefault());
}
}
try to set any sub class object to null
MainObject.SubObject1 = null;
Person.Address=null; // Address Object to be null not id
In Entity Framework Code First CTP5 is it possible to add a child entity to a parent entity collection using only the primary key of the child? My goal is to avoid having to first load the child entity from the data store.
For bonus points, can this be accomplished using only the parent and child primary keys (i.e. without loading any entities at all)?
Compiled in my head against CTP4 so be aware.
public void AddPersonToList(int id, int toAdd)
{
var mailList = new MailList { ID = id, ContactInformations = new List<ContactInformation>() };
this.db.MailLists.Attach(mailList);
var ci = new ContactInformation { ID = toAdd };
this.db.ContactInformations.Attach(ci);
this.db.ObjectStateManager.ChangeRelationshipState(mailList, ci, ml => ml.ContactInformations, System.Data.EntityState.Added);
}
You need to call a SaveChanges before anything is persisted.
Attaching and entity with only a ID and working with the Statemanager works really well in EF and allows you to create some really nice solutions performance wise.