Add an Object to Entity Framework using a navigational property - entity-framework

Hi I use C# and EF 4.
I have two Entities CmsContent and CmsJob.
CmsJob has a navigational property to CmsContent.
I need add a CmsContent Object to CmsJob using the navigational property.
My code run with no error but I cannot persist the new entry om DataBase.
Could you tell me what I'm doing wrong?
Please provide me an example of code. Thanks for your support!
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
CmsContent myContent = context.CmsContents.FirstOrDefault(x => x.ContentId == contentId);
CmsJob myJob = context.CmsJobs.FirstOrDefault(x => x.JobId == jobId);
myJob.CmsContents.Add(myContent);
}

Based on comments under #Hasan's answer you have incorrectly defined database. Your Job and Content are in many-to-many relation so you have junction table called CmsJobsContents but this table is missing primary key. Because of that it is read-only for EF and you cannot create new relations in your application. You must go to your database and mark both FKs in the CmsJobsContents as primary keys. After that update your model from database and you should be able to save changes.

That's because you haven't saved changes. Try this:
using (CmsConnectionStringEntityDataModel context = new CmsConnectionStringEntityDataModel())
{
CmsContent myContent = context.CmsContents.FirstOrDefault(x => x.ContentId == contentId);
CmsJob myJob = context.CmsJobs.FirstOrDefault(x => x.JobId == jobId);
myJob.CmsContents.Add(myContent);
context.SaveChanges();
}

Related

Excess context saving (Entity Framework)

