Well, i ran into an issue while inserting multiple records which is nicely described in my post on a forum:
http://forums.silverlight.net/forums/t/222783.aspx
Long story short, ria services doesn’t take care of the ordering of the items when calling insert methods for entities, ie it could easily call insert on child before parent. This problem doesn’t show itself when using Composition attribute, but it’s not like you want to use that attribute every time, so if you’re not using you’re pretty much leaving yourself at the mercy of your ORM. And Nhibernate is pretty crude sometimes in some respects, one of them being the data insertion.
If you don’t explicitly establish 2-way connection, NHibernate is just going to insert the data in the order you persisted it. Which in combination with RIA Service’s indifference is BAD.
I won’t give any sample project because it’s a bit difficult to extract plus i don’t have time, but this is the basic principle.
I decided to make a workaround on the Service level. What i decided to do is make an override on domainservice’s Submit method.
public override bool Submit(ChangeSet changeSet) { InsertList = changeSet.ChangeSetEntries .Where(x => x.Operation == DomainOperation.Insert && x.Entity is BaseEditModel) .Select(y => new InsertEntry(false, y)).ToList(); foreach (var entry in InsertList) { if (!entry.Inserted) { PersistEntry(entry, InsertList); } } return base.Submit(changeSet); }
BaseEditModel is a base class i use for all the Presentation models, you should use your own base clas, the point is that you just take entites which are getting inserted.
InsertEntry class is pretty basic:
private class InsertEntry { public InsertEntry(bool inserted, ChangeSetEntry entry) { Inserted = inserted; Entry = entry; } public ChangeSetEntry Entry { get; set; } public bool Inserted { get; set; } }
It’s used basically for keeping track of already persisted entities.
The meat of the solution is in the PersistEntry method:
private void PersistEntry(InsertEntry insEntry, IEnumerable<InsertEntry> insertList) { if (insEntry.Inserted) return; var entPm = (BaseEditModel)insEntry.Entry.Entity; if (entPm == null) return; if (insEntry.Entry.Associations != null) //iterate through associations of the entry foreach (var ass in insEntry.Entry.Associations) { //test if the association is a foreign key association if (IsForeignKey(entPm.GetType(), ass.Key)) { //find the entity from association in the insertList var entryTup = insertList.FirstOrDefault(x => x.Entry.Id == ass.Value[0]); //if it's found and it's not already inserted, insert it if (entryTup != null && !entryTup.Inserted) //the insertion is done recursively PersistEntry(entryTup, insertList); } } //since i'm using the presentation model, i first have to find the corresponding domain entity model var entType = GetEntType(entPm); var ent = (BaseEntity)Activator.CreateInstance(entType); ent.Guid = entPm.Guid; //sets the entity properties from presentation model entPm.SetEntity(ent, this); //and finally persist it Repository.Persist(ent); //set the inserted property to true so that the same entity doesn't get inserted 2 times insEntry.Inserted = true; }
The IsForeignKey method basically tests the association of a property for foreignkey bool value
private static bool IsForeignKey(Type entPmType, string propName) { var propInfo = entPmType.GetProperties().FirstOrDefault(x => x.Name == propName); if (propInfo == null) return false; var assAtt = propInfo.GetCustomAttributes(typeof(AssociationAttribute), false)[0] as AssociationAttribute; return assAtt != null && assAtt.IsForeignKey; }
I documented the code the best i could, so i hope you get the picture. The only thing left to do is to modify the InsertEntity methods so that it doesn’t persist anything, because we already persisted it.
protected void InsertEntity(TPm entityPm) { var entity = Repository.GetItem(entityPm.Guid); ChangeSet.Associate(entityPm, entity, UpdateEntityPmAttributes); }
So the previous method is used mostly to do the Association, we already did the persistion stuff in the submit method.
And that’s basically it, we did the insertion in the order which respects associations, and therefore you don’t get those nasty foreign key sql exceptions. Hope it’s well explained and you’ll find the way to use it on your example. It can easily be used on Entity domain models too.
Hope this helps!
Leave a Comment