Update master-detail item in an WPF ObservableCollection with EntityFramework - wpf

sorry for the silly question but this is my first approach with WPF and Entity Framework.
Let's explain my scenario...
I have a master class (Customers) and a detail class (Orders), both in my EF context.
I load my master class with a LINQ-to-EF query (including Orders) and put the IEnumerable result set in an ObservableCollection, that is used as source to a ListBox.
Then I have a DataGrid where I load the orders.
Now, because the master items are in an ObservableCollection, I am able to add, delete and update an item and that automatically reflects to my ListBox.
The problem is when I need to add an Order to a Customer.
Is there a way to update only an item in the ObservableCollection without re-load all the items from the context?
This is my code (simplified)
// Loading Customers
EntitiesContext context = new EntitiesContext();
IEnumerable<Customer> customers = from c in context.Customer.Include("Orders")
select c;
ObservableCollection<Customer> oc = new ObservableCollection<Customer>(customers);
// Binding ListBox
listCustomers.ItemsSource = oc;
...
// Retrieving a Customer (1st for instance...)
Customer c = (listCustomers.Items[0] as Customer);
Order o = new Order {Customer = c.ID, Item = "xxx"};
context.AddToOrders(o);
// How can I update the ObservableCollection?
Thanks in advance,
Manuel