Help me please resolve one problem.
I use Entity Framework for work with datebase.
My situation:
I have two tables which i need to fill. One of them must to contain identificator from the second table. Now I insert data to first table and make save changes. It give me identificator for the entity and I can to insert in second table the row with this identificator from first entity.
As a result, I have two save changes for my context. And I think that it's bad practice.
Code example:
var registration = new Registration()
{
//fill some properties
};
_registrationEntityService.Insert(registration);
context.SaveChanges();
var profile = new Profile()
{
//fill some properties
profile.RegId = registration.Id
};
_profileEntityService.Insert(profile);
context.SaveChanges();
How I can resolve this problem? Thanks.
EF assumes to use navigation properties like profile.Registration to resolve such problems:
var registration = new Registration()
{
//fill some properties
};
var profile = new Profile()
{
//fill some properties
profile.Registration = registration
};
context.SaveChanges();
Just ensure you generated the needed navigation property (it's generally based on foreign keys in your db). I highly recommend you to read any EF guide first http://www.entityframeworktutorial.net/EntityFramework5/add-entity-graph-using-dbcontext.aspx

Entity Framework POCO with Foreign keys

My question can better be explained via example code. I am using POCO with change tracking proxies that is generated by the C# POCO generator by default. Please see below.
Assume you have Movie, MusicDirector and Director in the database and the relationship between them is a Director & MusicDirector can direct multiple movies and a movie can have only one Director and MusicDirector. Since I am a new user and cannot post images, here is my db structure.
Movie table has MovieId, Name,MusicDirectorId,DirectorId
Director table has DirectorId, Name
MusicDirector table has MusicDirectorId, Name
Here is the link to the diagram. http://i.stack.imgur.com/ce49r.png.
I am trying to insert a new movie and the director and musicdirector "already exists" in the db. Below is my code.
Movie movie = new Movie();
movie.Name = "Movie1";
movie.Director = new Director() { Name = "DirectorA" };
movie.MusicDirector = new MusicDirector() { Name = "MusicDirectorA" };
using (TestEFEntities ctx = new TestEFEntities())
{
movie.Director = ctx.Directors.Where(x => x.Name == movie.Director.Name).FirstOrDefault();
movie.MusicDirector = ctx.MusicDirectors.Where(x => x.Name == movie.MusicDirector.Name).FirstOrDefault();
ctx.Movies.AddObject(movie);
ctx.SaveChanges();
}
Now when I do this, MusicDirector record is added again even though it is overwritten by the record from the db. You may think why I am keeping this line movie.Director = new Director() { Name = "DirectorA" }; initially, it is an Asp.net MVC app where Movie object is bound with the director and musicdirector names the user adds. So, the first 4 lines are done implicitly by MVC and think all the other lines are in the service layer. Am I missing something as this is a very basic scenario and the framework should handle it? Of course, one solution to correct this problem is to create a new Movie object and assign the records from db which I don't want to do as I have to copy all the properties from the movie object sent by controller. How can I solve this problem?
Also this works correctly in Self-Tracking Entities. Is this some kind of a limitation to POCO and it would be great if someone can explain the behavior?
If you are using POCOs, then your code should work as the way you expected. Alternatively, you can try to populated the FKs instead of assigning the whole object like this:
using (TestEFEntities ctx = new TestEFEntities())
{
movie.DirectorID = ctx.Directors
.Where(x => x.Name == movie.Director.Name).First().DirectorID;
movie.MusicDirectorID = ctx.MusicDirectors
.Where(x => x.Name == movie.MusicDirector.Name).First().MusicDirectorID;
ctx.Movies.AddObject(movie);
ctx.SaveChanges();
}

Updating a list of foreign keys in EF4, using MVC 2 Repository Viewmodel Pattern

Okay, I'm really struggling with how to update a list of foreign keys in MVC2/EF4.
I have a one to many relationship between a Template object which can have many or no TemplateScenario objects.
Essentially, I have an edit method in a controller that is trying to do this:
// POST: /Modes/Edit/1
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
Template template = _templateRepository.GetTemplate(id);
TemplateCreateViewModel viewModel = new TemplateCreateViewModel();
viewModel.Template = template;
viewModel.TemplateScenarioList = template.TemplateScenarios.ToList();
//Update the model
UpdateModel(viewModel);
UpdateModel(viewModel.Template.TemplateScenarios, "TemplateScenarioList", new[] { "ScenarioID", "TemplateID" });
_templateRepository.Save();
return RedirectToAction("Edit", new { id = template.TemplateID });
}
This code successfully updates the 'template' object. It also adds the 'templatescenario' child objects BUT only if it is the first time I have added 'templatescenarios' to this particular template. If any templatescenario objects already exist for a given template, and I try to update them based on the new list, I get this error:
"The operation failed: The relationship could not be changed because one or more
of the foreign-key properties is non-nullable. When a change is made to a
relationship, the related foreign-key property is set to a null value. If the
foreign-key does not support null values, a new relationship must be defined,
the foreign-key property must be assigned another non-null value, or the
unrelated object must be deleted."
The _templateRepository.Save(); is just calling the entities.SaveChanges() EF4 method.
I can solve this in a dirty way by passing down a list of templatescenario ids to my repository class in a custom 'update' method that looks like this:
public void Update(Template template, IList<int> templateScenarios)
{
//Delete Old Entries
foreach (TemplateScenario ts in entities.TemplateScenarios)
{
if (ts.TemplateID == template.TemplateID)
{
if (templateScenarios == null)
entities.TemplateScenarios.DeleteObject(ts);
else if (!templateScenarios.Where(tsl => tsl == ts.ScenarioID).Any())
entities.TemplateScenarios.DeleteObject(ts);
}
}
//Don't need to add anything if they are null.
if (templateScenarios == null)
return;
//Add New Entries
foreach (int ts in templateScenarios)
{
if (!entities.TemplateScenarios.Where(tsc => tsc.ScenarioID == ts && tsc.TemplateID == template.TemplateID).Any())
{
TemplateScenario tempScenToAdd = new TemplateScenario();
tempScenToAdd.ScenarioID = ts;
tempScenToAdd.TemplateID = template.TemplateID;
entities.TemplateScenarios.AddObject(tempScenToAdd);
}
}
}
But that just feels dirty and I think I'm so close with the first, more automatic method. I've scoured the internet and found some similar posts on stackoverflow but am finding it difficult to reach that 'aha' moment.
Thanks,
Tom.
Incidently, I sorted out my problem.
The problem was my joining table was incorrectly using it's own primary key instead of using a composite key based on two foreign keys. This is obviously wrong /bad practice and EF4 and UpdateModel() don't play nice.
I had inherited the DB design from an ex-collegue and thus had taken the db design as correct without thinking too much about it. Very stupid of me, I know.

