Skip to main content
Home Forums Silverlight Programming WCF RIA Services Related Entities: linking when creating new entity
7 replies. Latest Post by Maca007 on November 11, 2009.
(1)
maca007
Member
11 points
22 Posts
11-04-2009 6:56 PM |
I have 3 business object classes: Policy, Location and Property. They are related as such: Policy 1 -> n Location 1 -> 1 Property.
I have setup the relationships between the classes and the relationships are working, becuase when I read these classes in from the database I can access each of the classes from its parent. For example: "context.Policis[0].Locations[0].Property.Name" is possible once all data is read (assuming this data exists).
This allows me to display a list of Locations and then when the user clicks on a location the code can get the current Property. For example:
private void listLocations_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBox listBox = sender as ListBox; FarmLocation farmLocation = listBox.SelectedItem as FarmLocation; FarmViewModel.LocationSelected(farmLocation); }... public void LocationSelected(FarmLocation farmLocation) { CurrentFarmProperty = farmLocation.FarmProperty; }
NOTE: My UI is binding to CurrentFarmProperty.
The above works really nicely until I actually add a new property. Here is the code for adding a Property:
public int AddFarmProperty(FarmProperty farmProperty) { farmProperty.Id = 1; //default if (context.FarmProperties.Count > 0) { farmProperty.Id = context.FarmProperties.Max(prop => prop.Id) + 1; } context.FarmProperties.Add(farmProperty); return farmProperty.Id; }
NOTE: the farmProperty is created via a child window.
The problem with the above is that the context does not reflect the addition. That is, I'm expecting that the location class's Property member should reference the newly created Property. The location's Property member is null hence the selection changed code does not work.
Is there a way to get the context to reflect the addition through out its collection of entities using the relationships of the data?
EDIT: I should have also mentioned that my aim is to allow the users to build up a policy (add/edit locations and properties), and then press Save (to save the entire policy) or Cancel (to ignore changes made).
11-04-2009 7:52 PM |
I've just been checking out Brad Abrams heirachal data example (http://blogs.msdn.com/brada/archive/2009/08/10/business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update-part-hierarchal-data.aspx) and he manually maintians the relationship between the objects. His code:
1 private void Button_Click(object sender, RoutedEventArgs e) 2 { 3 try 4 { 5 var q = new Quotes(); 6 q.Quote = newQuoteTextBox.Text; 7 var emp = dataGrid1.SelectedItem as SuperEmployee; 8 q.SuperEmployee = emp; 9 10 //todo: do a real point generator.. 11 q.Points = random.Next(0, 10); 12 emp.Quotes.Add(q); 13 newQuoteTextBox.Text = ""; 14 } 15 catch (Exception validationException) 16 { 17 errorBox.Text = validationException.Message; 18 } 19 }
NOTE: lines 7 and 8 he makes sure that the currently selected SuperEmployee is correctly referenced in the new quote.
So in my example:
1 public int AddFarmProperty(FarmProperty farmProperty, FarmLocation farmLocation) 2 { 3 farmProperty.Id = 1; //default 4 5 if (context.FarmProperties.Count > 0) 6 { 7 farmProperty.Id = context.FarmProperties.Max(prop => prop.Id) + 1; 8 } 9 10 context.FarmProperties.Add(farmProperty); // add to the RIA classes 11 12 farmLocation.FarmProperty = farmProperty; 13 farmProperty.FarmLocation = farmLocation; 14 15 return farmProperty.Id; 16 }
NOTE: lines 12 and 13 I make sure that the location knows about the property and vice versa.
Does anyone know if this is the correct way of maintaining the hierachal data on the client side? I was hoping for some more RIA Services magic to do this for me.
ColinBlair
Contributor
6741 points
1,318 Posts
11-04-2009 11:12 PM |
When you add the new "Property" entity to the context as long as the keys were setup correctly then your Location entity should see the new Property entity. If it isn't working then I am going to guess that you actually have more than one instance of your DomainContext. I usually recommend putting a breakpoint in the constructor of the DomainContext and seeing how many times it gets called.
11-05-2009 12:00 AM |
ColinBlair:When you add the new "Property" entity to the context as long as the keys were setup correctly then your Location entity should see the new Property entity. If it isn't working then I am going to guess that you actually have more than one instance of your DomainContext. I usually recommend putting a breakpoint in the constructor of the DomainContext and seeing how many times it gets called.
Colin,
Thanks for you reply.
What you described is what I thought should happen but it wasn't.
I think I have my keys are setup correctly otherwise the entities would not be joined when I load from the database.
I probably do have more than one instance of the domain context because I have 2 View Models and I am re-using the same domain context. There's no real reason that I need to re-use the same context, so I could split it into 2. Or maybe I need to create one instance of the domain context and then feed that one instance to each ViewModel.
What do you suggest?
11-05-2009 10:28 AM |
I would recommend the opposite, keep the single DomainContext and make sure you only have one instance. Either put the DomainContext in your application object or put it in a containter such as Unity.
Maca007
11-11-2009 12:24 AM |
I have been doing some experimenting (on and off) and this is what I have found.
I created a test project which contains the same entities as mentioned above (Policy, Location, Porperty). I created one instance of the domain service in my App.xaml.cs constructor and then put a break point in my DomainContext constructor as suggested by Colin. The result was that it was only called once (tick).
I then created some data to reflect the relationships between the entity lists and called the load methods. I then viewed the context to visually check that the relationships where correct, which they were (tick).
I then put a button on the screen which created a new Location and then added it to the context:
1 private void AddLocation_Click(object sender, RoutedEventArgs e) 2 { 3 Location loc = new Location(); 4 loc.Id = context.Locations.Count + 1; 5 loc.PolicyId = 1; 6 loc.Name = "New Location: " + loc.Id.ToString(); 7 8 context.Locations.Add(loc); 9 //context.Policies[0].Locations.Add(loc); 10 }
Line 8 adds the new Location to context's Locations list. The result is that the new Location reference to the the policy reflected the addition (tick).
However if I looked at the Policies' location list the new location was not in the list. To actually get the list to reflect the change I had to put in the code at line number 9. This was not expected. :( I was hoping that both sides of the entity relationships would get updated.
Am I still doing something wrong?
11-11-2009 8:05 AM |
Take a look in the generated code and find the definition of Policy.Locations (it should be an EntityCollection). That EntityCollection has a filter method that you can put a breakpoint in. Take a look at that code and see if you can find why it isn't being triggered. I am wondering if your keys aren't setup correctly.
11-11-2009 4:31 PM |
Thanks again Colin for the suggestion. I'll give it a go sometime today.
Here are my test classes with their associations:
public class Policy { [Key] public int Id { get; set; } public string Name { get; set; } [Association("Policy_Location", "Id", "PolicyId")] public List Locations { get; set; } } public class Location { [Key] public int Id { get; set; } public string Name { get; set; } public int PolicyId { get; set; } [Association("Policy_Location", "PolicyId", "Id", IsForeignKey = true)] public Policy Policy { get; set; } [Association("Location_Property", "Id", "LocationId")] public Property Property { get; set; } } public class Property { [Key] public int Id { get; set; } public string Name { get; set; } public int LocationId { get; set; } [Association("Location_Property", "LocationId", "Id", IsForeignKey=true)] public Location Location { get; set; } }