Part of the problem may be manually setting the association (Foreign Key) property on the Order. When adding an object to a parent object in EF, you really just do something like the following:
Order o = new Order {Item="xxx"};
c.Orders.Add(o);
context.AddToOrders(o);
In this way you create the object, add it to the collection on the local side that you want it (behind the scenes EF tracks it's creation and remembers to give it a Key and Association Key) and it adds it to the contexts over-all Orders collection.
Be sure to call save changes when you are done with that operation and it will create the FK value and save it all to the database.
Also, of note: If you have more elaborate Object Hierarchies thyan just the two classes, (which is likely) you might look into LazyLoading if you don't want to have really goofy include statements hardwired into your code. This will retrieve needed data only when it is actually requested and will keep your LINQ statements a lot more succinct.

Related

Sorting and Filtering a collection in a Paged ListBox

I have a database with 10,000 items, to which you can add and remove while the app is running.
I have a ListBox that displays at most 100 items, and supports paging.
You can filter and sort on the 10,000 items, which needs to be immediately reflected in the listbox.
I have a button that randomly selects an item as long as it passes the filters.
What is the best set of collections/views to use for this kind of operation?
So far, my first step will be to create an ObservableCollection of ALL items in the database which we will call MainOC.
Then create a List of all items that match the filter by parsing MainOC which we will call FilteredList.
Then create a ListCollectionView based on the above List that holds the first 100 items.
CONS:
You have to recreate the ListCollectionView every time a sort operation is applied.
You have to recreate the ListCollectionView every time you page.
You have to recreate the ListCollectionView every time a filter is changed.
You have to recreate the ListCollectionView every time an item is added or removed to MainOC.
Is there a better approach that I am missing?
For example, I see that you can apply filters to a ListCollectionView. Should I populate my ListCollectionView with all 10,000 items? But then how can I limit how many items my ListBox is displaying?
Should I be doing my filtering and sorting directly against the database? I could build FilteredList directly off the database and create my ListCollectionView based off that, but this still has all the cons listed above.
Looking for any input you can provide!
This is a problem which is easily solved using DynamicData . Dynamic data is based on rx so if you are not familiar with the wonderful Rx I suggest you start learning it. There is quite a bit of a learning curve but but the rewards are huge.
Anyway back to my answer, the starting point of dynamic data is to get some data into a cache which is constructed with a key as follows
var myCache = new SourceCache<MyObject, MyId>(myobject=>myobject.Id)
Obviously being a cache there are methods to add, update and remove so I will not show those here.
Dynamic data provides a load of extensions and some controllers to dynamically interrogate the data. For paging we need a few elements to solve this problem
//this is an extension of observable collection optimised for dynamic data
var collection = new ObservableCollectionExtended<MyObject>();
//these controllers enable dynamically changing filter, sort and page
var pageController = new PageController();
var filterController = new FilterController<T>();
var sortController = new SortController<T>();
Create a stream of data using these controllers and bind the result to the collection like this.
var mySubscription = myCache.Connect()
.Filter(filterController)
.Sort(sortController)
.Page(pageController)
.ObserveOnDispatcher() //ensure we are on the UI thread
.Bind(collection)
.Subscribe() //nothing happens until we subscribe.
At any time you can change the parameters of the controllers to filter, sort, page and bind the data like follows
//to change page
pageController.Change(new PageRequest(1,100));
//to change filter
filterController.Change(myobject=> //return a predicate);
//to change sort
sortController .Change( //return an IComparable<>);
And as if by magic the observable collection will self-maintain when any of the controller parameters change or when any of the data changes.
The only thing you now have to consider is the code you need for loading the database data into the cache.
In the near future I will create a working example of this functionality.
For more info on dynamic data see
Dynamic data on Github
Wpf demo app

How to save child properties?

Breeze & Angular & MV*
I get an invoice object and expand it's necessary properties: Customer, Details, etc.
To access detail properties is easy, invoice.detail[n].property. And saving changes to existing properties (1 - n) is also easy. In my UI, I simply loop through my object vm.invoice.details to get & display all existing details, bind them to inputs, edit at will, call saveChanges(), done!
(keep in mind, in this UI, I need to complete the following too....)
Now, I have blank inputs for a new detail I need to insert.
However, I need to insert a new detail into the existing array of invoice details.
For example: invoice #5 has 3 details (detail[0], detail[1], detail[2]). I need to insert into this existing invoice, detail[3], and call saveChanges()
I've tried to call the manger.createEntity('invoice') but it complains about FK constraints. I know you can pass values as a second argument in createEntity('obj', newvalues)...but is that the correct and only method?
Seems like this should all be much easier but, well, I am at a loss so, please help where you can. TIA!
Take a look at the DocCode sample which has tests for all kinds of scenarios including this one.
Perhaps the following provides the insight you're looking for:
function addNewDetail() {
var newDetail = manager.createEntity('Detail', {
invoice: vm.currentInvoice,
... other initial values
});
// the newDetail will show up automatically if the view is bound to vm.details
}
Notice that I'm initializing the parent invoice navigation property. Alternatively, I could just set the Detail entity's FK property inside the initializer:
...
invoiceId: vm.currentInvoice.id,
...
Either way, Breeze will add the new detail to the details collection of the currentInvoice.
Your question spoke in terms of inserting the new Detail. There is no need to insert the new Detail manually and you can't manage the sort order of the vm.currentInvoice.details property any way.
Breeze has no notion of sort order for collection navigation properties.
If you need to display the details in a particular order you could add a sorting filter to your angular binding to vm.currentInvoice.details.
Make sure you have correct EntityName, because sometimes creating entity is a not as simple as it seems.Before working with entities see
http://www.getbreezenow.com/documentation/creating-entities
I will suggest you to look ur metadata file, go to last line of your file, you can see the field named "entitySet"
"entitySet":{"name":"Entity_Name","entityType":"Self.Entity_Name"}
check the entityName here i took as "Entity_Name" and then try to create the entity and use this name
manger.createEntity('Entity_Name');

Using check boxes to select Items

I am building a silverlight app. My requirement is to have Users select a number of skills that apply to them, from a larger list of skills.
Tables: Candidate => CandidateSkills; SkillsCategories => Skills. I think the schema is self explanatory. The front end will show all skills (grouped into different categories), and when the candidate logs in, only his selected skills will show in the check boxes. Fairly simple.
My question: Do I bring all the Skill entities to the front end and then get the CandidateSkill entities, loop through them and set the checkboxes accordingly or is their a easier/better way?
Thanks
I recommend building a class to use as a ViewModel. The class should contain at least a property to indicate whether the item is selected, the text to present, and either the model entity itself or its key.
You can create the set of view model objects by left-joining the set of all skills to the individual candidate's skills, and setting IsSelected to the result of a non-null test on the candidate skill.
You can then bind directly to the ViewModel.
I had a similar situation (Users to Permissions instead of Candidates to Skills) once, and I used this resource as a starting point. I hope it helps.
In my case, I had a "Save" button which, upon click, would run some code-behind code to iterate through the selected items and submit them to my Web service. Without knowing the details of your data and service implementation, I'll not clutter up the post with the nitty-gritty details.
Best of luck!
Comments for Discussion
Here is a pseudo-LINQ procedure creating view models by issuing two database calls:
var userskills = database.CandidateSkills
.Where(cs => cs.UserId == someUserId)
.Select(cs => cs.SkillId)
.ToList();
var skills = from s in database.Skills
select new CandidateSkillViewModel()
{
Text = s.SkillName,
IsSelected = userskills.Contains(s.SkillId),
Value = s.SkillId
};
mylist.ItemsSource = skills;
This would give you a bindable data source. Ultimately, using this pattern, you'll have to translate selections/deselections into inserts/deletes by hand. For me, I do this in the handler for the button click. I retrieve a fresh set of candidate skills, iterate through the items of the list, and insert/delete instances of CandidateSkill as needed.
I realize that depending on a button click to resolve my viewmodel state into database operations might not be considered by purists to be complete MVVM, but it worked for me.
I hope this helps a little more.

RIA Services get list of entities

I am working on a Silverlight application using RIA services with Entities Framework.
Forgive me, i'm fairly new with Ria services, but how do i go about getting a list of objects from the db without doing a load operation?
Example: I have an Employees table, in this table there's a IsSupervisor flag. I want to show a list of employees in a grid with a combobox cell bound to a list of supervisors (employees where isSupervisor = true).
The problem i have is that when the list of supervisors come back, the employee list only displays supervisors.
I hope this makes sense....
It's hard to really say without seeing your code, as RIA Services is pretty darn flexible.
It sounds like you are binding a DataGrid to your DomainContext's Employee EntitySet, and then making two calls to the server, one to get all employees, then one to get supervisors. If this is the case then yes your second call can wipe out the first one (depends on how you have LoadBehavior set).
But if you are querying the db to get all employees, then you already have the supervisors on the client side. Just create a separate collection that only contains the supervisors, and bind the ComboBox to this. Something like:
private void OnEmployeesLoaded(LoadOperation<Employee> loadOp) {
if(!loadOp.HasError) {
Employees = new List<Employee>(loadOp.Entities);
Supervisors = new List<Employee>(loadOp.Entities.Where(e => e.IsSupervisor));
}
}

Flex 4 data management - how do I approach this?

quite an explanation here, hope someone has the patience to read it through
I'm building an application in Flex 4 that handles an ordering system. I have a small mySql database and I've written a few services in php to handle the database.
Basically the logic goes like this:
I have tables for customers, products, productGroups, orders, and orderContent
I have no problem with the CRUD management of the products, orders and customers, it is the order submission that the customer will fill in that is giving me headaches:
What I want is to display the products in dataGrids, ordered by group, which will be populated with Flex datamanagement via the php-services, and that per se is no problem. But I also want an extra column in the datagrid that the user can fill in with the amount he wishes to order of that product. This column would in theory then bind to the db table "orderContent" via the php services.
The problem is that you would need to create a new order in the database first that the data could bind to (orderContent is linked to an order in the db).
I do not want to create a new order every time a user enters the page to look at the products, rather I would like to create the order when a button is pressed and then take everything from the datagrids on the page and submit it into the database.
My idea has been to create a separate one-column datagrid, line it up next to the datagrid that contains the products and in that datagrid the user would be able to enter the amount of that product he'd like to order.
I've created a valueObject that contains the data I would need for an order:
Code:
package valueObjects
{
public class OrderAmount
{
public var productId:int;
public var productAmount:int;
public var productPrice:Number;
public function orderAmount()
{
}
}
}
My idea was to use a service to get all products from a certain group, populate an ArrayCollection with the data, then transfer each object in that ArrayCollection to an instance of the Value Object above, add the value object to another ArrayCollection that would the be used as a dataProvider for the one-column datagrid (I would only display amount which would be set to zero at first, but use the other data upon transfering it to the db)
I've tried to use the results from the automatically generated serviceResults that retrieve the products for the datagrid and put in a resultHandler that transfers the valueobjects, however this does not seem to work.
Basically my question is this: Am I approaching this thing completely wrong, or is there a way I can get it to work the way I planned?
Would I need to create a completely new service request to get the product id:s, and price to populate the one-column datagrid.
I'll post some code if that would help.
Thank you if you read this far.
Solved it by creating a Value Object class to hold all the info needed for each row in the grid and from the php service that returned all products in a group, I looped through the result and transfered the data needed into my Value Object.
I then added each Value Object into an ArrayCollection and made that the dataProvider for the dataGrid.
No need to use two grids. I forgot how logic things get when you think of datagrid data just as an ArrayCollection and forget the visual presentation of it on screen.
Put in a few itemRenderers and the whole thing is beautiful!

Resources