How to save child relationship entities in the Entity Framework

I have an Entity Framework v1 project. I have two entities (Roles and Permissions), which have a many-to-many relationship with each other. I pass in a object to be saved (through a WCF call, I do not create it from a context myself), which has new entries in the many-to-many relationship.
I use "context.ApplyPropertyChanges" to update the record with the new properties. I know that this does not update relationships though. I attempt to either do a ChildCollection.Add(relatedObject); or ChildCollection.Attach(relatedObject).
When I use the "Add" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
When I use the "Attach" method, I get the error that: The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
I am getting quite frustrated, and I think I can hear the Entity Framework laughing at me.
Does anyone know how I can resolve this?
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
context.ApplyPropertyChanges("Roles", this);
foreach (MyPermission p in this.Permissions)
{
x.Permissions.Add(p);
// ^ or v
x.Permissions.Attach(p);
}
context.SaveChanges();
Thanks.
Wow. After 20 or so straight hours on this problem, I'm starting to hate the Entity Framework. Here is the code that appears to be working currently. I would appreciate any advice on how to make this more streamlined.
I did rework the WCF service so that there is only the one data context. Thanks Craig.
Then I had to change the code to the following:
MyRole x = context.Roles.FirstOrDefault(a => a.RoleId == this.RoleId);
if (x == null) // inserting
{
MyApplication t = this.Application;
this.Application = null;
context.Attach(t);
this.Application = t;
}
else // updating
{
context.ApplyPropertyChanges("Roles", this);
x.Permissions.Load();
IEnumerable<Guid> oldPerms = x.Permissions.Select(y => y.PermissionId);
List<MyPermission> newPerms = this.Permissions.Where(y => !oldPerms.Contains(y.PermissionId)).ToList();
IEnumerable<Guid> curPerms = this.Permissions.Select(y => y.PermissionId);
List<MyPermission> deletedPerms = x.Permissions.Where(y => !curPerms.Contains(y.PermissionId)).ToList();
// new
foreach (MyPermission p in newPerms)
{
x.Permissions.Add(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
// deleted
foreach (MyPermission p in deletedPerms)
{
x.Permissions.Remove(context.Permissions.First(z => z.PermissionId == p.PermissionId));
}
}
You are using multiple ObjectContexts concurrently (the variable context and whereever this came from). Don't do that. It will only make things very difficult for you. Use one ObjectContext at a time.
I can give more specific advice if you show more code.
I suspect you are getting the errors because the ObjectContext thinks you are trying to add a new entity but finds it already has a EntityKey. I use the AttachTo method of the ObjectContext to attach my already existing entities to their EntitySet. I have had results generating my entities from stubs or hitting the database. This way when you add the entity to the navigation property on your entity, the ObjectContext finds the entity in it's EntitySet and knows it is an existing entity and not a new one. I don't know if this is clear. I could post some code if it would help. As Mr Stuntz said in his answer, posting more of your code would help.

1-n relations and EntityKey with EntityFramework

When I have an entity that holds a reference to a singular entity I can create an EntityKey and assign that value to the EntityNameReference.Value property. It works perfectly and like a charm.
If I have an entity that holds a reference to multiple entities I cannot succeed in doing the same thing. Suppose an entity called Application that has a property that contains references to Modules (so Application has a List property called Modules).
How can I programmatically attach entity keys to that kind of property?
I tried something like this, without any success:
foreach(int idModule in selectedModules)
{
Module m = new Module();
m.EntityKey = new EntityKey("myModel.ModuleSet", "idModule", idModule);
ctx.Attach(m); //Here I have an exception
app.Modules.Add(m);
Thanks a lot for your help.
Marco
Does Module have Application navigation property? It should.
I would write something like:
foreach(int idModule in selectedModules)
{
Module m = new Module();
m.EntityKey = new EntityKey("myModel.ModuleSet", "idModule", idModule);
m.Application = app;
app.Modules.Add(m);
}
ctx.SaveChanges();