Silverlight + WCF Data Services getting InvalidOperationException :The context is already tracking a different entity with the same resource Uri - silverlight-4.0

I'm trying to replace an object with a new one and am getting the mentioned exception. I've tried several combination and can't get around it.
I have a Playlist that has Items (the Items has another nested object, but I'm leaving it out to help make my question clearer. The user can change which items are in the playlist.
if (playlistChanged)
{
// remove selectedForRemoval
IEnumerable<PlaylistItemViewModel> nonSelectedItems = selectedDisplayTemplates.Where(pivm => pivm.IsSelectedForRemoval);
foreach (temViewModel ivm in nonSelectedItems)
{
context.DeleteObject(ivm.Model);
}
// clear out and remove old items
foreach (Item item in playlist.PlaylistItems)
{
context.DeleteObject(item);
}
playlist.PlaylistItems.Clear();
// add the selectedItem(s) to the playlist
// these items can be from the Media, or other tables
// so are newly created in code on the client
foreach (ItemViewModel ivm in selectedItems)
{
playlist.Items.Add(ivm.PlaylistItemModel);
context.AddToItems(ivm.PlaylistItemModel);
}
context.BeginSaveChanges(SaveChangesOptions.Batch, new AsyncCallback((iar) =>
{
try
{
// Callback method for the async request, retrieves the status of the requested action
DataServiceResponse response = context.EndSaveChanges(iar);
}
catch (DataServiceRequestException)
{
throw;
}
}), context);
}
Any help is appreciated.
EDIT: I was overriding the Equals and ToString in Playlist partial class. After I removed those, it started working.

I was overriding the Equals and ToString in Playlist partial classes in Silverlight. After I removed those, it started working. I'm going to avoid that from now on with WCF Data Services.

If you fetched the data using a different context from the one you are trying to add/delete with, you will get the exception you posted. Either dispose of the original context you fetched the data with or explicitly call Detach on the item you are calling AddItem/DeleteObject on.

Related

How to setup a web api controller Post method

I am changing my project to post multiple objects to my db table. The issue I am having is, after I changed the Post method I do not know how to return the created route with the Id's of the objects. I have not done this before so I believe I a correct on everything else up until that point.
public async Task<IHttpActionResult> PostNewPurchaseOrderDetail([FromBody]IEnumerable<PurchaseOrderDetail> newPurchaseOrderDetail)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var context = new ApplicationDbContext())
{
context.PurchaseOrderDetails.AddRange(newPurchaseOrderDetail);
await context.SaveChangesAsync();
return CreatedAtRoute("PurchaseOrderDetailApi", new { newPurchaseOrderDetail.PurchaseOrderDetailId }, newPurchaseOrderDetail);
}
}
catch (Exception ex)
{
return this.BadRequest(ex.Message);
}
}
Error Message
Error 2 'System.Collections.Generic.IEnumerable' does not contain a definition for 'PurchaseOrderDetailId' and no extension method 'PurchaseOrderDetailId' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?) C:\Development\TexasExterior\TexasExterior\apiControllers\apiPurchaseOrderDetailController.cs 58 98 TexasExterior
The error tells you exactly what the issue is. You variable, newPurchaseOrderDetail is an enumerable of PurchaseOrderDetail, and you're trying to reference a property of PurchaseOrderDetail directly off of it. You need to get a single item from the list before you can call the property. For example:
return CreatedAtRoute("PurchaseOrderDetailApi", new { newPurchaseOrderDetail.First().PurchaseOrderDetailId }, newPurchaseOrderDetail);
Notice the .First(). However, that would only give you the id of the first item in the collection, which is probably not what you want. I'm not sure what your CreatedAtRoute method does or how it works, but you could change the second parameter to expect a collection of ids and then pass something like:
newPurchaseOrderDetail.Select(m => m.PurchaseOrderDetailId)
Which would give you a list of ids.

Unwrapping breeze Entity properties

