Entity Framework Code-First: "The ObjectStateManager cannot track multiple objects with the same key." - asp.net-mvc-3

I'm running into an issue with Entity Framework code-first in MVC3. I'm hitting this exception:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
This is addressed many times on SO, but I'm having trouble utilizing any of the suggested solutions in my situation.
Here is a code sample:
FestORM.SaleMethod method = new FestORM.SaleMethod
{
Id = 2,
Name = "Test Sale Method"
};
FestContext context = new FestContext();
//everything works without this line:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
context.Entry(method).State = System.Data.EntityState.Modified;
context.SaveChanges();
EDITED to clarify: I am attempting to update an object that already exists in the database.
Everything works fine without the query noted in the code. In my application, my controller is instantiating the context, and that same context is passed to several repositories that are used by the controller--so I am not able to simply use a different context for the initial query operation. I've tried to remove the entity from being tracked in the ObjectStateManager, but I can't seem to get anywhere with that either. I'm trying to figure out a solution that will work for both conditions: sometimes I will be updating an object that is tracked by the ObjectStateManager, and sometimes it will happen to have not been tracked yet.
FWIW, my real repository functions look like this, just like the code above:
public void Update(T entity)
{
//works ONLY when entity is not tracked by ObjectStateManager
_context.Entry(entity).State = System.Data.EntityState.Modified;
}
public void SaveChanges()
{
_context.SaveChanges();
}
Any ideas? I've been fighting this for too long...

The problem is that this query
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
brings one instance of the SaleMethod entity into the context and then this code
context.Entry(method).State = System.Data.EntityState.Modified;
attaches a different instance to the context. Both instances have the same primary key, so EF thinks that you are trying to attach two different entities with the same key to the context. It doesn't know that they are both supposed to be the same entity.
If for some reason you just need to query for the name, but don't want to actually bring the full entity into the context, then you can do this:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name;
If what you are tying to do is update an existing entity and you have values for all mapped properties of that entity, then the simplest thing to do is to not run the query and just use:
context.Entry(method).State = System.Data.EntityState.Modified;
If you don't want to update all properties, possibly because you don't have values for all properties, then querying for the entity and setting properties on it before calling SaveChanges is an acceptable approach. There are several ways to do this depending on your exact requirements. One way is to use the Property method, something like so:
var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();
These blog posts contain some additional information that might be helpful:
http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx

The obvious answer would be that your not actually saving the method object to the database before you call:
//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;
However, I think perhaps this is just a bit a code you left out.
What if you make your entities inherit from an abstract class ie.
public abstract class BaseClass
{
public int Id { get; set; }
}
Then update your Repository to
public class Repository<T> where T : BaseClass
{
.....
public void Update(T entity)
{
_context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
}
}
Also you might want to not set the ID of your SaleMethod and let it be generated by the database. Problem could also be because SaleMethod Object in the database has Id of 2 and then you try to add another SaleMethod object with Id 2.
The error you see stems from trying to add another SaleMethod object with ID of 2 to the ObjectStateManager.

Related

Recursive linq expressions to get non NULL parent value?

