I got a problem that goes against my understanding of how it supposed to work. I have a Arquillian-test that tests a repository-method with a JPA query.
The test persists an object, and then persists an another object with the first persisted object in a field. Then it calls the repository-method. Next the test detaches (and clear the entitymanager, checks that the object is not contained in the em etc etc). Last the test checks if the related object is there or not (it shouldn't since the query is not supposed to read the relation).
As expected, when looking in the debugger, the related object is null, but when the assert actually uses the getRelatedObject-method, is loads the related object.
Pseudocode to clarify (i hope):
FirstObject f = new FirstObject();
em.persist(f);
SecondObject s = new SecondObject();
s.setFirstObject(f);
em.persist(f);
MyRepo r = new MyRepo();
SecondObject result = r.runQuery(f.getId());
em.detach(result); //result.getFirstObject is null
em.clear();
assertIsNull(result.getFirstObject()); //loads first object and test fails
Is it my understanding that is wrong, should the related object still load? I expected a LazyInit-exception.
If i'm my understanding is wrong, how to verify that a query doesn't populate related object I don't won't?
(yes, using dto-objects instead of the entity is better, I know... we have had that discussion and I was overruled)
The book Pro JPA 2 (Apress, p160) notes
"The behavior of accessing an unloaded attribute when the entity is
detached is not defined. Some vendors might attempt to resolve the
relationship, while others might simply throw an exception or leave
the attribute uninitialized."
I do not have experience of EclipseLink personally and can find anything definitive in the documentation in this area however the following links all suggest that EclipseLink will try and resolve the relationship when you access a lazy association on a detached collection.
Eclipselink Lazy Loading
http://issues.apache.org/jira/browse/OPENJPA-2483
http://blog.ringerc.id.au/2012/06/jpa2-is-very-inflexible-with-eagerlazy.html
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 a project which uses Entity Framework extensively. And it all works fine.
However, there is one update that I am trying to make and it does not update, nor is an error thrown.
The basic process is that I take an existing entity, work with it to create a new (and different type) of entity. The new entity saves just fine, but the existing one does not.
Typically when I have encountered this, it means that the entity is not attached to the context. However, it is, and I have tried attaching it again as well as "Adding" it, but I have had no luck.
if (fileRowEntity != null)
{
this.Context.FileRowEntities.Attach(fileRowEntity);
fileRowEntity.FileRowStatusId = (int)FileRowStatus.Converted;
fileRowEntity.EdiDocument = ediDocument;
}
ediDocument.InsertedDate = DateTime.Now;
ediDocument.EdiDocumentGuid = Guid.NewGuid();
ediDocument.DocumentMetatdata = null;
this.Context.EdiDocuments.Add(ediDocument);
var count = this.Context.SaveChanges();
The ediDocument is saved, but the fileRowEntity is not saved.
I am tearing my hair out trying to figure this out. I have tried a second explicit save on the fileRowEntity but it comes back with zero changes saved:
if (fileRowEntity != null)
{
fileRowEntity.EdiDocumentId = ediDocument.EdiDocumentId;
fileRowEntity.Column100 = "X";
this.Context.FileRowEntities.Attach(fileRowEntity);
count = this.Context.SaveChanges();
}
count is always zero, and the database is not updated.
I don't know what else to try to debug this.
Attaching an entity to a context does not mark it as modified.
In your case, the presumption is that the context instance where you are calling SaveChanges() on, is not the same that retrieved the fileRowEntity object, so it doesn't know that it was modified (or what was modified).
When a DbContext retrieves an object from the store, it stores a copy of its original values (unless you used AsNoTracking() on that query), and whenever it detects changes by a call to DetectChanges(), which you can make explicitly, but the normal DbContext implementation will call it by itself at many points), it'll store the new object values. If there are differences between those, the next call to SaveChanges will update the entity in the store/database.
When you attach an entity but don't mark it as modified, it doesn't know anything has changed, so you can explicitly mark the entity as modified:
Using Context.Entry(fileRowEntity).State = EntityState.Modified; should tell EF that your entity was modified, and it'll generate the update command when you call SaveChanges() for the whole entity (it'll send all the field values in the UPDATE SQL). Note that doing this also attaches the entity if it was not attached to the context (no need to attach it and then set its state).
You can also mark only the properties that were modified (or change the entity OriginalValues), this way your query will be optimized (it'll only update the fields that have actually changed). It's a bit cumbersome to track those changes yourself, but if you need that extra optimization, it's worth a shot (personally, unless there's a critical load on the database server and every bit counts, I wouldn't do this, but your choice)
There is a second reason for the above described behavior: This code will turn off change tracking:
Context.Configuration.AutoDetectChangesEnabled = false;
After I found this had been set in the constructor and removed it, all worked as expected.
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.
We have a scenario in our code when only a few properties of an entity are allowed to be changed. To guarantee that, we have code similar to this:
public void SaveCustomer(Customer customer)
{
var originalCustomer = dbContext.GetCustomerById(customer.Id);
if (customer.Name != originalCustomer.Name)
{
throw new Exception("Customer name may not be changed.");
}
originalCustomer.Address = customer.Address;
originalCustomer.City = customer.City;
dbContext.SaveChanges();
}
The problem with this code is that the call to dbContext.GetCustomerById does not always gives me a new instance of the Customer class. If the customer already has been fetched from the database, Entity Framework will keep the instance in memory and return it on every subsequent call.
This leads us to the actual problem - customer and originalCustomer may refer to the same instance. In that case, customer.Name will be equal to originalCustomer.Name and we will not be able to detect if it differs from the database.
I guess the same problem exists with most other ORMs as well, because of the identitymap design pattern.
Any ideas how this can be solved? Can I somehow force EF to always give me a new instance of the customer class?
Or should we refactor the code instead? Does anyone know of any good design patterns for this scenario?
you can try by detaching the entity from the context, this will remove all the references to the context (as well as the identitymap behaviour).
So, before passing the Customer to your method you can detach it:
yourContext.Detach(customer);
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.