I'm very new to breeze/knockout, but I'm 99% of the way to doing what I need to do. I'm using the Hot Towel template, and I'm successfully retrieving a list of items via breeze. The entity (ITBAL) is a database first Entity Framework entity. When I look at the JSON coming back in Fiddler, I see the correct data. The problem is that all of the properties of data.results are dependentobservables, and not the raw values themselves.
We have a custom grid control that is trying to display the data.results array. Because it is not expecting observables, it is simply displaying "function dependentobservable" instead of the value.
I tried to unwrap the object, but keep getting the circular reference error. I don't know why that is, since ITBAL isn't associated with anything.
The data as Fiddler reports it:
[{"$id":"1","$type":"WebUIHtml5HotTowel.Models.ITBAL, WebUIHtml5HotTowel","IBITNO":"A100 ","IBWHID":"1 ","IBITCL":"50","IBITSC":"3 ","IBSUSP":" ","IBVNNO":"100 ","IBPRLC":" ","IBSCLC":" ","IBCCCD":"P","IBPICD":" ","IBSAFL":"Y","IBSTCS":399.99000,"IBUSCS":0.00000,"IBAVCS":414.95214,"IBLCST":7.00000,"IBLCCC":20.0,"IBLCDT":110923.0,"IBLSCC":20.0,"IBLSDT":130111.0,"IBLXCC":19.0,"IBLXDT":990102.0,"IBMXO1":2100.000,"IBMXO2":0.000,"IBMXO3":0.000,"IBMNO1":5.000,"IBMNO2":0.000,"IBMNO3":0.000,"IBFOQ1":0.000,"IBFOQ2":0.000,"IBFOQ3":0.000,"IBOHQ1":327.000,"IBOHQ2":0.000,"IBOHQ3":0.000,"IBAQT1":1576.000,"IBAQT2":0.000,"IBAQT3":0.000,"IBBOQ1":50.000,"IBBOQ2":0.000,"IBBOQ3":0.000,"IBPOQ1":448.000,"IBPOQ2":0.000,"IBPOQ3":0.000,"IBIQT1":1446.000,"IBIQT2":0.000,"IBIQT3":0.000,"IBRMD1":10.000,"IBRMD2":0.000,"IBRMD3":0.000,"IBRYD1":10.000,"IBRYD2":0.000,"IBRYD3":0.000,"IBISM1":0.000,"IBISM2":0.000,"IBISM3":0.000,"IBISY1":0.000,"IBISY2":0.000,"IBISY3":0.000,"IBAMD1":0.000,"IBAMD2":0.000,"IBAMD3":0.000,"IBAYD1":0.000,"IBAYD2":0.000,"IBAYD3":0.000,"IBMMD1":0.000,"IBMMD2":0.000,"IBMMD3":0.000,"IBMYD1":0.000,"IBMYD2":0.000,"IBMYD3":0.000,"IBSMD1":1.0,"IBSMD2":0.0,"IBSMD3":0.0,"IBSYD1":1.0,"IBSYD2":0.0,"IBSYD3":0.0,"IBBLME":335.000,"IBBLYO":2680.000,"IBBLLY":1441.000,"IBNMTY":8.0,"IBNMLY":11.0,"IBQSMD":21.000,"IBQSYD":21.000,"IBQSLY":20.000,"IBISMD":16318.19,"IBISYD":16318.19,"IBISLY":45714.87,"IBCSMD":373.46,"IBCSYD":373.46,"IBCSLY":67.00,"IBDQMD":0.000,"IBDQYD":0.000,"IBDQLY":0.000,"IBDSMD":0.00,"IBDSYD":0.00,"IBDSLY":0.00,"IBDCMD":0.00,"IBDCYD":0.00,"IBDCLY":0.00,"IBNOMD":18.0,"IBNOYD":18.0,"IBNOLY":18.0,"IBPKMD":15.0,"IBPKYD":15.0,"IBPKLY":14.0,"IBINUS":" ","IBIAID":0.0,"IBSAID":0.0,"IBCQT1":1527.000,"IBCQT2":0.000,"IBCQT3":0.000,"IBFCST":"Y","IBDRSH":" ","IBWMIU":"JP","IBFL15":" ","IBUS20":" ","IBLPR1":0.00000,"IBLPR2":0.00000,"IBLPR3":0.00000,"IBLPR4":0.00000,"IBLPR5":0.00000,"IBLPCD":" ","IBABCC":"B","IBPRCL":0.0,"IBQBCL":" ","IBACDC":"Y","IBTDCD":" ","IBDOUM":" ","IBTP01":0.0,"IBTP02":0.0,"IBTP03":0.0,"IBTP04":0.0,"IBLMCC":20.0,"IBLMDT":130513.0,"IBTMPH":"Y","IBCOMC":" ","IBCOMF":0.00000,"IBITCT":" ","IBEOQT":0.000,"IBITCM":0.0,"IBBRVW":" ","IBPTID":" ","IBQTLT":0.0000,"IBCTY1":"AUS","IBCTY2":"AUS","IBTXCD":"1","IBREVS":"Y","IBITXC":" ","IBMNOQ":0.000,"IBSTUS":0.000,"IBUS30":" ","IBPSLN":" ","IBPLIN":"N","IBUPDP":"Y","IBDFII":"2011-08-11T00:00:00.000","IBLHRK":"A","IBPLNC":" "}]
My Controller:
[BreezeController]
public class ItemInquiryController : ApiController
{
readonly EFContextProvider<AplusEntities> _contextProvider = new EFContextProvider<AplusEntities>();
[System.Web.Http.HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<ITBAL> ItemBalances(string itemNumber, string warehouse)
{
return _contextProvider.Context.ITBALs.Where(i => i.IBITNO == itemNumber && i.IBWHID == warehouse)
.OrderBy(i => i.IBWHID)
.ThenBy(i => i.IBITNO);
}
}
The relevant portion from the viewmodel:
var manager = new breeze.EntityManager("api/ItemInquiry");
var store = manager.metadataStore;
var itbalInitializer = function (itbal) {
itbal.CompositeKey = ko.computed(function () {
return itbal.IBITNO() + itbal.IBWHID();
});
};
store.registerEntityTypeCtor("ITBAL", null, itbalInitializer);
var index = "0" + (args.pageNum * args.pageSize);
var query = new breeze.EntityQuery("ItemBalances")
.withParameters({ itemNumber: "A100", warehouse: "1" })
.take(args.pageSize);
if (index > 0) {
query = query.skip(index);
}
manager.executeQuery(query).then(function (data) {
vm.itbals.removeAll();
var itbals = data.results;//[0].Data;
itbals.forEach(function (itbal) {
vm.itbals.push(itbal);
});
vm.totalRecords(1);
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
}).fail(function (e) {
logger.log(e, null, loggerSource, true, 'error');
});
I figure I must be missing something fairly simple, but it is escaping me.
UPDATE: I removed the BreezeController attribute from the ApiController, and it works correctly.
Jon, removing the [Breeze] attribute effectively disables breeze for your application so I don't think that is the long term answer to your problem.
If you don't actually want entities for this scenario - you just want data - than a Breeze projection that mentions just the data to display in the grid would seem to be the best choice. Projections return raw data that are not wrapped in KO observables and are not held in the Breeze EntityManager cache.
If you want the data as cached entities and you also want to display them in a grid that doesn't like KO observable properties ... read on.
You can unwrap a KO'd object with ko.toJS. However, the grid is likely to complain about circular references (or throw an "out of memory" exception as some grids do) ... even if the entity has no circular navigation paths. The difficulty stems from the fact that every Breeze entity has an inherent circularity by way of its entityAspect property:
something.entityAspect.entity //'entity' points back to 'something'
Because you are using Knockout for your model library and because you say ITBAL has no navigation properties ("is not related to anything"), I think the following will work for you:
manager.executeQuery(query).then(success) ...
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
vm.itbals(unwrapped);
vm.totalRecords(1); // huh? What is that "parseInt ..." stuff?
itemBalancesGrid.mergeData(vm.itbals(), args.pageNum, parseInt(vm.totalRecords()));
})
ko.toJS is a Knockout function that recursively unwraps an object or collection of objects, returning copies of values. Then we iterate over the copied object graphs, deleting their entityAspect properties. The array of results is stuffed into the vm.itbals observable and handed along.
You can imagine how to generalize this to remove anything that is giving you trouble.
Extra
What the heck is vm.totalRecords? I sense that this is supposed to be the total number of matching records before paging. You can get that from Breeze by adding .inlineCount() to the breeze query definition. You get the value after the query returns from the data.inlineCount property.
Do you really need vm.itbals()? If all you do here is pass values to the grid, why not do that and cut out the middle man?
The following success callback combines these thoughts
function success(data) {
var unwrapped = ko.toJS(data.results).map(
function(entity) {
delete entity.entityAspect;
return entity;
});
itemBalancesGrid.mergeData(unwrapped, args.pageNum, data.inlineCount);
})

