I have written a update method, given below:
public CANDIDATE UpdateCandidateDetails(CANDIDATE objCandidate)
{
using (var context = new URMSNEWEntities())
{
context.CANDIDATES.Attach(objCandidate);
context.ObjectStateManager.ChangeObjectState(objCandidate, System.Data.EntityState.Modified);
context.SaveChanges();
return objCandidate;
}
}
But when updating it is giving following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
As Gert mentions in the comments that error is telling you that your object objCandidate, is already being tracked by another context.
You can't attach an already attached object, nor should you want to, as the two contexts are more than likely going to be in conflicting states.
In theory, you could Detach your object from the context to which it currently belongs, but that's likely to cause additional complications.
To track down where the object is attached (and the context to which it is attached), you'll have to look through your code to the place where your objCandidate was created (or attached), there will be another context that has been instantiated, from which you're obtaining the objCandidate object.
The best solution to the problem will likely involve sharing a common context throughout certain parts of your application.
Search this site for the UnitOfWork and Repository patterns for some excellent information/advice about how to manage your contexts. e.g. entity framework + repository + unit or work question
Good luck.
Related
I'm a newbie in developing in CRM, so I want to get some things clear. And you first you need to know the reason why you have to do something in order to fully understand it. So lets get to the question.
I know you have to do this when making a plugin:
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.InputParameters.Contains("Target") && context.InputParameters.["Target"] is Entity)
{
var entity = (Entity)context.InputParameters["Target"];
if(entity.LogicalName == "myEntity")
{
//Do something with your entity
}
}
Now, in the PluginRegistration Tool, you can set up that your plugin will be fired on the defined Message and which entities (and specific attributes from them) will be affected by it, besides other stuff.
I can see the validations are very useful when manipulating several entities with a single plugin.
Now, let's suppose you only are updating a single entity with your plugin. Why should I check if the entity that's on "Target" is the entity I want to work on, if I already know because I set it up for that entity in particular? What can an Entity be otherwise in that scenario?
Also, in what cases "Target" is NOT an Entity (in the current context)?
Thanks in advance, and sorry if this is a silly question.
See this answer: Is Target always an Entity or can it be EntityReference?
Per the SDK (https://msdn.microsoft.com/en-us/library/gg309673.aspx):
Note that not all requests contain a Target property that is of type
Entity, so you have to look at each request or response. For example,
DeleteRequest has a Target property, but its type is
EntityReference.
The bottom line is that you need to look at the request (all plugin's fire on an OrganizationRequest) of which there are many derived types (https://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.organizationrequest.aspx) to determine the type for the Target property.
As Nicknow said, the Input Parameters will change depending on the Message being executed.
You can get that information from the MSDN (every request will list the Input Parameters under the Properties section, for instance the CreateRequest) or using the ParameterBrowser.
There's also a really nice alternative to deal with this in a type-safe approach (intellisense included) described in the following blog article: mktange's blog.
I have an Update method in my repository which I'm using to update articles on my project. I was initially using this method only to carry out admin edits for articles. It handles that correctly, but I decided I'd like to add a simple mechanism to calculate "most read" articles. In order to do that, I'd like to update TimesRead property each time an article has been viewed. This has been giving me trouble with the updates which seem to revolve around using ObjectStateManager.ChangeObjectState. Here's my Update method:
public void Update(Article article)
{
if (article == null) return;
db.Articles.Attach(article);
db.ObjectStateManager.ChangeObjectState(article, EntityState.Modified);
db.SaveChanges();
}
In my AdminController the following method updates correctly:
[HttpPost]
public ActionResult Edit(AdminEditViewModel viewModel)
{
if (ModelState.IsValid)
{
Article article = Mapper.Map<AdminEditViewModel, Article>(viewModel);
articleRepository.Update(article);
return RedirectToAction("Index");
}
viewModel.Categories = new SelectList(categoryRepository.GetAll(), "CategoryID", "Name", viewModel.CategoryID);
return View(viewModel);
}
However, in the TimesRead scenario, the update will trigger an exception of:
The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state.
Relevant code from that controller method:
var model = articleRepository.GetByID(id);
model.TimesRead++;
articleRepository.Update(model);
return View(model);
After having a look around to see what I can do to solve this, I came across the answer to this SO question. So I implemented that answer by replacing my Update method with the code suggested. This also works correctly in my admin scenario but not in the TimesRead scenario. The following exception is thrown:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
The exceptions are quite clear in their meaning but it does leave me wondering how I am supposed to handle simple updates such as these. I found that I can "fool" the EF into thinking the model is unchanged by setting EntityState.Unchanged and that will update TimesRead but give an exception for admin updates, stating the ObjectStateManager doesn't hold a reference to the object.
It's also clear to me how these scenarios differ. The Edit action is mapping properties from a ViewModel onto a new, unattached Article object, whereas, ArticleController is dealing with an object retrieved directly from the context. That leaves me with the feeling I should refactor one of those controller methods so the steps taken to update are the same. I'm just not really sure how I should even approach that as both approaches seem like they should be able to coexist to me. So my question is, what can I change to get both types of update to work correctly?
Thanks for your time and I'm really sorry for the amount of code posted. I just feel it is all relevant to the problem.
The primary difference between your two methods is that the Admin Edit method creates a new Article from your AdminEditViewModel, then it attaches this newly created Article to your database. This works because it's a new object that has never been attached to a dc.
In the second case, you get an Article from the repository, update that Article, then try and attach it again, this fails because it's not a newly created Article, it's an Article returned from the db Context in the first place so it's already attached. and you are trying to attach it again.
I was getting an error
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
After i googled it i found IsAttachedTo extension method there:
Is is possible to check if an object is already attached to a data context in Entity Framework?
here is my code:
foreach (string s in types)
{
Subscription subscription = new Subscription { Id = Int32.Parse(s) };
if (service.repository._context.IsAttachedTo(subscription))
service.repository._context.Detach(subscription);
service.repository._context.AttachTo("Subscriptions", subscription); //error here
horse.Subscriptions.Add(subscription);
}
But when the subscription with the same key appeared in the foreach loop the extension method IsAttachedTo returning false every time, it is does not detect that there is already such entity attached. And in result i am getting the same error:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
Why it is could be?
What can i do to fix that?
I have little code review for you because your sample code scares me.
You probably read a lot about fancy design patterns and layered architectures and you started to use them yourselves. Unfortunately you missed the main point. What the hell is this?
service.repository._context.XXX
Why do you bother with any service layer or repository layer if they don't encapsulate their logic? Why do you expose repository on the service? Nobody should know about service internal implementation? Even worse why do you expose context on the repository? That spoiled the whole point of the repository!
There are a lot of supporting rules for writing high quality object oriented code. One of this rules is called Law of Demeter. You don't have to follow each rule, you also don't have to follow rules all the time but in case of layered architecture this law is a must.
If you have three layers A -> B -> C the layer A can call methods on the layer B but it doesn't know about C and it cannot reach its methods. If it can, it is not a new layer but it is the same layer as B and the layer A doesn't need to call it through B, it can call it directly.
In your example you have just exposed D to A because A is current layer, B is service, C is repository and D is context.
One more points about your code. There are well known naming conventions. These conventions are not about I like this and you like that but about the fact that framework you are using follow these conventions strictly so using another one to mix your naming convention with framework naming convention make your code look messy.
I'm sorry, If this was only some example code to make your code structuring clear. I just needed to describe how wrong this code is.
Now to your real problem. The method you have referenced from the related question will not work in your case. I think it will work only if you load the subscription from the database. The reason is that the referenced method uses EntityKey (either internally or directly) to get the entity from context but your new entity doesn't have the entity key yet. I expect that calling TryGetObjectStateEntry for your entity will always return Detached. Entity key it is created during attaching or you have to build it manually.
If you want some IsAttachedTo method try this:
public bool IsAttachedTo<T>(this ObjectContext context, T entity) where T : IEntity
{
return context.GetObjectStateEntries(~EntityState.Detached)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<T>()
.Any(e => e.Id == entity.Id);
}
And make sure that your entity implements helper interface
public interface IEntity
{
int Id { get; }
}
But to be able to detach attached entity you will need:
public T GetAttached<T>(this ObjectContext context, T entity) where T : IEntity
{
return context.GetObjectStateEntries(~EntityState.Detached)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<T>()
.SingleOrDefault(e => e.Id == entity.Id);
}
You will have to detach instance returned from this method.
Anyway I would start to think why do you need that for the first place because it looks like your architecture has another wrong concept. Why don't you use attached entities directly? If you don't use them why do you even keep the context with them?
It's likely that IsAttachedTo does not compare by the key (Id) but by object identity. Because you create a new Subscription for every item in the loop the objects are all different instances.
Since you seem to have objects with same Id in your types collection but in the end only want to add one object per key into the context you can perhaps make your life easier by filtering out the duplicates in the first place:
var distinctTypes = types.Distinct();
foreach (string s in distinctTypes)
{
Subscription subscription = new Subscription { Id = Int32.Parse(s) };
service.repository._context.AttachTo("Subscriptions", subscription);
horse.Subscriptions.Add(subscription);
}
This way there should be only one object per key which gets attached to the context.
I am building an ASP.NET 4.0 MVC 2 app with a generic repository based on this blog post.
I'm not sure how to deal with the lifetime of ObjectContext -- here is a typical method from my repository class:
public T GetSingle<T>(Func<T, bool> predicate) where T : class
{
using (MyDbEntities dbEntities = new MyDbEntities())
{
return dbEntities.CreateObjectSet<T>().Single(predicate);
}
}
MyDbEntities is the ObjectContext generated by Entity Framework 4.
Is it ok to call .CreateObjectSet() and create/dispose MyDbEntities per every HTTP request? If not, how can I preserve this object?
If another method returns an IEnumerable<MyObject> using similar code, will this cause undefined behavior if I try to perform CRUD operations outside the scope of that method?
Yes, it is ok to create a new object context on each request (and in turn a call to CreateObjectSet). In fact, it's preferred. And like any object that implements IDisposable, you should be a good citizen and dispose it (which you're code above is doing). Some people use IoC to control the lifetime of their object context scoped to the http request but either way, it's short lived.
For the second part of your question, I think you're asking if another method performs a CRUD operation with a different instance of the data context (let me know if I'm misinterpreting). If that's the case, you'll need to attach it to the new data context that will perform the actual database update. This is a fine thing to do. Also, acceptable would be the use the Unit of Work pattern as well.
I have an ADO.Net Data Service that I am using to do a data import. There are a number of entities that are linked to by most entities. To do that during import I create those entities first, save them and then use .SetLink(EntityImport, "NavigationProperty", CreatedEntity). Now the first issue that I ran into was that the context did not always know about CreatedEntity (this is due to each of the entities being imported independently and a creation of a context as each item is created - I'd like to retain this functionality - i.e. I'm trying to avoid "just use one context" as the answer).
So I have a .AddToCreatedEntityType(CreatedEntity) before attempting to call SetLink. This of course works for the first time, but on the second pass I get the error message "the context is already tracking the entity".
Is there a way to check if the context is already tracking the entity (context.Contains(CreatedEntity) isn't yet implemented)? I was thinking about attempting a try catch and just avoiding the error, but that seems to create a new CreatedEntity each pass. It is looking like I need to use a LINQ to Data Services to get that CreatedEntity each time, but that seems innefficient - any suggestions?
I think you should look at the EntityState property of your entity.
Only if it is of the value EntityState.Detached than you have to add it to your context.
Do not forget the following remark:
This enumeration has a FlagsAttribute
attribute that allows a bitwise
combination of its member values.
I would create a extension method:
public static class EntityObjectExtensions
{
public static Boolean IsTracked(this EntityObject self)
{
return (self.EntityState & EntityState.Detached) != EntityState.Detached;
}
}
When trying to check whether the context was tracking the entity that I wanted to update (or add) I was pretty disapointed when I found that the context.Entites.Contains(currentItem) didn't work.
I got around it using:
if (context.Entities.Where(entities => entities.Entity == currentItem).Any())
{
this.service.UpdateObject(currentItem);
}