Code First EF save extended entity >> The entity type xxx is not part of the model for the current context - c#

I am new to EF.
I extended an entity with new properties only needed in the controller.
When I save the entity, I don't need the properties anymore and upcasted to the base entity and tried to save, but every time I got the error:
The entity type XXX is not part of the model for the current context.
The attribute [NotMapped] did not work either (on class and / or property).
How can I simply upcast and save the entity?
If I create a new instance of the base entity, everything works just fine.

#Devil, this only covers "downcasting" - I need "upcating"!
I wrote a converter. I could not believe, that I have to do thisĀ“, but now it is working just fine! Thx, for your help!
public static T ConvertToBase<T>(Object extended) {
if(extended == null) {
throw new ArgumentException("Parameter extended was passed null!");
}
if(extended.GetType().BaseType != typeof(T)) {
throw new ArgumentException($"Parameter extended does not inherit base type '{typeof(T).FullName}'");
}
PropertyInfo[] baseProperties = extended.GetType().BaseType.GetProperties();
Object baseInstance = Activator.CreateInstance(extended.GetType().BaseType);
foreach(PropertyInfo basePropertyInfo in baseProperties) {
basePropertyInfo.SetValue(baseInstance, basePropertyInfo.GetValue(extended));
}
return (T)System.Convert.ChangeType(baseInstance, typeof(T));
}

Related

how to stop dbentityentry.currentvalues.setvalues trying to change entitykey value

I am using the following code to update an entity object with the new information gathered from the my code. I am using Entity Framework 5.
I use the following extension method (as a alternative to the reattach code I used to use in EF4):
public static void ApplyValues(this object currentObject, object sourceObject, System.Data.Entity.DbContext obj)
{
obj.Entry(currentObject).CurrentValues.SetValues(sourceObject);
}
The problem is that when this method is called the SetValues method tries to modify the EntityKey value on the attached object (obviously I do not want it to do this) and so it throws an error.
I suppose there are two questions here:
Is there a way to prevent it from trying to update the key value?
if not how do I replicate the ObjectContext.ApplyCurrentValues() code that used to work fine in EF4?
----UPDATE----
My previous code I used for EF4 is as follows:
public static System.Data.Objects.DataClasses.EntityObject ReAttach(this System.Data.Objects.ObjectContext obj, System.Data.Objects.DataClasses.EntityObject objectDetached)
{
if (objectDetached.EntityKey != null)
{
object original = null;
if (obj.TryGetObjectByKey(objectDetached.EntityKey, out original))
{
objectDetached = obj.ApplyCurrentValues(objectDetached.EntityKey.EntitySetName, objectDetached);
return objectDetached;
}
else
{
throw new ObjectNotFoundException();
}
}
else
{
return objectDetached;
}
}
In my opinion this exception indicates that something is wrong - or at least unusual - in your calling code.
currentObject is an attached entity while sourceObject is (normally) a detached object (not necessarily an entity) that should have the same key values (or no key properties at all).
Indeed setting the current values works differently with DbContext because you have to supply the current attached entity explicitly in order to update its current values. Using ApplyCurrentValues of ObjectContext you don't supply this entity:
objectContext.ApplyCurrentValues("MyEntitySet", sourceObject);
This is different because...
sourceObject must be an entity and cannot be an arbitrary object
it updates the values of the attached entity that has the same key values as sourceObject
In your example it would update another entity than currentObject because apparently currentObject is not the entity that has the same key as sourceObject.
If you have used ObjectStateEntry.ApplyCurrentChanges (which is closer to the new version in DbContext) you would get the same exception:
var objectContext = ((IObjectContextAdapter)obj).ObjectContext;
var entry = objectContext.ObjectStateManager.GetObjectStateEntry(currentObject);
entry.ApplyCurrentValues(sourceObject);
EF will complain here that you try to change the key values. And it will complain if sourceObject is not of the same type as currentObject while DbContext would allow that (which makes the procedure with DbContext more useful in my opinion because you can use arbitrary objects with matching property names - for example DTOs - to update the entity).
Edit
The main problem to reproduce the method you have used with EF 4 is that entities with EF 5/DbContext don't derive from EntityObject but are POCOs. Because of this you don't have an EntityKey available that would allow a generic implementation of this method.
What you could do is to introduce an interface that marks the key property of your entities, like so for example:
public interface IEntity
{
int Id { get; set; }
}
Your entity classes would implement this interface, for instance an Order entity:
public class Order : IEntity
{
public int Id { get; set; }
public DateTime ShippingDate { get; set; }
// etc.
}
You could create a generic method with a constraint for this interface:
public static T ReAttach<T>(DbContext context, T objectDetached)
where T : class, IEntity
{
T original = context.Set<T>().Find(objectDetached.Id);
if (original == null)
throw new ObjectNotFoundException();
context.Entry(original).CurrentValues.SetValues(objectDetached);
return objectDetached;
}
If your entities do not always have an int property Id but their keys have different types, names or could be composite it is probably the easier way to pass in the entity's key into the method instead of using an interface:
public static T ReAttach<T>(DbContext context, T objectDetached,
params object[] keyValues) where T : class
{
T original = context.Set<T>().Find(keyValues);
if (original == null)
throw new ObjectNotFoundException();
context.Entry(original).CurrentValues.SetValues(objectDetached);
return objectDetached;
}

Read the value of a referenced entity's PropertyInfo using EF4

I would like to dynamically read the values of the PropertyInfos of EntityObjects that I come across when looping through the PropertyInfos of a parent entity (the column values of the instance of ImageType that is connected to the current instance of Image, f.i.).
The main entity's type is only known at runtime, so I'm looking for a generic way of reading the PropertyInfo values of any referenced entity object.
I can loop through the PropertyInfos of the sub entity, but when I try to get a value I get a TargetException: Object does not match target type.
// loop through the main entity's properties
foreach (PropertyInfo pi in entityType.GetProperties())
{
// if the main entity's property is an entity
if (pi.PropertyType.BaseType == typeof(System.Data.Objects.DataClasses.EntityObject))
{
// loop through the sub entity's properties
foreach(PropertyInfo mychildren in pi.PropertyType.GetProperties())
{
// the loop works fine but when i try to get a value I get a
// TargetException: Object does not match target type.
object test = mychildren.GetValue(pi, null);
}
}
}
How can I do this?
Edit:
Entity Framework 4.0 doesn't seem to allow you to dynamically retrieve the instances of an entity's related entities. But with EF 4.1 and up you can, by using their class name as a string identifier. So I upgraded to EF 4.2 and got it working.
The reason I wanted this code is to use it in my DTO translation routine. My DTO's can have string properties that correspond to the name properties of related entities and this way I can access those without having to hard code the related entities' types.
In EF 4.1 and up, the ObjectContext is wrapped by a class named DbContext, which provides navigational properties with which to get instances of related entities using strings. To dynamically retrieve a singular related entity, you can use:
dynamic refObject = Activator.CreateInstance(refObjectType);
refObject = context.Entry(currentObject).Reference(refObjectType.Name).CurrentValue;
For those upgrading from 4.0: The recommended way to work with DbContext is not with EntityObjects but with POCOs. These can be made manually or they can be generated via the edmx context menu.
My current implementation is as follows:
// Loop through the propertyinfos of the dto's type
foreach (PropertyInfo pf in dtoType.GetProperties().Where(p => p.CanWrite))
{
// Use the name of the dto property to get the corresponding property from the POCO's type. If it doesn't exist, pi will be null
PropertyInfo pi = pocoType.GetProperty(pf.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (pi != null)
{
// Check if the current propertyinfo of the POCO has a subproperty named Id
// If this is the case we treat the propertyinfo as a referenced POCO
if (pi.PropertyType.GetProperty("Id") != null)
{
// skip referenced POCOs if their data is not needed
if (!includeRelated) continue;
// Get the type of the referenced POCO
Type refObjectType = pi.PropertyType;
// Create an instance of the referenced POCO
dynamic refObject = Activator.CreateInstance(refObjectType);
// Create a type of GenericRepository<objectType>
Type refObjectRepositoryType = typeof(GenericRepository<>).MakeGenericType(refObjectType);
// Create an instance of GenericRepository<objectType>
dynamic refObjectRepository = Activator.CreateInstance(refObjectRepositoryType);
// Fill the dynamic POCO instance with the values of the referenced POCO instance
refObject = refObjectRepository._context.Entry(poco).Reference(refObjectType.Name).CurrentValue;
try
{
// Set the dto property with the name value of the referenced POCO instance
// (i.e. dtoImage.ImageType = pocImage.ImageType.Name)
pf.SetValue(dto, refObject.Name, null);
}
catch (RuntimeBinderException)
{
// this happens when the related entity is null, ie. in one to zero-or-one relationships
continue;
}
continue;
}
// If the propertyinfo's propertytype does not have an Id property, just set the value of
// the dto property to that of the POCO's propertyinfo directly
pf.SetValue(dto, pi.GetValue(poco, null), null);
}
}
For now this code will only work for referenced entities that have both an Id and a Name property. Also, there is likely to be a performance penalty for this approach so I have implemented the flag includeRelated to toggle whether to query the related objects or not.
You're trying to get value from parent's PropertyInfo, but GetValue expected object of pi.PropertyType type. You should use something like this:
using (var context = new MyContext())
{
var cust = context.Customer.First();
var custType = cust.CustomerType;
var pi = typeof (Customer).GetProperty("CustomerType");
var child = pi.PropertyType.GetProperty("CustomerTypeID");
var res = child.GetValue(custType, null);
// this returns value of Customer.CustomerTypeID
}

Is there a way to call generic repository methods on an entity that was generated at run time with codeDOM?

I am using codeDOM to generate my entity classes at run time. I also have a generic repository to deal with various DB functionality. Here is the Insert method as an example method in my generic repository:
public void Insert<TEntity>(TEntity entity) where TEntity : class, IBusinessEntity
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
TEntity existing = Existing(entity);
if (existing == null)
{
_context.Set<TEntity>().Add(entity);
this._context.SaveChanges();
}
}
Here is some example code of how I generate an entity class and how to create an entity based on that entity class using codeDOM:
//Generate the fields of the new entity class
EntityGenerator.EntityFieldInfo entityField1 = new EntityGenerator.EntityFieldInfo("Name", typeof(string), RelationshipType.NoRelation);
EntityGenerator.EntityFieldInfo entityField2 = new EntityGenerator.EntityFieldInfo("Shape", typeof(string), RelationshipType.NoRelation);
ICollection<EntityGenerator.EntityFieldInfo> entityFieldList = new List<EntityGenerator.EntityFieldInfo> { entityField1, entityField2 };
// Create the new entity class using the fields established above
// as well as the name of the entity (typeName = "Thing")
string typeName = "Thing";
EntityGenerator.CreateEntityClass(entityFieldList, typeName);
CompilerResults results = EntityGenerator.GetCompiledEntity(typeName);
// Create an entity instance based on the new entity class that was just created
Object newThing = EntityGenerator.CreateInstanceOfEntity(results, typeName);
SetObjectField(newEntity, "Name", "Box");
SetObjectField(newEntity, "Shape", "Cuboid");
As you can see, newThing (the new entity instance) is an Object type. If this was a hardcoded entity class then I could just say
Thing newThing;
But the Thing entity created by CodeDOM isn't a hardcoded class so I have to use type Object instead of type Thing. This is a problem because I am using a generic repository. Let's say I want to insert this entity into the database. I would like to call:
myRepository.Insert<Thing>(newThing);
However, Thing was just created by CodeDOM at run time, so it isn't a class, which means it can't go inside the <>. You may have noticed above in my Insert method, TEntity is also an IBusinessEntity. If I try
myRepository.Insert<IBusinessEntity>(newThing);
I get the error:
Argument type 'object' is not assignable to parameter type 'Models.IBusinessEntity'
If I try without anything inside the <>, like this:
myRepository.Insert(newThing);
I get the error:
The type 'object' must be convertible to 'Models.IBusinessEntity' in order to use it as a parameter 'TEntity' in the generic method 'void Insert(TEntity)'.
Does anyone know how I can reconcile this codeDOM generated entity with a generic repository? Could reflection help? It would be nice if reflection could somehow give me a class of Thing which could be passed into the <>. Also I should note that all entities I create with CodeDOM extend IBusinessEntity.
I think it will be hard to make it work because the DbSets contained in the DbContext are used by EF to create mappings. How do you think to create them?
Anyway, you don't need the type to work with EF, you can often use GetType.
In your methods (Existing(.) is missing but I think is similar) you can use
public void Insert(object entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
if (!(entity is IBusinessEntity))
throw new ArgumentInvalidException("entity is not an IBusinessEntity");
object existing = Existing(entity);
if (existing == null)
{
_context.Set(entity.GetType()).Add(entity);
this._context.SaveChanges();
}
}
Using Set<> or Set(.) I'm quite sure that EF will search in mappings created starting from DbSets contained in DbContext. I can't remember the exact exception but I sow it different times (when I used DbContext.Set(myEntityType)).

Failure to attach a detached entity (entity with the same key is already in the context)

I'm using Entity Framework 6, Code First approach. I'll try to present my problem with a simple piece of code:
public void ViewEntity(MyEntity Entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
Db.MyEntities.Attach(Entity); // Exception
}
}
The exception message is: Attaching an entity of type 'MyProgram.MyEntity' failed because another entity of the same type already has the same primary key value.
From what I've read on MSDN it's an expected behaviour. But what I want on that last line is to first check if there is an entity with the same key already attached to a context; if it is, use it instead, and only otherwise attach my entity to context.
But I've failed to find a way to do so. There are many utility methods on ObjectContext instance (for example GetObjectByKey). I can't test them all 'cause they all ultimately need a qualifiedEntitySetName, and I don't have any in my real imlpementation, because this method should be on an abstract class and it should work for all entity types. Calling Db.Entity(this) is no use, there is no EntityKey which would have EntitySetName.
So all of this became complex really fast. And in my terms I just want to check if the object is already in "cache" (context), use it, otherwise use my object and attach it to this context.
To be clear, I have a detached object from a TreeNode.Tag in the first place, and I just want to use it again, or if it's impossible; if there already is one in the context), use that one instead. Maybe I'm missing some crucial concepts of EF6, I'm just starting out with EF.
I've found a solution for me. As I guessed correctly ObjectContext.GetObjectByKey method does what I need, but first I needed to construct qualifiedEntitySetName, and I found a way to do so. A tad bit cumbersome (using reflection, iterating properties of MyDbContext), but does not compare to a headache of a problem I made out of all this. Just in case, here's the patch of code that is a solution for me:
public SdsAbstractObject GetAttachedToContext()
{
var ObjContext = (SdsDbContext.Current as IObjectContextAdapter).ObjectContext;
var ExistingItem = ObjContext.GetObjectByKey(GetEntityKey()) as SdsAbstractObject;
if (ExistingItem != null)
return ExistingItem;
else
{
DbSet.Attach(this);
return this;
}
}
public EntityKey GetEntityKey()
{
string DbSetName = "";
foreach (var Prop in typeof(SdsDbContext).GetProperties())
{
if (Prop.PropertyType.IsGenericType
&& Prop.PropertyType.GenericTypeArguments[0] == ObjectContext.GetObjectType(GetType()))
DbSetName = Prop.Name;
}
if (String.IsNullOrWhiteSpace(DbSetName))
return null;
else
return new EntityKey("SdsDbContext." + DbSetName, "Id", Id);
}
An Entity can be in one of five stages : Added, Unchanged, Modified, Deleted, Detached.
public void ViewEntity(MyEntity entity) // Want to read properties of my entity
{
using (var Db = new MyDbContext())
{
var DummyList = Db.MyEntities.ToList(); // Iteration on this DbSet
// Set the Modified state of entity or you can write defensive code
// to check it before set the state.
if (Db.Entry(entity).State == EntityState.Modified) {
Db.Entry(entity).State = EntityState.Modified
}
// Attached it
Db.MyEntities.Attach(Entity);
Db.SaveChanges();
}
}
Since EF doesn't know which properties are different from those in the database, it will update them all.

How to save an object graph with EntityFramework, lazy load disabled, proxy disabled and entities are non trackable

Here is the situation I am into using EF.
Repository gets an instance of A and returns to the presentation tier (i.e. MVC controllers)
The controllers change certain properties on A's instance and the given it back to persist.
Before persisting, I need to figure out what change was done to the object and validate if the change is permitted or not.
To compare the change, I need the old instance from the database.
But EF returns the same dirtied instance, so I cannot compare them.
What I tried doing:
Class Structure
public Class A {
public B B {get;set;}
}
public class B {
public ICollection<A> As {get;set;}
public C c { get; set;}
}
public class C {
}
B and C map to the same database table.
Though it's because EF is tracking the old instance and since save has yet been called on the instance, so it's returning back the same inatce.
So I turned off lazy loading, proxy gen, and return the objects as non-trackable for the repository.
Now EF returns fresh record from the DB, but If changed some property on A, then in the B's collection of A, it loads only the instance of A that I changed, not the entire collection.
When If I want to create a new A and save, I do the following
B b = GetSomeOldB();
A a = new A();
a.B = b
a.Save();
So I essentially I add the new A to the context and call SaveChanges.
Ef returns and exception that "Unable to cast C to B".
Basically, all I want is to get the old object graph from the context when I ask for, compare with the dirtied one the giving the dirtied to persist.
Would really appreciate any help!!
Here is the solution that finally implemented:
1. Turned back on the Tracking and proxy creation
2. Get the orginal copy from EF.
3. Wrote this generic method to hydrate a new instance of the entity
public ICollection<T> GetOriginalCollection<T>(ICollection<T> changedCollection) where T : class {
ICollection<T> original = new Collection<T>();
foreach (var item in changedCollection) {
//Dont return the newly added ones to the original collection
if (_context.Entry(item).State != EntityState.Added){
original.Add(GetOriginal(item));
}
}
return original;
}
public T GetOriginal<T>(T changedEntity) where T : class {
Func<DbPropertyValues, Type, object> getOriginal = null;
getOriginal = (originalValues, type) =>
{
object original = Activator.CreateInstance(type, true);
foreach (var ptyName in originalValues.PropertyNames) {
var property = type.GetProperty(ptyName);
object value = originalValues[ptyName];
//nested complex object
if (value is DbPropertyValues) {
property.SetValue(original, getOriginal(value as DbPropertyValues, property.PropertyType));
} else{
property.SetValue(original, value);
}
}
return original;
};
return (T)getOriginal(_context.Entry(changedEntity).OriginalValues, typeof(T));
}
I think what you want is working in disconnected environment. You can attach disconnected entities and provide what changed in the graph by specifying its EntityState. If you want you can create Interface like IObjectState and implement in your models to find out what exactly changed in the graph.

Resources