NHibernate - Handling StaleObjectStateException to always commit client changes - Need advice/recommendation

I am trying to find the perfect way to handle this exception and force client changes to overwrite any other changes that caused the conflict. The approach that I came up with is to wrap the call to Session.Transaction.Commit() in a loop, inside the loop I would do a try-catch block and handle each stale object individually by copying its properties, except row-version property then refreshing the object to get latest DB data then recopying original values to the refreshed object and then doing a merge. Once I loop I will commit and if any other StaleObjectStateException take place then the same applies. The loop keeps looping until all conflicts are resolved.
This method is part of a UnitOfWork class. To make it clearer I'll post my code:
// 'Client-wins' rules, any conflicts found will always cause client changes to
// overwrite anything else.
public void CommitAndRefresh() {
bool saveFailed;
do {
try {
_session.Transaction.Commit();
_session.BeginTransaction();
saveFailed = false;
} catch (StaleObjectStateException ex) {
saveFailed = true;
// Get the staled object with client changes
var staleObject = _session.Get(ex.EntityName, ex.Identifier);
// Extract the row-version property name
IClassMetadata meta = _sessionFactory.GetClassMetadata(ex.EntityName);
string rowVersionPropertyName = meta.PropertyNames[meta.VersionProperty] as string;
// Store all property values from client changes
var propertyValues = new Dictionary<string, object>();
var publicProperties = staleObject.GetType().GetProperties();
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
propertyValues.Add(p.Name, p.GetValue(staleObject, null));
}
}
// Get latest data for staled object from the database
_session.Refresh(staleObject);
// Update the data with the original client changes except for row-version
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
p.SetValue(staleObject, propertyValues[p.Name], null);
}
}
// Merge
_session.Merge(staleObject);
}
} while (saveFailed);
}
The above code works fine and handle concurrency with the client-wins rule. However, I was wondering if there is any built-in capabilities in NHibernate to do this for me or if there is a better way to handle this.
Thanks in advance,
What you're describing is a lack of concurrency checking. If you don't use a concurrency strategy (optimistic-lock, version or pessimistic), StaleStateObjectException will not be thrown and the update will be issued.
Okay, now I understand your use case. One important point is that the ISession should be discarded after an exception is thrown. You can use ISession.Merge to merge changes between a detached a persistent object rather than doing it yourself. Unfortunately, Merge does not cascade to child objects so you still need to walk the object graph yourself. So the implementation would look something like:
catch (StaleObjectStateException ex)
{
if (isPowerUser)
{
var newSession = GetSession();
// Merge will automatically get first
newSession.Merge(staleObject);
newSession.Flush();
}
}

