Entity Binding in Winforms - one to many - winforms

I'm struggling with the Entity Framework when creating a new record in a one to many relation. I've the following tables defined:
Person
PersonInternetAddresses
InternetAddresses
The Entity Framework has created the EntityObjects Person with a navigation property named PersonInternetAddresses and InternetAddresses with a navigation property to persons.
To create a new InternetAddresses record i've created a form with a couple of text fields and a bindingsource to InternetAddresses.
Under my "new" button i've placed the following code:
internetAddressesBindingSource.AddNew();
(internetAddressesBindingSource.Current as InternetAddresses).Id = Guid.NewGuid();
(internetAddressesBindingSource.Current as InternetAddresses).Persons = InternetAddresses.Persons;
On the last line i'm getting the following error:
The EntityCollection has already been initialized. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.
I'm guessing that i cannot set the person through the navigation property of the InternetAddresses which would mean that the Entity Framework itself would create a PersonInternetAddresses record. Since there isn't a PersonsInternetAddresses EntityObject in the model i'm pretty stuck!

Found it!
Persons is of type EntityCollection duh!
For testing purposes i've changed the code to:
internetAddressesBindingSource.AddNew();
(internetAddressesBindingSource.Current as InternetAddresses).Id = Guid.NewGuid();
foreach(Person p in InternetAddresses.Persons)
{
(internetAddressesBindingSource.Current as InternetAddresses).Persons.Add(p);
}

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

Entity Framework 5 Table Relationship

I use database first. In the auto-generated EF5 code:
Account has ClientID(FK)
Client has AddressID(FK)
Address has public List<EFClient> Clients { get; set; } (i did not specify this in DB but ef5 auto generated it)
When I serialize Address object, it throws exception "there is a circular reference" because the client collection in the address also cotains same address again
What can I do in this situation?
How can I not let EF5 auto-generate that Clients collection?
Thanks in advance!
I don't think you actually want to stop EF from auto-generating this navigation property, as it will affect lots of places. What you can do is simply remove the property that it generated from the model, which will remove the property from the class.

XmlSerializer stopped working after updates

I'm using XmlSerializer. I've had no problems with it until now. I updated Silverlight from 4 to 5 and at the same time also updated the WCF RIA Services from v1 SP1 to v1 SP2. Now the following line gives me an error.
XmlSerializer s = new XmlSerializer(typeof(MyCustomObject));
The error is:
System.InvalidOperationException: System.ServiceModel.DomainServices.Client.EntityConflict cannot be serialized because it does not have a parameterless constructor.
The object I'm using (MyCustomObject in the sample) has not changed in any way so I'm starting to think it's either SL5 or the new RIA Services that is breaking my code. I didn't find any breaking changes document or mentions that this could happen. I don't know why it has a problem with EntityConflict since I'm not using any entities within my object.
Has anyone seen an error like this and/or know how to solve it?
UPDATE!
The final property that the error message says before EntityConflict is an Entity. I think that makes a difference but it has been working before. I'd also like to know why the serializer already tries to serialize the object in the constructor?
public static XmlSerializer GetEntityXmlSerializer<TEntity>()
where TEntity : Entity
{
XmlAttributes ignoreAttribute = new XmlAttributes()
{
XmlIgnore = true,
};
// use base class of Entity,
// if you use type of implementation
// you will get the error.
Type entityType = typeof(Entity);
var xmlAttributeOverrides = new XmlAttributeOverrides();
xmlAttributeOverrides.Add(entityType, "EntityConflict", ignoreAttribute);
xmlAttributeOverrides.Add(entityType, "EntityState", ignoreAttribute);
return new XmlSerializer(typeof(TEntity), xmlAttributeOverrides);
}
I am not sure why this would be happening, RIA Services entities are not XmlSerializable objects and the entities themselves are not decorated with the [Serializable] attribute. Have you added partial classes on the client side which decorate the entities with [Serializable] or modified the code generation in some way?
I got around this problem by using intermediary serializable POCO objects which were copies of my custom objects (which were inherited from Entity). The POCO objects did not inherit from Entity. I just updated their values from the original Entity objects. They then serialized quite nicely. Of course, when you de-serialize you need to update your Entity objects from the POCO objects.

How to get can CanAddNew to be true for a collection returned by RIA Services

