Если вы создаете правильные родительско-дочерние ассоциации, вам просто нужно отследить отношения вставленной модели представления (PM) и объекта:
РМ:
public class Parent
{
[Key]
public int? ParentID { get; set; }
[Include]
[Composition]
[Association("Parent_1-*_Child", "ParentID", "ParentID", IsForeignKey = false)]
public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int? ChildID { get; set; }
[Include]
[Association("Parent_1-*_Child", "ParentID", "ParentID", IsForeignKey = true)]
public Parent Parent { get; set; }
}
Обязательно используйте [Composition], чтобы заставить WCF RIA вызывать метод InsertChild на DomainService.
Silverlight:
...
public Child NewChild(Parent parent)
{
return new Child
{
ParentID = parent.ParentID,
Parent = parent,
};
}
...
public void SubmitChanges()
{
DomainContext.SubmitChanges(SaveComplete, null);
}
...
Если Parent не новый, у него будет ParentID. Если он новый, идентификатор родителя будет нулевым. Установив для Child.Parent ссылку на новый родительский объект, RIA понимает, что вы пытаетесь сделать, сохраняет ссылку после ее отправки на сервер.
DomainService на сервере:
[EnableClientAccess]
public class FamilyDomainService : DomainService
{
private readonly IDictionary<object, EntityObject> _insertedObjectMap;
public void InsertParent(Parent parent)
{
ParentEntity parentEntity = new ParentEntity();
ObjectContext.AddToParents(parentEntity);
_insertedObjectMap[parent] = parentEntity;
ChangeSet.Associate(parent, parentEntity, (p, e) => p.ParentID = e.ParentID;
}
public void InsertChild(Child child)
{
var childEntity = new ChildEntity();
if (child.ParentID.HasValue) // Used when the Parent already exists, but the Child is new
{
childEntity.ParentID = child.ParentID.GetValueOrDefault();
ObjectContext.AddToChildren(childEntity);
}
else // Used when the Parent and Child are inserted on the same request
{
ParentEntity parentEntity;
if (child.Parent != null && _insertedObjectMap.TryGetValue(child.Parent, out parentEntity))
{
parentEntity.Children.Add(childEntity);
ChangeSet.Associate(child, childEntity, (c, e) => c.ParentID = e.Parent.ParentID);
}
else
{
throw new Exception("Unable to insert Child: ParentID is null and the parent Parent cannot be found");
}
}
_insertedObjectMap[child] = childEntity;
ChangeSet.Associate(child, childEntity, (c, e) => c.ChildID = e.ChildID );
}
protected override bool PersistChangeSet()
{
ObjectContext.SaveChanges();
_insertedObjectMap.Clear();
return true;
}
}
Две важные части здесь. Во-первых, _insertedObjectMap хранит отношения между вновь вставленными объектами, для которых не установлен идентификатор. Поскольку вы делаете это в транзакции и в одном обращении к БД, идентификатор будет установлен только после того, как все объекты будут вставлены. Сохраняя взаимосвязь, дочерний PM может найти версию сущности родительского PM, используя базу данных. Дочерний объект добавляется в коллекцию Children на родительском объекте, и LINQToSQL или LINQToEnityFramework должны обрабатывать внешний ключ для вас.
Вторая часть связывает изменения после совершения транзакции. В сценарии, где отправляются оба родительских и дочерних элемента, вы должны не забыть установить внешний ключ ParentID для дочернего элемента.
Моя информация из ChangeSet.Associate () получена от: http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx