Lets say i have a lot of entity classes.
I have a db context.
I have a code
Person p=new Person();
p.Name="test";
Note, that there is no lines with context.
How must I save this object using context, if context only knows that it is EntityObject?
UPDATE:
if (obj.EntityState == System.Data.EntityState.Detached)
context.AddObject(obj.EntityKey.EntitySetName, obj);
but obj.EntityKey is null, so it does not work
UPDATE2:
I have a code:
public static void EntitySave(EntityObject obj)
{
if (obj == null)
throw new ArgumentNullException(obj.GetType().Name, obj.GetType().Name + " не должен быть пустым");
var context = GetSqlConnection();
if (obj.EntityState == System.Data.EntityState.Detached)
context.AddObject(obj.EntityKey.EntitySetName, obj);//there is an exception
context.SaveChanges();
}
and one another:
public static void SaveNewPerson()
{
Person p = new Person();
EntitySave(p);//there is an exception
}
So how EntitySave should save the object correctly? Or may be i need a helper functions to create each entities classes?
You cannot do it by passing EntityObject alone, because a newly created EntityObject has no info on the entity set. You can create a helper function or a helper dictionary, something like
private static Dictionary<Type, string> _entitySets = new Dictionary<Type, string>
{
{ typeof(Person), "Persons"},
{ typeof(Address), "Addresses"}
}
...
public static void EntitySave(EntityObject obj)
{
if (obj == null)
throw new ArgumentNullException("не должен быть пустым");
var context = GetSqlConnection();
if (obj.EntityState == System.Data.EntityState.Detached)
context.AddObject(EntitySets[obj.GetType()], obj);
context.SaveChanges();
}
Just check if the set name is "Persons" or "PersonsSet"
It is still not clear what you mean by "context doesn't know the type of object". The context must know the type of object otherwise it doesn't know how to map and persist the object. The type of the object is described in EDMX.
If you just want to add object to the context you must use either:
// You must say the name of EntitySet where you want to add the object
context.AddObject("PersonsSet", person);
or:
// You must call the AddObject method on correct object set
context.CreateObjectSet<Person>().AddObject(person);
Default code generation also offers specific method like AddPerson or something like that.
Related
I observed that we can write custom linq queries if we use
dbContext.set<MyEntity>()
But can not on
dbContext.set(SomeType).
I have a context class EGEntities and I have an Entity say "Employee".
How can I assign Employee type Entity to MyEntity? So that I can create a queryable instance of Employee?
Another Failure Idea
var instance = Activator.CreateInstance(thisType);
GetEntityTemplate(instance, thisType);
// Just to Test
public static List<Object> GetEntityTemplate<T>(T instance,Type targetType) where T :class
{
var Context = new EGEntities();
var set = Context.Set<T>();
if (set == null)
{
throw new Exception();
}
List<object> l = null;
return l;
}
But don't know why 'set' is only looking for 'object' and exception is "Model is not current context" though instance is correctly carrying the class instance in the parameter.
The DbContext.Set() method is having another (non-generic) overload that is expecting a System.Type:
var employeesSet = dbContext.Set(typeof(Employee));
And if you're already having a reference to an existing instance of Employee:
var employeeType = myEmployee.GetType();
var employeesSet = dbContext.Set(employeeType);
See MSDN
In my MVC application, I have been using Repository pattern for DAL.
Now, when I do select one entity record and and update the entity field value and do Update operation then getting below error.
Attaching an entity of type 'DAL.User' failed because another entity
of the same type already has the same primary key value. This can
happen when using the 'Attach' method or setting the state of an
entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate."} System.Exception
Below is repository stuff:
public void Update(TEntity entity)
{
if (_context.Entry(entity).State != EntityState.Modified)
{
_dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
Calling as follow:
In Bussines layer library:
Manager class :
private readonly IUnitOfWork _unitOfWork;
private IRepository <User , int> UserRepository
{
get
{
return _unitOfWork.GetRepository<AccountUser, int>();
}
}
public void UpdateUserEntity(UserDTO u)
{
try
{
User model = new User ();
UserRepository.Update(Mapper.Map(u, model));
_unitOfWork.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
Please guide me how I could resolve above error.
The exception says that there is another entity with the same key that has been attached, but different reference.
The exception could be caused by previous attached entity.
db.Set<Entity>().Attach(new Entity { Id = 123 });
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
Or could be also caused by tracked entity that automatically attached.
db.Set<Entity>().FirstOrDefault(e => e.Id == 123); // automatically attached
db.Set<Entity>().Attach(new Entity { Id = 123 }); // different reference but same key
The second cause can be solved by mentioning AsNoTracking when retrieving item.
db.Set<Entity>().AsNoTracking().FirstOrDefault(e => e.Id == 123);
Or to be safe you can use this extension to always detach any attached entity.
public static class DbSetExtension
{
public static void SafeAttach<T>(
this DbContext context,
T entity,
Func<T, object> keyFn) where T : class
{
var existing = context.Set<T>().Local
.FirstOrDefault(x => Equals(keyFn(x), keyFn(entity)));
if (existing != null)
context.Entry(existing).State = EntityState.Detached;
context.Set<T>().Attach(entity);
}
}
Usage.
db.SafeAttach(entity, e => e.Id);
It's because of the reason,
"TEntity entity as a new object instead of the one which already exists".
Means,Entity framework treats each new object as new entry.(eventhough with same existing old data,PK & all).
Solution is,
First retrieve the object from database
Do/assign the changes to the same object (preferably without changing Primary key)
Then do state as Modified ,Update,SaveChange()
I created my own ContextProvider, sub classed from EFContextProvider. In BeforeSaveEntity I am running some business logic to validate the transaction. I need the updates to be "all or nothing", so if the 3rd entity in the collection fails the validation, the entire batch should be discarded, even though Ive already returned "true" for the first 2 entities.
I have a class level property thats getting set when any entity fails. In the final check in BeforeSaveEntities I can get the value of the flag.
I think this is where I can abort the update, but not sure how. Do I clear the map? Or throw an error?
Also, I will need to re-query the DB for my validation routines. I've read some posts that talk about creating a 2nd instance of the context to do the querying for the current values. Is there some docs on doing this, or gotchas I need to be aware of?
thanks
In your BeforeSaveEntities call you can throw an EntityErrorsException: Here is an example where we throw an exception if there is attempt to save any "Order" objects within a save bundle:
[HttpPost]
public SaveResult SaveWithEntityErrorsException(JObject saveBundle) {
ContextProvider.BeforeSaveEntitiesDelegate = ThrowEntityErrorsException;
return ContextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> ThrowEntityErrorsException(Dictionary<Type, List<EntityInfo>> saveMap) {
List<EntityInfo> orderInfos;
if (saveMap.TryGetValue(typeof(Order), out orderInfos)) {
var errors = orderInfos.Select(oi => {
return new EntityError() {
EntityTypeName = typeof(Order).FullName,
ErrorMessage = "Cannot save orders with this save method",
ErrorName = "WrongMethod",
KeyValues = new object[] { ((Order) oi.Entity).OrderID },
PropertyName = "OrderID"
};
return new EFEntityError(oi, "WrongMethod", "Cannot save orders with this save method", "OrderID");
});
var ex = new EntityErrorsException("test of custom exception message", errors);
// if you want to see a different error status code use this.
// ex.StatusCode = HttpStatusCode.Conflict; // Conflict = 409 ; default is Forbidden (403).
throw ex;
}
return saveMap;
}
And you should use BeforeSaveEntities exclusively instead of BeforeSaveEntity as your save logic becomes more complicated.
I had a requirement to perform server side calculations on entities that had been changed on the client - without saving - and get the results back to the client. The solution based on Breeze named saves that I came up with could be useful in this situation too.
I added the following method to the base class for my Breeze controllers.
protected SaveResult OverrideSaveChanges(JObject saveBundle, Action<List<object>> action, bool shouldSave = false)
{
var saveChangesDelegate = new SaveChangesOverride(action, shouldSave);
return saveChangesDelegate.Execute(saveBundle, ContextProvider);
This allows concrete controllers to implement named saves very simply. The saveBundle plus an Action<List<object>> are passed into the OverrideSaveChanges method. The action can make whatever modifications to the entities that are required and those changes will be propagated back to the client. The objects in the list are the entities that the client recognized as having changes and sent down to the server for the named save. Optionally, you could pass a shouldSave argument with a value of true to have the entities saved - the default is false.
OverrideChanges delegates to SaveChangesOverride for most of the heavy lifting.
public class SaveChangesOverride
{
public SaveChangesOverride(Action<List<object>> action, bool shouldSave = false)
{
Action = action;
ShouldSave = shouldSave;
}
private readonly Action<List<object>> Action;
private readonly bool ShouldSave;
public List<object> Entities;
public SaveResult Execute(JObject saveBundle, ContextProvider contextProvider)
{
contextProvider.BeforeSaveEntitiesDelegate = OnBeforeSaveEntities;
contextProvider.SaveChanges(saveBundle);
return new SaveResult
{
Entities = Entities,
KeyMappings = new List<KeyMapping>()
};
}
private Dictionary<Type, List<EntityInfo>> OnBeforeSaveEntities(Dictionary<Type, List<EntityInfo>> arg)
{
Entities = arg.SelectMany(x => x.Value).Select(x => x.Entity).ToList();
Action(Entities);
if (!ShouldSave)
{
return new Dictionary<Type, List<EntityInfo>>();
}
return arg;
}
}
Although we have access to all of the changed entities in the saveBundle actually performing the modifications in OnBeforeSaveChanges allows us to work with entities rather than a JObject.
Also, contextProvider.SaveChanges must be called regardless of whether we wish to have the entities saved. This is what triggers OnBeforeSaveEntities to be called. To ensure that the entities are not saved despite calling SaveChanges (if that is what is desired), rather than returning arg from OnBeforeSaveEntities, an empty dictionary is returned.
To ensure that the changes make it back to the client, a reference to the entities is saved in OnBeforeSaveEntities. This is used in Execute to prepare a SaveResult that is populated with the modified entities.
I'm trying to call ObjectContext.ExecuteFunction from my objectcontext object in the repository of my site.
The repository is generic, so all I have is an ObjectContext object, rather than one that actually represents my specific one from the Entity Framework.
Here's an example of code that was generated that uses the ExecuteFunction method:
[global::System.CodeDom.Compiler.GeneratedCode("System.Data.Entity.Design.EntityClassGenerator", "4.0.0.0")]
public global::System.Data.Objects.ObjectResult<ArtistSearchVariation> FindSearchVariation(string source)
{
global::System.Data.Objects.ObjectParameter sourceParameter;
if ((source != null))
{
sourceParameter = new global::System.Data.Objects.ObjectParameter("Source", source);
}
else
{
sourceParameter = new global::System.Data.Objects.ObjectParameter("Source", typeof(string));
}
return base.ExecuteFunction<ArtistSearchVariation>("FindSearchVariation", sourceParameter);
}
But what I would like to do is something like this...
public class Repository<E, C> : IRepository<E, C>, IDisposable
where E : EntityObject
where C : ObjectContext
{
private readonly C _ctx;
// ...
public ObjectResult<E> ExecuteFunction(string functionName, params[])
{
// Create object parameters
return _ctx.ExecuteFunction<E>(functionName, /* parameters */)
}
}
Anyone know why I have to call ExecuteFunction from base instead of _ctx?
Also, is there any way to do something like I've written out? I would really like to keep my repository generic, but with having to execute stored procedures it's looking more and more difficult...
Update: Here's what I've tried and the method does not show up in intellisense/it gives me an error when I try to compile with it
public ArtistSearchVariation findSearchVariation(string source)
{
System.Data.Objects.ObjextContext _ctx = new ObjectContext(/* connection string */);
System.Data.Objects.ObjectParameter sourceParam = new ObjectParameter("Source", source);
return _ctx.ExecuteFunction<ArtistSearchVariation>("FindSearchVariation", sourceParam);
}
Thanks,
Matt
You don't have to use base.ExecuteFunction, the ExecuteFunction method (and overloads) are public, not protected, so you can call them from external sites. Are you having trouble calling it?
I have an entityframework entity called "ABC" (attributes ID and Title).
On update record view, I have added the ID as hidden field and title is the text box.
Controller looks like something:
public ActionResult UpdateAction( ABC obj )
I get everything fine and fair in obj - i.e., the title, and the ID.
Now to update the record in database, I read the original entity:
var original = (from x in base.context.ABC where x.id == obj.id ).Single();
Now to reflect the changes in original, I think should do the update model:
this.TryUpdateModel( original );
I get an error :| ... stating that column ID cannot be changed.
The property 'id' is part of the object's key information and cannot be modified.
I do not want to manually assign the properties back to original object.
The other alternative can be:
TryUpdateModel(original, new string[] { "Title" }, form.ToValueProvider());
But I hate strings - also, my object has like 20 attributes :|
Can someone please suggest a better pattern of doing so?
Rgds
public class ControllerExt : Controller
{
protected void UpdateModel<TModel>(TModel model, params Expression<Func<TModel, object>>[] property) where TModel : class
{
var props = new List<string>(property.Length);
foreach (var p in property)
{
var memberExpression = RemoveUnary(p.Body) as MemberExpression;
if (memberExpression == null)
{
throw new NullReferenceException("Can not retrieve info about member of {0}".FormatThis(typeof(TModel).Name));
}
props.Add(memberExpression.Member.Name);
}
this.UpdateModel(model, props.ToArray());
}
private static Expression RemoveUnary(Expression body)
{
var unary = body as UnaryExpression;
if (unary != null)
{
return unary.Operand;
}
return body;
}
}
Example:
UpdateModel<MyModel>(model, x => x.PropertyFromMyModel_1, x => x.PropertyFromMyModel_2);