RIA Services is returning a list of Entities that won't allow me to add new items. Here are what I believe to be the pertinent details:
I'm using the released versions of Silverlight 4 and RIA Services 1.0 from mid-April of 2010.
I have a DomainService with a query method that returns List<ParentObject>.
ParentObject includes a property called "Children" that is defined as List<ChildObject>.
In the DomainService I have defined CRUD methods for ParentObject with appropriate attributes for the Query, Delete, Insert, and Update functions.
The ParentObject class has an Id property marked with the [Key] attribute. It also has the "Children" property marked with the attributes [Include], [Composition], and [Association("Parent_Child", "Id",
"ParentId")].
The ChildObject class has an Id marked with the [Key] attribute as well as a foreign key, "ParentId", that contains the Id of the parent.
On the client side, data is successfully returned and I assign the results of the query to a PagedCollectionView like this:
_pagedCollectionView = new PagedCollectionView(loadOperation.Entities);
When I try to add a new ParentObject to the PagedCollectionView like this:
ParentObject newParentObject = (ParentObject)_pagedCollectionView.AddNew();
I get the following error:
" 'Add New' is not allowed for this view."
On further investigation, I found that _pagedCollectionView.CanAddNew is "false" and cannot be changed because the property is read-only.
I need to be able to add and edit ParentObjects (with their related children, of course) to the PagedCollectionView. What do I need to do?
I was just playing around with a solution yesterday and feel pretty good about how it works. The reason you can't add is the source collection (op.Entities) is read-only. However, even if you could add to the collection, you'd still want to be adding to the EntitySet as well. I created a intermediate collection that takes care of both these things for me.
public class EntityList<T> : ObservableCollection<T> where T : Entity
{
private EntitySet<T> _entitySet;
public EntityList(IEnumerable<T> source, EntitySet<T> entitySet)
: base(source)
{
if (entitySet == null)
{
throw new ArgumentNullException("entitySet");
}
this._entitySet = entitySet;
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (!this._entitySet.Contains(item))
{
this._entitySet.Add(item);
}
}
protected override void RemoveItem(int index)
{
T item = this[index];
base.RemoveItem(index);
if (this._entitySet.Contains(item))
{
this._entitySet.Remove(item);
}
}
}
Then, I use it in code like this.
dataGrid.ItemsSource = new EntityList<Entity1>(op.Entities, context.Entity1s);
The only caveat is this collection does not actively update off the EntitySet. If you were binding to op.Entities, though, I assume that's what you'd expect.
[Edit]
A second caveat is this type is designed for binding. For full use of the available List operation (Clear, etc), you'd need to override a few of the other methods to write-though as well.
I'm planning to put together a post that explains this a little more in-depth, but for now, I hope this is enough.
Kyle
Here's a workaround which I am using:
Instead of using the AddNew, on your DomainContext you can retrieve an EntitySet<T> by saying Context.EntityNamePlural (ie: Context.Users = EntitySet<User> )
You can add a new entity to that EntitySet by calling Add() and then Context.SubmitChanges() to send it to the DB. To reflect the changes on the client you will need to Reload (Context.Load())
I just made this work about 15mins ago after having no luck with the PCV so I am sure it could be made to work better, but hopefully this will get you moving forward.
For my particular situation, I believe the best fit is this (Your Mileage May Vary):
Use a PagedCollectionView (PCV) as a wrapper around the context.EntityNamePlural (in my case, context.ParentObjects) which is an EntitySet. (Using loadOperation.Entities doesn't work for me because it is always read-only.)
_pagedCollectionView = new PagedCollectionView(context.ParentObjects);
Then bind to the PCV, but perform add/delete directly against the context.EntityNamePlural EntitySet. The PCV automatically syncs to the changes done to the underlying EntitySet so this approach means I don't need to worry about sync issues.
context.ParentObjects.Add();
(The reason for performing add/delete directly against the EntitySet instead of using the PCV is that PCV's implementation of IEditableCollectionView is incompatible with EntitySet causing IEditableCollectionView.CanAddNew to be "false" even though the underlying EntitySet supports this function.)
I think Kyle McClellan's approach (see his answer) may be preferred by some because it encapsulates the changes to the EntitySet, but I found that for my purposes it was unneccessary to add the ObservableCollection wrapper around loadOperation.Entities.
Many thanks to to Dallas Kinzel for his tips along the way!

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