I've seen a lot of articles about retrieving the identity ID of a new created record using other DBs and frameworks. I am creating a record in code in a Silverlight RIA Services app. I perform the Add method and then do a SubmitChanges on the DomainContext, but I do not see the ID. It comes up as 0. How do I get the new ID? Thanks.
Try this
private void OnProfileCreateCallback(SubmitOperation submitOperation)
{
submitOperation.ChangeSet.AddedEntities.First().GetIdentity()
....
}
OnProfileCreateCallback is a call back method used in context.submitchanges(...)
You can put custom logic if you are having multiple records as part of add.
Related
I created a WCF Dataservice and tested using Postman (Chrome extension) which work fine. Then I added the service reference which points the WCF Dataservice in my client C# library.
When I call the Transactions DbSet on my DataService, I get the number of transactions properly however all items in the list were overwritten with the last item in the list. i.e if I have 10 transactions in the database, I get 10 in my list but the data in all items are same which is the last item in the database.
I am using simple LINQ to get the data as shown below
List<Transaction>() tempList = new List<Transaction>();
tempList = GetContext().Transactions.ToList();
The weird part is when I catch the query that runs on database using the Fiddler, the output from the database is fine, which means something is happening in my client and overriding all the items in the list with the last item.
Can anyone point me how to debug this or how can I catch who is overriding my list.
Got it..found that there is no UNIQUE key on the table, so from client upon deserializing its adding the last found item to the list
According to this post, I am using a data context per call, so in each method of my WCF service, I use a using block to create a new data context.
But I have some doubts in the form to work in this way.
For example, I use a method getAllCLients() from my repository to get all the clients of the data base, then the service send to the client that call the method a list with all the clients. Then the user modify the information of some of them, three for example. The modify client perhaps I can add to a list that have the modified clients.
When I want to update this three clients, I can call a method updateClients() which receive a list of modified clients. How I am use a new data context per each method, in updateCients() get a new dataContext, without entities, so I think that I have to follow this steps:
1.- create a new data context which has the clients that I want to update. SO I need to specified the conditions for that. This is an extra operation (I get the clients before with the getAllClients() method), so I need to get again the clients.
2.- go throw the clients collection of the DBSet (I use EF 4.1) and change the information. This makes me to go throw the list that I receive from the client application too. So I must to go throw two lists. This needs resources.
3.- save the changes. This is needed anyway, so it has no required more work.
There is any way to make the step 2 easily? exist some method in dataContext to pass the values from my modified client to the client in the data context? I use POCO entities, perhaps it exists an easy way to do that.
Other question is about concurrency. If I control the concurrency with pesimistic concurrency that allow EF (with a timestamp field for example), is it better to call the updateClient() one for each client or better to pass a list with all the clients? I mean that if I use a list as parameter, if there is a concurrency issue with one client,the second for example, the first client will be update correctly, but the second not and the third neither. How can I notify to the user that there is problems with some clients?
To resume, I would like to know the best way to make updates when I have a short life datacontext.
Thanks.
Daimroc.
The service is disconnected scenario so when your client passes backs modified records you just need to process them as modified. You don't need to load all records from database for that.
public void SaveClients(List<Client> modifiedClients)
{
using (var context = new Context())
{
modifiedClients.ForEach(c =>
{
context.Entry(c).State = EntityState.Modified;
});
context.SaveChanges();
}
}
If you are using per call service and every service operation needs context you can move your context instancing to service constructor because service instance will live only to server single service call = you don't need using for every call. If you do that don't forget to implement IDisposable on your service to dispose context.
Other question is about concurrency. If I control the concurrency with
pesimistic concurrency that allow EF (with a timestamp field for
example), is it better to call the updateClient() one for each client
or better to pass a list with all the clients?
EF doesn't support pesimistic concurrency out of the box. Using timestamp is optimistic concurrency because it allows others to use the record. Pesimistic concurrency is application logic where other client is not able to select locked record for update.
The concurrency is resolved per record but the problem in this case is transaction. Each call to SaveChanges results in transaction used to process all changes in the database. So if any of your modified records is not up to date you will get concurrency exception and whole transaction is rolled back = no record is updated.
You can still overcome the issue by passing list of modified records to the service (reducing roundtrips between client and service is a best practice) but you can process each record separately by calling SaveChanges for every single record. Anyway this should be very carefully considered because each call to SaveChanges is like separate unit of work - is it really what you want?
Btw. the best practice is to make your service statless. You should avoid maintaining data between service calls and this example really doesn't need it.
I try to get above configuration working, but with no luck.
Step 1)
I started a new solution with a WCF Service Application project.
Step 2)
In this project, I added an edmx file and create a very simple model:
Entity Parent with Id and DisplayName
Entity Child with Id and ChildDisplayName
Association from Parent to Child, 1-to-m, resulting in NavigationProperties on both entities.
I generatedthe database without any problems. After generation, I inserted one Parent object with two related Child objects manually to the database.
Step 3)
I added the code generation, using the ADO.NET Self-Tracking Entity Generator.
I know that this should be done in diffrent assemblies, but to make it straight and easy, I put it all to the same project (the WCF project)
Step 4)
I just changed the IService Interface to create a simple get
[OperationContract]
Parent GetRootData(Int32 Id);
In the corresponding implementation, I take an Page object from the context and return it:
using (PpjSteContainer _context = new PpjSteContainer() )
{
return _context.ParentSet.Include("Child").Single(x => x.Id == Id);
}
Problem:
If I now run this project (the Service1.svc is start page), VS2010 automatically generates the test client to invoke the service. But once I invoke the service, I get an StackOverflowException! Debugging on the server side looks ok until it returns the object graph.
If I remove the Include("Child") everything is ok, but of course the Child objects are missing now.
I have no idea what I'm missing. I read a lot of howto's and guides, but all do it the way I did it (at least that's what I think)...
I tried the School example here, but this does not work for me as it seems the database generation and the coding in the example does not match.
So, I would much appreciate if someone could guide me how to make this work.
P.S.
Yes, all Entity-Classes are marked "[DataContract(IsReference = true)]"
Lazy-Loading is set to "false" in the edmx file
Edit:
I changed the WCF to be hosted in a console app and no longer in IIS. Of course then I had to write my own little test client.
Funny enough, now everything's working.
I of course have no idea why, but at least for my testing this is a solution...
Have a look here. Basically you have to make the serializer aware of cycles in the navigation properties.
I have created a Rest collection WCF Service using the starter kit.
Everything is fine except I need to modify the OnGetItem(string id) to accept an additional parameter such that the URI becomes
http://localhost/service.svc/1/5
where 1 = department id
and 5 = employee id
I can see the ICollectionService<ITem> within the abstract class collection service base
protected abstract TItem OnGetItem(string id); but I am not allowed to modify it.
Does anyone have any ideas on how I can make such a simple addition?
Thanks in advance.
I believe you already found your answer on this thread.
I have created an n-tier solution where I am retrieving related data from a WCF service, updating it within a Windows Forms application, and then returning the updated data via WCF to be persisted to the database. The Application, WCF Service and Database are all on different machines.
The data being retrieved consists of an object and child objects...
public Product Select(string catalogueNumber) {
return (from p in this.ProductEntities.Products.Include(#"Tracks")
where p.vcCatalogueNumber == catalogueNumber
select p).FirstOrDefault() ?? new Product();
}
The updates being applied by the client application can, as well as updating existing content, also insert additional "Track" objects.
When I receive the Product object back from the client application, I can see all of the updates correctly, however in order to save all of the changes correctly I have to jump through a few hoops...
public void Save(Product product) {
Product original = this.Select(product.vcCatalogueNumber);
if (original.EntityKey != null) {
this.ProductEntities.ApplyPropertyChanges(product.EntityKey.EntitySetName, product);
// There must be a better way to sort out the child objects...
foreach (Track track in product.Tracks.ToList()) {
if (track.EntityKey == null) {
original.Tracks.Add(track);
}
else {
this.ProductEntities.ApplyPropertyChanges(track.EntityKey.EntitySetName, track);
}
}
}
else {
this.ProductEntities.AddToProducts(product);
}
this.ProductEntities.SaveChanges();
}
Surely, there has to be an easier way to do this?
Note: I have spent the better part of the afternoon investigating the EntityBag project, but found that this has not been updated to work with EF RTM. In particular, whilst it will successfully update the existing data exceptions are thrown when mixing in new objects.
I don't have a ready-made answer for your particular scenario - but just a question: have you checked out ADO.NET Data Services (f.k.a. "Astoria") ?
They're built on top of Entity Framework, WCF's RESTful interface, and they offer a client-side experience, plus they also seem to have a decent story for not just querying, but also updating, inserting records into databases.
Could this be an option?
Check them out on MSDN, at David Hayden's blog, on Channel9, or see some of the excellent sessions at MIX08 and MIX 09
Marc
You should probably take a look at Danny Simmons' EntityBag sample.
It is designed to simplify these sorts of issues:
http://code.msdn.microsoft.com/entitybag/
As CatZ says things will be a lot easier in .NET 4.0.
One of the things we are planning on doing to help is creating a T4 template for you that generates classes for you that are capable of self-tracking, and some extra surface to make it simple for these self-tracking entities to ApplyChanges() to the context when they get back to the server tier.
Hope this helps
Cheers
Alex (PM on the Entity Framework team at Microsoft).
I see that this thread is quiet followed, so I allow myself to do a little update...
Weeeeee !
Self-Tracking entities has arrived in EF 4!
Check this out:
http://blogs.msdn.com/efdesign/archive/2009/03/24/self-tracking-entities-in-the-entity-framework.aspx
Explanation of the self-tracking mechanism by the entity framework team.
http://aleembawany.com/2009/05/17/new-features-in-entity-framework-40-v2/
Anouncement of new features in EF 4.
http://msdn.microsoft.com/en-us/magazine/ee321569.aspx
Comparison of several N-Tier patterns for disconnected entities.
Enjoy !
In Entity Framewrok 4 you can use the method "ApplyCurrentValues" to update a detached entity.
In your scenario will be something like this:
this.ProductEntities.Product.ApplyCurrentValues(product);
foreach (Track track in product.Tracks.ToList()) {
if (track.EntityKey != null)
{
//Update Entity
this.ProductEntities.Track.ApplyCurrentValues(track);
}
else
{
//New Entity
this.ProductEntities.Track.Attach(track);
}
}
I hope it will be useful
One of the limitations of Entity Framework v1.0 is Updating Entities. Unfortunately I think you are out of luck until version 2 arrives.