I have an entity that has one child and I need to update it, but, in the TryUpdateModel method, it does not accept an strong typed object (accepts only a FormCollection), and when I try to update it, I get the following error.
{"A relationship is being added or deleted from an AssociationSet 'FK__SG_Usuari__ID_Si__534D60F1'. With cardinality constraints, a corresponding 'SG_Usuario' must also be added or deleted."}
The problem is that I can't load in the formcollection the child property, only an id, but not the entire object.
You can directly click update from model in entity framework and it will automatically update the enitty with all the relationship
the "create" statement is like this:
public ActionResult Edit(FormCollection form)
{
Usuario usuario = new Usuario
{
NomeUsuario = form["Usuario.NomeUsuario"],
IdeUsuario = form["Usuario.IdeUsuario"],
NumRegistroRM = form["Usuario.NumRegistroRM"],
SenUsuario = form["Usuario.SenUsuario"],
SituacaoUsuario = this.SituacaoUsuarioService.GetSituacaoUsuario(x => x.ID_SituacaoUsuario == Convert.ToInt32(form["Usuario.SituacaoUsuario"]// note that i have to retrieive the entire object... the "child"
};
this.UsuarioService.AddUsuario(usuario);
}
the edit statement should be like this:
TryUpdateModel(a, "Usuario", this.GetUsuarioWhiteList(), form.ToValueProvider()); // but the form contains only the id and I can't load the child in it nor pass the object.
I had the same issue recently and I managed to solve it when I changed the cardinality ratios for my foreign keys in the child table from 1:many to 0..1:many in the Entity Designer and it worked fine.
Related
I'm using Entity Framework 5.0 for my MVC4 project. There's a problem with it. When i give a db model to any view, controller send model with no relationship
example;
I have User class and with relation departments
when i use it in controller
using(context)
{
var user = context.Find(id);
string department = user.Department.Name;
}
its working when call in context. but when i do that
using(context)
{
var user = context.Find(id);
return View(user);
}
and call in view like
Model.Department.Name
i got error.
Here is my answer but its not good
using(context)
{
var user = context.Find(id);
string department = user.Department.Name;
return View(user);
}
when i try to user Model.Department.Name in view i got no error i must do that for every relation when i use class as model. there is have better solution for this problem ? i want use all relationship in View without call these in controller.
I hope you can understand me, sorry my english.
On your DbContext you could use the .Include method to eagerly load the relations you need:
context.Users.Include(u => u.Department).FirstOrDefault(u => u.Id == id);
or if you are using an older version of entity Framework the generic version of this method might not be available:
context.Users.Include("Department").FirstOrDefault(u => u.Id == id);
The reason for this is that you haven't "loaded" the Department in your original code. As your context is wrapped in a using statement it's being disposed of before the view is created and therefore your user object lacks the data you want.
In your second code example you have specifically called into the related Department object and therefore it now exists within the User object.
You need to eager load the Department in your original line using something like
context.User.Include(c => c.Department).Find(id);
Now your user object should have this available in the view.
What are u trying to accomplish? List a view for a user with one or many departments?
I'm debugging this method for two cases: one where there is a parent, the other where there is no parent.
If there is no parent, the new Person has an id of 0 but never actually gets saved to the db.
If there is a parent, the new Person has an id of 0 in this method, but a new record is inserted into the db with the correct value (one more than the highest in the table).
What is going on here? I know I'm doing something wrong, I'm just not sure what.
I'm using EF Codefirst.
The code for the controller method:
[HttpPost]
public ActionResult Create(CreatePersonViewModel viewModel)
{
if (ModelState.IsValid)
{
var parent = _db.Persons.FirstOrDefault(s => s.PersonId == viewModel.ParentId);
var person = new Person() { Name = viewModel.Name };
// if it has a parent, build new relationship
if (parent != null)
{
person.Parent = parent;
parent.Children.Add(person);
};
_db.Save();
return RedirectToAction("detail", "person", new { personId = person.PersonId });
}
return View(viewModel);
}
If there is no parent, the new Person has an id of 0 but never actually gets saved to the db.
That's because you never tell EF that it should persist the entity. You only create a new Person() and that's it.
You should do:
dbContext.AddToPersons(person);
before calling dbContext.SaveChanges().
In the case when there is a parent, person is saved because of its relationship with parent.
Update
Just occurred to me: If you're doing code first you might not have the AddToPersons(...) method available on the data context. If this is so, you can use dbContext.Persons.AddObject(person) instead.
The fact with you are referring to is auto increment ID for you object. It is controlled by your ORM. You may want to check this question
You may want to check this link from msdn
Remarks Refresh has the dual purpose of allowing an object to be
refreshed with data from the data source and being the mechanism by
which conflicts can be resolved. For more information, see Saving
Changes and Managing Concurrency (Entity Framework). The order in
which objects are refreshed is nondeterministic. After the Refresh
method is called, the object’s original values will always be updated
with the data source value, but the current values might or might not
be updated with the data source value. This depends on the
RefreshMode. The StoreWins mode means that the object should be
updated to match the data source values. The ClientWins value means
that only the changes in the object context will be persisted, even if
there have been other changes in the data source. To ensure that an
object has been updated by data source-side logic, you can call the
Refresh method with the StoreWins value after you call the SaveChanges
method.
I have this code in a Windows Service targeted to .Net 4.5 that uses a database-first Entity Framework layer:
var existingState = DataProcessor.GetProcessState(workerId);
existingState.ProcessStatusTypeId = (int)status;
existingState.PercentProgress = percentProgress;
existingState.ProgressLog = log;
DataProcessor.UpdateProcessState(existingState);
And this code in a data processing class in the same solution:
public ProcessState GetProcessState(int id)
{
using (var context = new TaskManagerEntities())
{
var processes = (from p in context.ProcessStates.Include("ProcessType").Include("ProcessStatusType")
where p.IsActive && p.ProcessStateId == id
select p);
return processes.FirstOrDefault();
}
}
public ProcessState UpdateProcessState(ProcessState processState)
{
using (var context = new TaskManagerEntities())
{
context.ProcessStates.Add(processState);
context.Entry(processState).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
return processState;
}
ProcessState is a parent to two other classes, ProcessStatusType and ProcessType. When I run that code in the windows service, it retrieves a record, updates the entity and saves it. Despite the fact that the ProcessType child is never used in the above code, when the save on the ProcessState entity is performed, EF does an insert on the ProcessType table and creates a new record in it. It then changes the FK in the ProcessStatus entity to point it at the new child and saves it to the database.
It does not do this in the ProcessStatusType table, which is set up with an essentially identical FK parent-child relationship.
I now have a database full of identical ProcessType entries that I don't need, and I don't know why this is occurring. I feel like I'm making some obvious mistake that I can't see because this is my first EF project. Is the issue that I'm allowing the context to expire in between calls but maintaining the same entity?
Using Add will set the state of all elements to Added, which is causing the child elements to be inserted. The parent element is not inserted as you specify EntityState.Modified for this element.
Try using the following in the UpdateProcessState rather than using Add.
context.ProcessStates.Attach(processState);
context.Entry(processState).State = EntityState.Modified;
context.SaveChanges();
Attach will set the state of all elements to Unchanged and by specifying Modified for the parent element you are indicating that only this element should be updated.
On another note. You should use the strongly-typed Include(x => x.ProcessType) rather than Include("ProcessType").
I'm using a customized method for tracking individual modified properties of an n-tier disconnected entity class. I extracted it from
Programming Entity Framework: DbContext by Julia Lerman and Rowan
Miller (O’Reilly). Copyright 2012 Julia Lerman and Rowan Miller,
978-1-449-31296-1.
The code is:
public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
// bind the entity back into the context
dbContext.Set<TEntity>().Add(root);
// throw exception if entity does not implement IObjectWithState
CheckForEntitiesWithoutStateInterface(dbContext);
foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
IObjectWithState stateInfo = entry.Entity;
if (stateInfo.State == RecordState.Modified) {
// revert the Modified state of the entity
entry.State = EntityState.Unchanged;
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
entry.Property(property).IsModified = true;
}
} else {
entry.State = ConvertState(stateInfo.State);
}
}
dbContext.SaveChanges();
}
The purpose of this method is to let the EF know only a predefined set of entity fields are ready for update in the next call of SaveChanges(). This is needed in order to workaround the entity works in ASP.NET MVC 3 as follows:
on initial page load: the Get action of the controller is loading the
entity object and passing it as a parameter to the view.
The View generate controls for editing 2 of the fields of the entity,
and holds the ID of the record in a hidden field.
When hitting [save] and posting the entity back to the controller all
of the fields excepting the 3 preserved in the view comes with a null
value. This is the default behavior of the MVC binding manager.
If i save the changes back to the database the update query will of course overwrite the non mapped fields with a sentence as follows:
UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3
This is the reason i'm trying to track the fields individually and update only those fields i'm interested in. before calling the custom method with ApplyChanges() i'm adding the list of fields i want to be included in the update to the IObjectWithState.ModifiedProperties list, in order to get a SQL statement as follows:
UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3
The problem is, when marking one of the fields as modified in ApplyChanges, i.e.:
entry.Property(property).IsModified = true;
the system is throwing the following exception:
{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.
at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)
at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)
at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)
...
So the question is. There's a way to bypass this EF validation or let the context know of the existance of this system property (IsModified) that i'm trying to change?
Summary of the architeture:
EF Code first (annotation + Fluent API)
Oracle .NET EF Data provider (ODAC)
Context is injected to a cutom business context with nInject.MVC => this is the reason i customized the ApplyChanges() method from
using (var context = new BreakAwayContext()){
context.Set().Add(root);
to a simple call to the already initialized dbcontext
dbContext.Set().Add(root);
Oracle Database is created manually i.e. without the help of EF, so no EF metadata tables are used.
Thanks,
Ivan.
Very good description, however I can't find any information on why you need a transient property called "IsModified" in the object and/or why you need to tell EF about it being modified (EF won't be able to persist it anyway).
The value of the IsModified property should be set by the model binder if the property was incldued in the view anyway.
You could just add code in your ApplyChanges method to skip a property named "IsModified", or even better, filter only known properties using entry.CurrentValues.PropertyNames, e.g.:
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
if (entry.CurrentValues.PropertyNames.Contains(property)) {
entry.Property(property).IsModified = true;
}
}
Update: Ivan, very sorry I did not understand the problem better when you posted it several months ago and that I did not follow up after your added these clarifying comments. I think I understand better now. That said, I think the code snippet that I offered can be part of the solution. From looking at the exception you are getting again, I understand now that the problem that EF is detecting is that NotifyCEDDealine is not a persistent property (i.e. it is not mapped in the Code First model to a column in the database). IsModified can only be used against mapped properties, therefore you have two options: you change the code of the implementation of IObjectWithState in your entities so that non-mapped properties are not recorded in ModifiedProperties, or you use my code snippet to prevent calling IsModified with those.
By the way, an alternative to doing all this is to use the Controller.TryUpdateModel API to set only the modified properties in your entities.
Hope this helps (although I understand it is very late).
HI
I have a project with uses EF self tracking objects.I am trying to add a relationship to an object . (parentobject.relationshipObject.Add(New relationshipObject...)).
But it throws an error:
Cannot change ObservableCollection
during a CollectionChanged or
PropertyChanged event.
This error occurs in the #Region "Association Fixup" of the code created by the template.
Initially the mainobject does not bring any relationship. Only when the item is selected by the user the relationships are updated in the item.
i found that if i remove the MainObject from the collection and readd it with the relationships this error does not occur. if i only update the relationship object in the mainObject , this issue occurs when i add a new relationship object from the client side
any help is much appreciated
--code sequence is as follows
1. get all the parent entities.
2. when user select an entity get the relationship of the entity and update the relationship entity
parentCol.AsEnumerable.Where(Function(x) x.ID = e.Result.ID).FirstOrDefault().StopTracking()
parentCol.AsEnumerable.Where(Function(x) x.ID = e.Result.ID).FirstOrDefault().relationshipEntity = e.Result.relationshipEntity
parentCol.AsEnumerable.Where(Function(x) x.ID = e.Result.ID).FirstOrDefault().StartTracking()
to add a new item in the relationEntity
Dim newRel As New relationshipEntity
newRel.Ref_parent_Id = parentItem.ID
newRel.REF_rel_ID = relItem.Id
parentItem.relationshipEntity.Add(newRel) ---> Throws error here
the relationshipEntity denotes the relationship table between the parent entity and another entity (many to many relationship).
thanks
Are you trying to add a new child while setting the child's parent?
Since EF tries to fix up one way links on two way relationships, I assume this could cause such an issue.
E.g.
parent.Add(new Child { Parent = parent, Name = "abc" });
As opposed to letting EF do the other side of the connection
parent.Add(new Child { Name = "abc" });
or
new Child { Parent = parent, Name = "abc" });