SubmitChanges overwrites local data - silverlight

I have added properties to client side entity that is generated by the Ria Services tooling.
I'm doing this by creating a new file containing a partial class definition.
Through the UI, some changes are made to various properties of an instance of this class. The problem comes when I call the DomainContext SubmitChanges().
It seems that the changed object is sent to the server (that's good) but then it seems that something else must be happening because my object's client side properties are being reset.
How should I preserve the local data such that it survives from one SubmitChanges to the next.

This is a known problem with WCF RIA Services. You would get the same problem if you ever try to refresh the entity with a new load. If null is not a valid value for your property in the setter of the property check to see if the value is null and if it is then ignore the set.
If your property is an integer change it to an int? so that you can get a null back instead of a 0.

I guess you see this behavior (the reset of the client side object properties) after the Submitchange's response. This is normal and I wouldn't change it.
With fiddler and the wcf binary inspector take a look at the response: the server update the state of the client-side object's after the submitchange's call.Do the updated object looks empty ?

Related

SL RIA app - Insert and Update using standard generated code does not work - is there a better way?

I have a Silverlight RIA app where I share the models and data access between the MVC web app and the Silverlight app using compiler directives, and for the server, to see what context I am running under I would check to see if the ChangeSet object was non-null (meaning I was running under RIA rather than MvC). Everything works alright but I had problems with the default code generated by the domain service methods.
Let's say I had a Person entity, who belonged to certain Groups (Group entity). The Person object has a collection of Groups which I add or remove. After making the changes, the SL app would call the server to persist the changes. What I noticed happening is that the group entity records would be inserted first. That's fine, since I'm modifying an existing person. However, since each Group entity also has a reference to the existing person, calling AddObject would mark the whole graph - including the person I'm trying to modify - as Added. Then, when the Update statement is called, the default generated code would try to Attach the person, which now has a state of Added, to the context, with not-so-hilarious results.
When I make the original call for an entity or set of entities in a query, all of the EntityKeys for the entities are filled in. Once on the client, then EntityKey is filled in for each object. When the entity returns from the client to be updated on the server, the EntityKey is null. I created a new RIA services project and verified that this is the case. I'm running RIA Services SP1 and I am not using composition. I kind of understand the EntityKey problem - the change tracking done is on two separate contexts. EF doesn't know about the change tracking done on the SL side. However, it IS passing back the object graph, including related entities, so using AddObject is a problem unless I check the database for the existence of an object with the same key first.
I have code that works. I don't know how WELL it works but I'm doing some further testing today to see what's going on. Here it is:
/// <summary>
/// Updates an existing object.
/// </summary>
/// <typeparam name="TBusinessObject"></typeparam>
/// <param name="obj"></param>
protected void Update<TBusinessObject>(TBusinessObject obj) where TBusinessObject : EntityObject
{
if (this.ChangeSet != null)
{
ObjectStateManager objectStateManager = ObjectContext.ObjectStateManager;
ObjectSet<TBusinessObject> entitySet = GetEntitySet<TBusinessObject>();
string setName = entitySet.EntitySet.Name;
EntityKey key = ObjectContext.CreateEntityKey(setName, obj);
object dbEntity;
if (ObjectContext.TryGetObjectByKey(key, out dbEntity) && obj.EntityState == System.Data.EntityState.Detached)
{
// An object with the same key exists in the DB, and the entity passed
// is marked as detached.
// Solution: Mark the object as modified, and any child objects need to
// be marked as Unchanged as long as there is no Domainoperation.
ObjectContext.ApplyCurrentValues(setName, obj);
}
else if (dbEntity != null)
{
// In this case, tryGetObjectByKey said it failed, but the resulting object is
// filled in, leading me to believe that it did in fact work.
entitySet.Detach(obj); // Detach the entity
try
{
ObjectContext.ApplyCurrentValues(setName, obj); // Apply the changes to the entity in DB
}
catch (Exception)
{
entitySet.Attach(obj); // Re-attach the entity
ObjectContext.ApplyCurrentValues(setName, obj); // Apply the changes to the entity in DB'
}
}
else
{
// Add it..? Update must have been called mistakenly.
entitySet.AddObject(obj);
}
}
else
DirectInsertUpdate<TBusinessObject>(obj);
}
Quick walkthrough: If the ChangeSet is null, I'm not under the RIA context, and therefore can call a different method to handle the insert/update and save immediately. That works fine as far as I can tell. For RIA, I generate a key, and see if it exists in the database. If it does and the object I am working with is detached, I apply those values; otherwise, I force detach and apply the values, which works around the added state from any previous Insert calls.
Is there a better way of doing this? I feel like I'm doing way too much work here.
In this kind of a case, where you're adding Group entities to Person.Groups, I would think of just saving the Person and expect RIA to handle the Groups for me.
But let's take a step back, how are you trying to persist your changes? You shouldn't be saving/updating entities one by one. All you have to do is call DomainContext.SubmitChanges and all your changes should be persisted.
I work with pretty complicated projects and I seldom ever have to touch add/update code.
This question has been around with no solid answer, so I'll tell you what I did... which is nothing. That's how I handled it in RIA services, using the code above, since I was sharing the RIA client model and the server model.
After working with RIA services for a year and a half, I'm in the camp that believes that RIA services is good for working with smaller, less complex apps. If you can use [Composite] for your entities, which I couldn't for many of my entities, then you're fine.
RIA services can make throwing together small applications where you want to use the entity from EF really quick, but if you want to use POCOs or you foresee your application getting complex in the future, I would stick with building POCOs on the service end and passing those through regular WCF, and using shared behaviors by making your POCOs partial classes and sharing the behavior code with the client. When you're trying to create models that work the same on the client and the server, I had to write a ridiculous amount of plumbing code to make it work.
It definitely IS possible to do, I've done it; but there is a lot of hoops you must jump through for everything to work well, and I never fully took into consideration things like your shared model pre-loading lists for use on the client, whereas the server didn't need these preloaded everytime and actually slowed down the loading of the web page unnecessarily and countering by writing hacky method calls which I had to adopt on the client. (Sorry for the run-on.) The technique I chose to use definitely had its issues.

nHibernate: Reset an object's original state

I have a very basic query. I am using WPF Binding to edit a object which is loaded by a ISession. If somebody edits this object in the form, because of two way binding and a stateful session, whenever I close the session, changes to the object made in the form are stored back in the database. Which is the best way to avoid this?
The methods I know:
Shadow copy the object and use the copied object as the DataContext (the method I am using as of now).
ISession.Clear
Use IStatelessSession.
Is there any way to reset the object to it's original form before closing the ISession?
If you look here: http://nhforge.org/wikis/howtonh/finding-dirty-properties-in-nhibernate.aspx
It is an example of finding dirty properties. NHibernate internally tracks a persistent object's state by way of the EntityEntry object.
This is useful for you, because with a little modification to the method above, you're able to get old values back ... which you can use to reset the properties.
As for closing your session causing the object to be flushed to the database, you can set the session FlushMode to FlushMode.Never. This will mean no database sync occurs until you call Session.Flush().
Alternatively, you can hook into IFlushEntityEventListener to reset the object state. There are a reasonable examples of using the NHibernate event system on google.
See Managing the caches on NHibernate Forge:
When Flush() is subsequently called, the state of that object will be synchronized with the database. If you do not want this synchronization to occur or if you are processing a huge number of objects and need to manage memory efficiently, the Evict() method may be used to remove the object and its collections from the first-level cache.
I think that sounds like what you want.
I would suggest the use of transactions. You just rollbackthe transaction if that is the case? what do you think?

An entity with the same identity already exists in this EntitySet

I'm trying to peform an update statement using WCF RIA Services, but everytime I update I keep getting "An entity with the same identity already exists in this EntitySet. Any insight on where I can start looking or figuring out what is wrong?
Step 1
LoadOperation<Analysis> AnalysisLP = ((App)Application.Current)._context.
Load(((App)Application.Current)._context.GetAnalysisQuery().
Where(o => o.ProjectID == Convert.ToInt32(((App)Application.Current).Project.ProjectID)));
Step 2
AnalysisLP.Completed += delegate
{
if (!AnalysisLP.HasError)
{
Analysis = AnalysisLP.Entities.FirstOrDefault();
};
Step 3
((App)Application.Current)._context.Analysis.Attach(Analysis);
((App)Application.Current)._context.SubmitChanges(OnSubmitCompleted, null);
Can anyone help me, what is it i'm doing wrong??
thanks
Your object Analysis comes from the EntitySet via a query but is still attached to that EntitySet.
You just need to change its properties and call SubmitChanges. Do not try to attach it again.
To avoid the “An Entity with the same identity already exists in the EntitySet” exception, Entities that are updated, modified or deleted must always be fully refreshed from server upon saving, there can be no references held in memory to the previous instances of the entities. To prevent orhpaned instances from hanging around, I follow these rules:
Entity instances should not have any property changed event handlers assigned directly to them, rather use OnCreated or OnPropertyNameChanged partial methods instead.
When entities are added to an EntitySet, do not assign parent Entity instance references, use the foreign key ID property instead (myEntity.ParentalID = SelectedParent.ParentalID rather than myEntity.Parent = SelectedParent) because the SelectedParent probably isn’t getting reloaded upon saving because it isn’t part of the unit of work, so that reference will be held after the save and refresh.
Any combo boxes that are used as populate sources for Entity properties of the Entity need to have their EntitySet reloaded after saving as well; otherwise those related Entities populating the combo will hold references to the previous entity instance.

How to synchronize Silverlight clients with WCF?

this is probably only some conceptual problem, but I cannot seem to find the ideal solution.
I'd like to create a Silverlight client application that uses WCF to control a third party application via some self written webservice. If there is more than one Silverlight client, all clients should be synchronized, i.e. parameter changes from one client should be propagated to all clients.
I set up a very simple Silverlight GUI that manipulates parameters which are passed to the server (class inherits INotifyPropertyChanged):
public double Height
{
get { return frameworkElement.Height; }
set
{
if (frameworkElement.Height != value)
{
frameworkElement.Height = value;
OnPropertyChanged("Height", value);
}
}
}
OnPropertyChanged is responsible for transferring data. The WCF service (duplex net.tcp) maintains a list of all clients and as soon as it receives a data packet (XElement with parameter change description) it forwards this very packet to all clients but the one the packet was received from.
The client receives the package, but now I'm not sure, what's the best way to set the property internally. If I use "Height" (see above) a new change message would be generated and sent to all other clients a.s.o.
Maybe I could use the data field (frameworkElement.Height) itself or a function - but I'm not sure whether there would arise problems with data binding later on. Also I don't want to simply copy parts of the code properties, to prevent bugs with redundant code.
So what would you recommend?
Thanks!
One common solution here is to use a boolean to track your current state within OnPropertyChanged. It can be set to true when a WCF packet is received, and if it's true, you don't rebroadcast. You then set it to false after setting the property.
When you set the property normally, you'd just leave it false. This will cause it to broadcast normally when set internally, but not when set via the WCF call.
This option works, but it does require care to get right. Since you're putting this logic into a single point, it should be fairly straightforward to get correct.

RIA: Loading entity sets

I am missing something very fundamental when working with SL4 RIA entities.
I have a Domain Service with User entities. On the service context, I have a method:
EntityQuery<User> GetUsersQuery()
I perform a load like so:
context.Load(context.GetUsersQuery(), (loadOp)=>
{
// Things done when the load is completed
}, null);
When the Completed action executes, the loadOp.Entities collection is full of the User entities, but they are not attached to the context.Users entity set. It also appears that I can't attach them manually from the callback. What important step am I missing to get these tracked in the entity set?
Just to elaborate, in the completed handler, I tried:
foreach (var user in loadOp.Entities)
context.Users.Attach(user);
And I get an exception that says an entity with that name is already attached.
Yet, both context.Users and context.EntityContainer are empty.
Are you sure you are using the same instance of the context in all cases? What does context.EntityContainer.GetEntitySet<User>().Count say?
Does LoadOperation<User>.HasError return true? If so, what is the error?

Resources