Silverlight + RIA: problem editing entity - silverlight

I have a Silverlight 4 application with EntityFramework as data layer.
There are two entities: Customer and Products. When I get customer from a database, the related products are also read, as I added related 'Include' attribute in customer's metadata and call Include method in get query:
public IQueryable<customer> GetCustomerSetById(int customerId)
{
return this.ObjectContext.CustomerSet
.Include(o => o.Products)
.Where(o => o.Id = customerId);
}
The problem that when I change any property in customer's product I get this exception:
This EntitySet of Type
'MyApp.Web.Models.Product' does not
support the 'Edit' operation.
But everything works if I read customer products directly, e.g. not through customer entity (CustomerContext) , but via product one (ProductContext).
Also there is the IsReadOnly=true property in a product entity.
UPDATE:
I have all CUD operations and also marked all of them with related Insert, Update and Delete attributes. Otherwise it wouldn't work at all, but it works for me in some cases as I wrote above.
Any ideas?

This is the real problem with RIA+EF so we keep all our entities in one domain service because at client side it is difficult to deal with multiple entities related via navigation properties. Think for a minute it actually makes no difference and we use EF T4 template to generate all domain service operation in one class. And we generated partial methods to intercept logic of domain service methods.

It sounds like you need to make sure you have an update operation in your domain service. It will look something like this:
public void UpdateProduct(Product product)
{
ObjectContext.Products.AttachAsModified(product, ChangeSet.GetOriginal(product));
}

RIA Services EntitySet does not support 'Edit' operation
Since the aforementioned solutions do not appear to be helping try using this:
Domain Service Wizard
This wizard should look at your entity, and generate the appropriate CRUD operations.
If you then cant update your entities you have a different problem.

Have you tried moving the Include to the end?
Return this.ObjectContext.CustomerSet
.Include(o => o.Products)
.Where(o => o.Id = customerId);
Could be:
Return (from o in this.ObjectContext.CustomerSet
where o.Id = customerId
select o).Include("Products");

Related

primary key constraint updating many-to-many self referential table in entity framework code-first