How to add a new entity to a domain context and immediately see it in data bound controls before SubmitChanges?

I've got a Silverlight 4 RIA Services (SP1) app using Entity Frameworks 4 CTP5. I can databind a grid or listbox to the IEnumerable loaded by the domain context and it shows data from the server. Great.
Now I want to create a new instance of MyEntity and add it to the client-side data so that the user can see the newly added entity. MyEntity is a true entity descendant, not a POCO.
The only Add method I can find is domainContext.EntityContainer.GetEntitySet<MyEntity>().Add(newobj)
This does add the new entity to the domain context, and the domainContext.HasChanges does become true, but the new entity doesn't show up in the databound controls.
How do I get the new entity to show up in the databound controls prior to SubmitChanges?
(Probably related to this SO question from years ago that never got an answer)
Here's the server side declarations of the domain service, per requests:
[EnableClientAccess()]
public class MyDomainService : LinqToEntitiesDomainService<MyObjectContext>
{
protected override MyObjectContext CreateObjectContext()
{
return new MyObjectContext();
}
public IQueryable<MyEntity> GetMyEntities()
{
return this.ObjectContext.MyEntities;
}
public void InsertMyEntity(MyEntity MyEntity)
{
// ...
}
public void UpdateMyEntity(MyEntity currentMyEntity)
{
// ...
}
public void DeleteMyEntity(MyEntity MyEntity)
{
// ...
}
}
I've figured this out with a combination of my own trial and error and hints provided by some of the other responses to this question.
The key point I was missing was that it's not enough for the ViewModel to keep track of the DomainContext and hand out query results to the View for databinding. The ViewModel also has to capture and retain the query results if you want entity adds and deletes performed by the ViewModel to appear in the UI before DomainContext.SubmitChanges(). The ViewModel has to apply those adds to the collection view of the query results.
The ViewModel collection property for View databinding. In this case I'm using the Telerik QueryableDomainServiceCollectionView, but other collection views can be used:
public IEnumerable<MyEntity> MyEntities
{
get
{
if (this.view == null)
{
DomainContextNeeded();
}
return this.view;
}
}
private void DomainContextNeeded()
{
this.context = new MyDomainContext();
var q = context.GetMyEntitiesQuery();
this.view = new Telerik.Windows.Data.QueryableDomainServiceCollectionView<MyEntity>(context, q);
this.view.Load();
}
The ViewModel function that adds a new entity for the UI to display:
public void AddNewMyEntity(object selectedNode)
{
var ent = new MyEntity() { DisplayName = "New Entity" };
if (selectedNode == null)
{
this.view.AddNew(ent);
}
else if (selectedNode is MyEntity)
{
((MyEntity)selectedNode).Children.Add(ent);
}
}
Other responses mentioned ObservableCollection. The query results and the collection view may not return instances of ObservableCollection. They could be just IEnumerables. What is critical is that they implement INotifyCollectionChanged and IEditableCollectionView.
Thanks to those who contributed responses. I've +1'd each response that was helpful, but since none directly solved my problem I couldn't justify marking any as the definitive answer.
Your domainContext will have a property domainContext.MyEntities. Does it not show up in there when you add it?
Bind to that collection or watch that collection for changes.
domainContext.MyEntities.PropertyChanged += MyEventHandler;
I assume you bind your control to the IEnumerable which is provided by LoadOperation<TEntity>.Entities. In that case your binding source is not the DomainContext.GetEntitySet<MyEntity>().
DomainContext.GetEntitySet<MyEntity>() holds all your currently tracked instances of MyEntity, including the one you add with .Add().
LoadOperation<TEntity>.Entities only contains the instances of MyEntity that were actually loaded by your last LoadOperation/Query.
You have two options: Either add the new entity to the ItemsSource-collection for your control (I recommend that) or rebuild the collection with the contents of DomainContext.GetEntitySet<MyEntity>(). That may contain other elements that you have not cleared out before, though.