I wrote a simple recursive function to climb up the tree of a table that has ID and PARENTID.
But when I do that I get this error
System.InvalidOperationException: 'The instance of entity type 'InternalOrg' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
Is there another way to do this or maybe done in one LINQ expression ?
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
{
if (dcDto.InternalOrgId != null)
{
InternalOrg io = _internalOrgRepo.Get(Convert.ToInt32(dcDto.InternalOrgId));
InternalOrgDto ioDto = new InternalOrgDto
{
Id = io.Id,
Abbreviation = io.Abbreviation,
Code = io.Code,
Description = io.Description
};
return ioDto;
}
else
{
//manually get parent department
Department parentDepartment = _departmentRepo.Get(Convert.ToInt32(dcDto.ParentDepartmentId));
DepartmentChildDto parentDepartmenDto = ObjectMapper.Map<DepartmentChildDto>(parentDepartment);
return GetInternalOrgDto(parentDepartmenDto);
}
}
Is there a way to get a top-level parent from a given child via Linq? Not that I am aware of. You can do it recursively similar to what you have done, though I would recommend simplifying the query to avoid loading entire entities until you get what you want. I'm guessing from your code that only top level parent departments would have an InternalOrg? Otherwise this method would recurse up the parents until it found one. This could be sped up a bit like:
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
{
var internalOrgid = dcDto.InternalOrgId
?? FindInternalOrgid(dcDto.ParentDepartmentId)
?? throw new InternalOrgNotFoundException();
InternalOrgDto ioDto = _context.InternalOrganizations
.Where(x => x.InternalOrgId == internalOrgId.Value)
.Select(x => new InternalOrgDto
{
Id = x.Id,
Abbreviation = x.Abbreviation,
Code = x.Code,
Description = x.Description
}).Single();
return ioDto;
}
private int? FindInternalOrgid(int? departmentId)
{
if (!departmentId.HasValue)
return (int?) null;
var details = _context.Departments
.Where(x => x.DepartmentId == departmentId.Value)
.Select(x => new
{
x.InternalOrgId,
x.ParentDepartmentId
}).Single();
if (details.InternalOrgId.HasValue)
return details.InternalOrgId;
return findInternalOrgId(details.parentDepartmentId);
}
The key considerations here are to avoid repository methods that return entities or sets of entities, especially where you don't need everything about an entity. By leveraging the IQueryable provided by EF through Linq we can project down to just the data we need rather than returning every field. The database server can accommodate this better via indexing and help avoid things like locks. If you are using repositories to enforce low level domain rules or to enable unit testing then the repositories can expose IQueryable<TEntity> rather than IEnumerable<TEntity> or even TEntity to enable projection and other EF Linq goodness.
Another option to consider where I have hierarchal data where the relationships are important and I want to quickly find all related entities to a parent, or get to a specific level, one option is to store a breadcrumb with each record which is updated if that item is ever moved. The benefit is that these kinds of checks become very trivial to do, the risk is that anywhere/anything that can modify data relationships could leave the breadcrumb trail in an invalid state.
For example, if I have a Department ID 22 which belongs to Department 8 which belongs to Department 2 which is a top-level department, 22's breadcrumb trail would be: "2,8". If the breadcrumbs are empty we have a top-level entity. (and no parent Id) We can parse the breadcrumbs using a simple string.Split() operation. This avoids the recursive trips to the DB entirely. Though you may want a maintenance job running behind the scenes to periodically inspect recently modified data to ensure their breadcrumb trails are accurate and alerting you if any get broken. (Either by faulty code or such)

Better way to handle this code

I am working on a MVC3 application with nhibernate and SQL server. Have written a normal method which is re-usable. Please find the below code and let me know a better way to handle it. I have observed to execute this piece of code it is taking a long time.
private void GetParentCompany(IEnumerable<Company> companiesList)
{
foreach (var company in companiesList)
{
long? dunsUltimateParent = company.DUNSUltimateParent;
Company ultimateParent = _companyService.GetCompanyByDUNS(Convert.ToInt64(dunsUltimateParent));
if (ultimateParent != null)
{
company.UltimateParentName = ultimateParent.CompanyName;
company.UltimateCompanyId = ultimateParent.CompanyId;
company.UltimateParentDuns = ultimateParent.DUNS;
}
}
}
Adding an index to your company.DUNS column might help. However consider to introduce a many-to-one relationship from company to (parent) company.
Place a UltimateParent property with type company in the company class. The fields UltimateParentName and UltimateParentDuns would then be redundant and you could simply get company.UltimateParent.Name for example. The mapping of UltimateParent can be done using 'References' in fluent-nhibernate.
References(x => x.UltimateParent);

Entity Framework 4 - List<T> Order By based on T's children's property

I have the following code -
public void LoadAllContacts()
{
var db = new ContextDB();
var contacts = db.LocalContacts.ToList();
grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name));
grdItems.DataBind();
}
I'm trying to sort the list of the contacts according to the area name that is contained within each contact. When I tried the above, I get "At least one object must implement IComparable.". Is there an easy way instead of writing a custom IComparer?
Thanks!
try this:
public void LoadAllContacts()
{
var db = new ContextDB();
var contacts = db.LocalContacts.ToList();
grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name).First().Name);
grdItems.DataBind();
}
this will order the contacts by the first area name, after ordering the areas by name.
Hope this helps :)
Edit: fixed error in code. (.First().Name)
I was in a discussion with #AbdouMoumen but in the end I thought I'd provide my own answer :-)
His answer works, but there two performance issues in this code (both in the answer as in the original question).
First, the code loads ALL contacts in the db. This may or may not be a problem, but in general I would recommend NOT to do this. Many modern controls support paging/filtering out of the box, so you'd be better off supplying an not-yet-evaluated IQueryable<T> instead of List<T>. If however you need everything in memory, you should delay the ToList to the last possible moment.
Second, in AbdouMoumen's answer, there is a so-called 'SELECT N+1' problem. Entity Framework will by default use lazy loading to fetch additional properties. I.e. the Areas property will not be fetched from the database until it's accessed. In this case this will happen in the controls 'for loop', while it's ordering the result set by name.
Open up SQL Server Profiler to see what I mean: you will see a SELECT statement for all the contacts, and an additional SELECT statement for each contact that fetches the Areas for that contact.
A much better solution would be the following:
public void LoadAllContacts()
{
using (var db = new ContextDB())
{
// note: no ToList() yet, just defining the query
var contactsQuery = db.LocalContacts
.OrderBy(x => x.Areas
.OrderBy(y => y.Name)
.First().Name);
// fetch all the contacts, correctly ordered in the DB
grdItems.DataSource = contactsQuery.ToList();
grdItems.DataBind();
}
}
Is it one to one relation (Contact->Area)?
if yeah then try the following :
public partial class Contact
{
public string AreaName
{
get
{
if (this.Area != null)
return this.Area.Name;
return string.Empty;
}
}
}
then
grdItems.DataSource = contacts.OrderBy(x => x.AreaName);

Linq query is triggered multiple times without any apparent reason

I’m trying to optimize my app, and I notice that one query is triggered multiple times without any apparent reason.
Is a MVC 3 App, razor and I’m using Linq and EF.
I have ViewModel class with a couple of properties.
One of these properties is the model for to view.
This is my controller (I omit all the others properties initialization):
public ActionResult companyDetail(Guid id)
{
companyDetailsViewModel myModel = new companyDetailsViewModel();
myModel.companyDetail = companiesRepository.getCompany(id);
return View(myModel);
}
This is my getCompany method:
public company getCompany(Guid id)
{
return db.companies.Single(c => c.id == id);
}
The view is too long to paste here, but is a simple view.
This is a part for example:
<div id="companyName">
<h2>
#Model.companyDetail.companyName
</h2>
</div>
<div id="companyInfoWapper">
<div class="companyInfo">
<h5>
industry: #Model.companyDetail.industry<br />
revenue: #String.Format("{0:C}", Model.companyDetail.revenue)
</h5>
</div>
</div>
I’m using AnjLab SQL Profiler to view the transactions..
When I call the view, the query it’s
called 3 times.
The Generated SQL is
the exact same on all 3.
The transaction ID is different, and also
the duration varies a little bit.
The rest are pretty much the same.
Any Idea what can be making this query to run multiple times?
Another Question!
Anyone know why db.companies.Single(c => c.id == id) ask for top 2? Like this:
SELECT TOP (2)
[Extent1].[id] AS [id], ….
Thanks in Advance!
Edgar.
Update!
The third call was my fault, and I fix it. However, I find this:
The application is Multi-language, so I write a class that implements Controller.
I trace the problem to this class. The query is triggered the second time at the end of the class when I call the Base:
base.Execute(requestContext);
and of course, the action is called again.
Any Idea how to prevent this?
Another Update!
Linkgoron ask why I call Base.Execute(), the answer is because of the localizedController implementation.
But his question make me think, and there is another part of the code:
public abstract class LocalizedControllerBase : Controller
{
public String LanguageCode { get; private set; }
private String defaultLanguage = "es";
private String supportedLanguages = "en|es|pt";
protected override void Execute(RequestContext requestContext)
{
if (requestContext.RouteData.Values["languageCode"] != null)
{
LanguageCode = requestContext.RouteData.Values["languageCode"].ToString().ToLower();
if (!supportedLanguages.ToLower().Contains(LanguageCode))
{
LanguageCode = defaultLanguage;
}
}
else {
LanguageCode = defaultLanguage;
}
System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture(LanguageCode);
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = culture;
base.Execute(requestContext);
}
}
My controller are defined like this:
public class companiesController : LocalizedControllerBase
I put a break point in “Base.Execute” and another in the “return View(myModel)” in the controller.
When I call the view companyDetail, the first stop is in base.Execute, the second is in return view, but for some reason there is a third stop in Base.Execute and a fourth in Return View, and finally the view is render.
This is making me crazy!
Anyone know why db.companies.Single(c
=> c.id == id) ask for top 2? Like this:
SELECT TOP (2) [Extent1].[id] AS [id],
….
Single() throws an exception if there is not exactly one match - so the Linq to Entities provider translates that as a top 2 query which is enough data to make a decision - throw an exception if the query returns 2 results or none, return the only result otherwise.
This doesn't make sense. If the query is executed multiple times you must call GetCompany method multiple times. Once you call Single the query is executed and Company instance is materialized so using it multiple times in view will not cause new executions. Those another calls must be caused by different part of your code.
Btw. you can avoid them by using Find (in EF 4.1) or GetObjectByKey (in EFv1 and EFv4) instead of Single. Single always executes query in database whereas Find first checks if the entity with the same entity key was already loaded and returns the instance without executing db query:
This is code for DbContext API (EF 4.1):
public company getCompany(Guid id)
{
// Id must be primary key
return db.companies.Find(id);
}
Code for ObjectContext API is little bit complicated because you first have to build EntityKey which requires entity set name. Here I described full example which works with different key types and names.

How do I delete records from a child collection in LINQ to SQL?

I have two tables in my database connected by foreign keys: Page (PageId, other data) and PageTag (PageId, Tag). I've used LINQ to generate classes for these tables, with the page as the parent and the Tag as the child collection (one to many relationship). Is there any way to mark PageTag records for deletion from the database from within the Page class?
Quick Clearification:
I want the child objects to be deleted when the parent DataContext calls SubmitChanges(), not before. I want TagString to behave exactly like any of the other properties of the Page object.
I would like to enable code like the following:
Page page = mDataContext.Pages.Where(page => page.pageId = 1);
page.TagString = "new set of tags";
//Changes have not been written to the database at this point.
mDataContext.SubmitChanges();
//All changes should now be saved to the database.
Here is my situation in detail:
In order to make working with the collection of tags easier, I've added a property to the Page object that treats the Tag collection as a string:
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
PageTags.Clear();
foreach (string tag in tags) {
PageTag pageTag = new PageTag();
pageTag.Tag = tag;
PageTags.Add(pageTag);
}
}
}
Basically, the idea is that when a string of tags is sent to this property, the current tags of the object are deleted and a new set is generated in their place.
The problem I'm encountering is that this line:
PageTags.Clear();
Doesn't actually delete the old tags from the database when changes are submitted.
Looking around, the "proper" way to delete things seems to be to call the DeleteOnSubmit method of the data context class. But I don't appear to have access to the DataContext class from within the Page class.
Does anyone know of a way to mark the child elements for deletion from the database from within the Page class?
After some more research, I believe I've managed to find a solution. Marking an object for deletion when it's removed from a collection is controlled by the DeleteOnNull parameter of the Association attribute.
This parameter is set to true when the relationship between two tables is marked with OnDelete Cascade.
Unfortunately, there is no way to set this attribute from within the designer, and no way to set it from within the partial class in the *DataContext.cs file. The only way to set it without enabling cascading deletes is to manually edit the *DataContext.designer.cs file.
In my case, this meant finding the Page association, and adding the DeleteOnNull property:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true)]
public Page Page
{
...
}
And adding the DeleteOnNull attribute:
[Association(Name="Page_PageTag", Storage="_Page", ThisKey="PageId", OtherKey="iPageId", IsForeignKey=true, DeleteOnNull = true)]
public Page Page
{
...
}
Note that the attribute needed to be added to the Page property of the PageTag class, not the other way around.
See also:
Beth Massi -- LINQ to SQL and One-To-Many Relationships
Dave Brace -- LINQ to SQL: DeleteOnNull
Sorry, my bad. That won't work.
It really looks like you need to be doing this in your repository, rather than in your Page class. There, you have access to your original data context.
There is a way to "attach" the original data context, but by the time you do that, it has become quite the code smell.
Do you have a relationship, in your Linq to SQL entity diagram, linking the Page and PageTags tables? If you don't, that is why you can't see the PageTags class from the Page class.
If the foreign key in the PageTags database table is set to Allow Nulls, Linq to SQL will not create the link when you drag the tables into the designer, even if you created a relationship on the SQL Server.
This is one of those areas where OR mapping can get kind of hairy. Providing this TagString property makes things a bit more convenient, but in the long run it obfuscates what is really happening when someone utilizes the TagString property. By hiding the fact that your performing data modification, someone can very easily come along and set the TagString without using your Page entity within the scope of a DataContext, which could lead to some difficult to find bugs.
A better solution would be to add a Tags property on the Page class with the L2S model designer, and require that the PageTags be edited directly on the Tags property, within the scope of a DataContext. Make the TagString property read only, so it can be genreated (and still provide some convenience), but eliminate the confusion and difficulty around setting that property. This kind of change clarifies intent, and makes it obvious what is happening and what is required by consumers of the Page object to make it happen.
Since Tags is a property of your Page object, as long as it is attached to a DataContext, any changes to that collection will properly trigger deletions or insertions in the database in response to Remove or Add calls.
Aaron,
Apparently you have to loop thru your PageTag records, calling DeleteOnSubmit for each one. Linq to SQL should create an aggregate query to delete all of the records at once when you call SubmitChanges, so overhead should be minimal.
replace
PageTags.Clear();
with
foreach (PageTag tag in PageTags)
myDataContext.DeleteOnSubmit(tag);
Aaron:
Add a DataContext member to your PageTag partial class.
partial class PageTag
{
DataClassesDataContext myDataContext = new DataClassesDataContext();
public string TagString {
..etc.
Larger code sample posted at Robert Harvey's request:
DataContext.cs file:
namespace MyProject.Library.Model
{
using Tome.Library.Parsing;
using System.Text;
partial class Page
{
//Part of Robert Harvey's proposed solution.
MyDataContext mDataContext = new TomeDataContext();
public string TagString {
get {
StringBuilder output = new StringBuilder();
foreach (PageTag tag in PageTags) {
output.Append(tag.Tag + " ");
}
if (output.Length > 0) {
output.Remove(output.Length - 1, 1);
}
return output.ToString();
}
set {
string[] tags = value.Split(' ');
//Original code, fails to mark for deletion.
//PageTags.Clear();
//Robert Harvey's suggestion, thorws exception "Cannot remove an entity that has not been attached."
foreach (PageTag tag in PageTags) {
mDataContext.PageTags.DeleteOnSubmit(tag);
}
foreach (string tag in tags) {
PageTag PageTag = new PageTag();
PageTag.Tag = tag;
PageTags.Add(PageTag);
}
}
}
private bool mIsNew;
public bool IsNew {
get {
return mIsNew;
}
}
partial void OnCreated() {
mIsNew = true;
}
partial void OnLoaded() {
mIsNew = false;
}
}
}
Repository Methods:
public void Save() {
mDataContext.SubmitChanges();
}
public Page GetPage(string pageName) {
Page page =
(from p in mDataContext.Pages
where p.FileName == pageName
select p).SingleOrDefault();
return page;
}
Usage:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string pageName, FormCollection formValues) {
Page updatedPage = mRepository.GetPage(pageName);
//TagString is a Form value, and is set via UpdateModel.
UpdateModel(updatedPage, formValues.ToValueProvider());
updatedPage.FileName = pageName;
//At this point NO changes should have been written to the database.
mRepository.Save();
//All changes should NOW be saved to the database.
return RedirectToAction("Index", "Pages", new { PageName = pageName });
}

Resources