TL;DR What is the proper way of rehydrating an entity framework object with a self referential many to many relationship from a DTO and updating it with the new values so that the database updates correctly?
I have the following entity (irrelevant stuff trimmed)
public class Role
{
[Key]
[Required]
public String RoleId { get; set; }
public List<Role> Children { get; set; }
}
In my dbContext, I have set up a many to many relationship
modelBuilder.Entity<Role>().HasMany(r => r.Children).WithMany();
I'm using MVC front end, with a web-api backend for an n-tier setup, and an mssql database.
The following chain of events happens
Browser->MVC Controller->REST call to Web API->WebAPI Controller->DB Context Query
This chain happens twice, once to view the page in edit mode, and then again when the user pushes the save button to persist.
When setting children on the entity, they always already exist first (IE, you don't create the parent and the children at the same time, you are just adding an existing child to a parent)
There is a DTO used by the MVC model and web API, which I re-hydrate to the entity on the web-api side.
public IHttpActionResult UpdateRoleInfo(RoleVM roleInfo){
//lookup existing entity to update
var existing = db.Roles.FirstOrDefault(y => y.RoleId == roleInfo.ExistingRoleId);
...Something happens here (see below for things i've tried)...
db.SaveChanges();
}
My first try was this :
existing.Children = roleInfo.Children
This tried to recreate all of the existing children as part of the save. (Primary key constraint violation on the roles table)
I changed that to
//Fetch all of the roles from the database to lookup the existing children
var allRoles = GetRoles();
//Have to reselect the roles from the DB so the DB doesn't try to recreate new ones for the children.
var childrenToAdd = roleInfo.Roles.Select(role2 => allRoles.FirstOrDefault(r => r.RoleId == role2.RoleId)).ToList();
existing.Children = childrenToAdd;
This correctly works for updating a role that does not already have any children, to add some the first time, but if you update a role that already has children, it tries to re-add the children to the database a second time, getting a primary key violation on the roles_role table
I then tried pre-pending this code to the second one above,
existing.Children.Clear();
db.SaveChanges();
I would expect this to delete all the existing parent-child relationships from the many to many table for this parent, and then recreate them with the new children. Why not?
TL;DR What is the proper way of rehydrating an entity framework object with a self referential many to many relationship from a DTO and updating it with the new values so that the database updates correctly?
Try turning off auto detect changes (before retrieving from the DB) via
context.Configuration.AutoDetectChangesEnabled = false;
Then set the state to modified on the specific role object you are updating
context.Entry(role).State = EntityState.Modified;
Haven't tried this myself on a self-referencing many-to-many table, but adding & updating entities in the manner can save all sorts of headaches where EF incorrectly infers what you are adding/updating
Found the problem.
On the initial load of the entity, I was using an include statement to eager load the children.
When I updated the entity, when I fetched it from the db again, I did not eager load the children. Therefore the additions/updates were getting confused. Once I put the include in during the upload Scenario #2 above worked (the explicit clear was not needed)
db.Roles.Include("Children").FirstOrDefault(z => z.RoleId == RoleId);
Also related, if you have this same problem when dealing with relationships across different tables, make sure all the entities that are involved in the graph are from the same DB context!
https://msdn.microsoft.com/en-us/magazine/dn166926.aspx

Editing Data From a Domain Service

I need to edit data in a WCF RIA Domain Service. all examples I can find are using a datagrid.
I have the following but it is retuning a null value, where am I going wrong???
var mytask = from v in DomainRentDetail.tblRentDetails
where v.CustID == xCustID
select v;
tblRentDetail t = mytask.FirstOrDefault<tblRentDetail>();
t.ReturnDate = DateTime.Now;
DomainRentDetail.SubmitChanges();
Please give us more detail but,
I think it must be like this, In a domain service class (I suppose you are on web project)
from v in this.ObjectContext.tblRentDetails
where v.....
select v
If there are some records on your table tblRentDetails
you may check it by FirstOrDefault << Type >> ()== null
When you create your domain service class you must choose your domain(edmx). Then ObjectContext carries your entitiy objects. Ask your Entities to this ObjectContext member.
Note: The below state is an extreme stuation. Maybe you may face off laterly.
If you need diffrent model you are not created domain service class for
there are some other technics,
//Scope level domain service class definition. Not offered.
//Generally views solves this issue
using(XDomainService service=new XDomainService())
{
from one in service
from two in this.objectContext
where...
select new member
}

SL4 / EF / RIA Services - Return parents, grand parents and great grandparents in one server hit

Let's say I have Category > SubCategory > SubSubCategory > Item set up in my EF entities.
What is the best way to get Category, Subcategory, SubSubCategory and Item where Item.Property = x all in one single request to the server using WCF RIA Services?
With .Include I can only get the children of the entity, not grandchildren and further down ( or up depending on how you look at it).
Furthermore, if I do this...
public IQueryable<ToolingTreeItem> GetTree(int currentLocationId)
{
var tree = from tc in this.ObjectContext.ToolingCategories
from tg in tc.ToolingGroups
from tt in tg.ToolingTypes
from t in tt.Toolings
where t.CurrentLocationId == currentLocationId
select new ToolingTreeItem { Cat = tc, Group = tg, Type = tt, Tool = t };
return tree;
}
...the method is not available on my context in the client side project, presumably because my custom entity class ToolingTreeItem is not recognized somewhere in the mysteries of the deep chasm that is WCF RIA Services.
If it isn't obvious by now, all I want to do is populate my TreeView with Category > SubCategory > SubSubCategory > Item in a single call to the server. What is the best approach?
Many happy returns!
You should be able to load the entities by using eager loading.
Assuming that you add "[Include]" to the "parent" attributes in you metadata something similar to the code below should work (note that I have guessed the name of all relations so you will probably need to edit the code)
public IQueryable<Toolings> GetToolsWithTree(int currentLocationId)
{
var tree = from t in this.ObjectContext.Tooling.Include("ToolingType.ToolingGroup.ToolingCategory")
where t.CurrentLocationId == currentLocationId
select t;
return tree;
}
It looks like ToolingTreeItem is a complex object, rather than an entity. Unfortunately, RIA services can't generate classes on the client side that are a mix of complex objects and entities - the class has to be entirely one or the other. The two solutions that I'm aware of are to make the ToolingTreeItem an 'entity', by putting the Key attribute on a property, or just make several requests for the data. I've also found this a real limitation.

Inserting an entity and dependent entities using RIA Services

I got a question about inserting an entity with dependent entities using RIA Services (With Silverlight 4.0).
Let's say I have an Entity in my (sql) database called "Beer" and one called "Supplier", with a relationship: Beer 1 - n Supplier. There are multiple suppliers for one kind of beer.
Now there's the following use case: The user enters a new beer with, let's say, 5 suppliers.
On the silverlight view I now got two DomainDataSource's. On the Beer DomainDataSource I add and submit the new beer and on the Supplier DomainDataSource I submit the now suppliers, which contain a foreign key which links them to the beer.
My question is: How can I make sure that the Beer gets submitted first and afterwards the dependent (remember the foreign key) Suppliers?
I am aware that I could simply chain up the SubmitChanges() using the OnSubmitted event. But this solution is pretty... well... lame. It makes for some really ugly code.
Thanks for all your numerous ideas!
Unfortunately there is no way to force the order of the updates that come in the same ChangeSet.
However, if all new suppliers are submitted to the server with new beers (a big IF), you could manually check the ChangeSet in your Upddate method:
public void UpdateBeer(Beer beer)
{
foreach(ChangeSetEntry changeSetEntry in ChangeSet.Entries)
{
if (changeSetEntry.Entity.GetType() == typeof(Supplier))
{
Supplier supplier = (Supplier)changeSetEntry.Entity;
UpdateSupplierInternal(supplier);
}
}
DataContext.Beers.Attach(beer, ChangeSet.GetOriginal(beer));
}
That calls a separate method to update supplier. You still need an UpdateSupplier method or RIA will throw an exception when it exists in the ChangeSet, but the method should do nothing:
public void UpdateSupplier(Supplier supplier)
{
// do nothing
}

Linq and ObservableCollection

I have a problem with Linq and ObservableCollections in my WPF application.
Context of the problem:
I've created a very simple SQL database with two tables: User and BankAccounts.
The User Table has an one-to-many relationship with the BankAccounts Table. Next I've created Linq-to-SQL dataclasses, which worked fine ==> the assosiation between the two tables was detected as well.
Next I've created a function to retreive all Users which works fine:
DataClassesDataContext dc = new DataClassesDataContext
var query = from u in dc.Users
select u;
Now suppose I want to add a new BankAccount to each user (not very likely but still).
I could add the following code
for each(User u in query)
{
u.BankAccounts.Add(New BankAccount());
}
The above works all fine. The BankAccounts property is automaticly part of the User class, due to the assosiation in the database and Linq DataClasses.
However, in my application I first add the query results to an ObservableCollection. Hereby I could use all sorts off databinding and changenotification. This is accomplished by the following code;
ObservableCollection<User> oUsers = new ObservableCollection<User>(query);
Problem: Within the ObservableCollection I can't do anyting with the users BankAccounts property because it is now of type EntitySet<>. So I can't do the following statement anymore.
for each(User u in oUsers)
{
u.BankAccounts.Add(New BankAccount());
}
Somehow, when queryresults are added to an observablecollection It is not possible to acces the user.BankAccounts properties anymore. However, it is possible to bind the BankAccounts Property to any control, like a listbox, and it contains the correct data.
Does someone now how I can create an observableCollction (or similar collection) from wich I can access these "assosiated" properties? I'm realy looking forward for to a solution.
Thanks in advance!
Best regards,
Bas Zweeris
E: Bas.Zweeris#Capgemini.com
Keep track of the original query which will implement IQueryable, you can run any further queries you need against that.
The ObservableCollection should just be for WPF to have something to bind to - its very useful if you want to add a new collection item but not have it pushed to the database before the user has had chance to edit it.
eg.
// Create a new blank client type
var ct = new ClientType()
{
IsArchived = false,
Description = "<new client type>",
Code = "CT000",
CanLoginOnline = true
};
// Tell the data source to keep track of this object
db.ClientTypes.InsertOnSubmit(ct);
// Also add the object to the observable collection so that it can immediately be shown in the UI and editted without hitting the db
clienttypes.Add(ct);

Resources