Refreshing an entity throws an NHibernate.UnresolvableObjectException

The entities and mappings I'm talking about in this question can be found here :)
Here is the context:
I have a parent view-model which helps to manage some entities, and which has its own session.
From this VM, I open another view-model (with its own session too), do some changements to the entity (add and/or remove children), and when I validate the changements, I commit the session and warns the first view-model to refresh the display:
public void Validate()
{
using (var tx = Session.BeginTransaction())
{
try
{
SelectedTeam.ClearRoster();
foreach (var teamPlayer in TeamPlayers)
SelectedTeam.AddPlayer(teamPlayer);
teamsRepository.SaveOrUpdate(SelectedTeam);
tx.Commit();
}
catch (Exception ex)
{
tx.Rollback();
}
finally
{
if (tx.WasCommitted)
ServiceLocator.Current.GetInstance<Mediator>().NotifyColleagues(MediatorMessages.DisplayEntityInfos, SelectedTeam.Id);
}
}
}
Here is the faulted method of the parent VM:
public void RefreshEntitiesListAndDisplayEntityInfos(int selectedEntityId)
{
TEntity entity = entitiesRepository.Load(selectedEntityId);
Session.Refresh(entity);
//...
}
The exception is thrown at the Refresh line:
NHibernate.UnresolvableObjectException
And the message is:
No row with the given identifier exists[Emidee.CommonEntities.PlayerInTeam#3
I can open and change the entity multiple times, but it seems that the exception is thrown when I delete a children, then add another one, and finally delete another one.
After some readings on the web, it seems that's because when I refresh the entity, and because I changed the HasMany relationship (because I have deleted a player for example), NH tries to reload the deleted row.
I've tried to add a NotFound.Ignore statement on the HasMany in my mappings, I've tried to force a new query to the DB instead of a Load, but I still get this exception.
Does someone know how I could fix that?
Thanks in advance
Mike
This is a known behavior when refreshing objects with modified collections.
To force reload, change your method to do session.Evict with the entity as a parameter. This is the code we use in our base model class:
public T ReGet<T>(T entity) where T : IEntity
{
var id = entity.Id;
Session.Evict(entity);
return Session.Get<T>(id);
}
Well, I've just spotted the problem.
To update the players list of the team, I used to clear the list, and add the new players, before updating the entity.
Now, I update the list by removing and adding only the players who have been moved by the user, and I don't have any problems at all now.
That's weird. I don't know what was wrong before, but as least that works now.