2010-01-30 1 views
2

Я делясь данными через службы RIA с использованием модели презентации поверх классов LINQ to SQL. На клиенте Silverlight я создал пару новых сущностей (альбом и исполнитель), связал их друг с другом (добавив альбом в коллекцию альбома исполнителя или установив свойство Artist на альбоме - либо один из них), добавлен их в контекст и внесенные изменения.Услуги RIA: вставка нескольких объектов презентационной модели

На сервере я получаю два отдельных вызова Insert - один для альбома и один для исполнителя. Эти полномочия новы, поэтому их значения ID установлены на значение по умолчанию int (0 - помните, что в зависимости от моего БД это может быть действительный идентификатор в БД), поскольку, насколько я знаю, вы не устанавливаете идентификаторы для новых объектов на клиенте. Все это будет нормально работать, если я передам классы LINQ to SQL через свои службы RIA, потому что хотя вложение альбома включает в себя вставку Artist и Artist, включает альбом, оба объекта, а контекст L2S распознает их. Однако с моими пользовательскими объектами модели представления мне нужно преобразовать их обратно в классы LINQ to SQL , поддерживая ассоциации в процессе, чтобы их можно было добавить в контекст L2S.

Проще говоря, насколько я могу судить, это невозможно. Каждый объект получает свой собственный вызов Insert, но вы не можете просто вставить один объект, потому что без идентификаторов ассоциации теряются. Если в базе данных использовались идентификаторы GUID, это была бы другая история, потому что я мог бы установить ее на клиенте.

Возможно ли это, или я должен преследовать другой дизайн?

ответ

3

Если вы создаете правильный родитель-ребенок ассоциацию, вам просто нужно отслеживать модель вставлено представление (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); 
} 
... 

Если Родитель не является новым, он будет иметь 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 с использованием базы данных. Объект Child добавляется в коллекцию Children на родительском объекте, а LINQToSQL или LINQToEnityFramework должны обрабатывать внешний ключ для вас.

Вторая часть связывает изменения после совершения транзакции. В сценарии, в котором представлены оба родителя и ребенка, вы должны помнить о том, что для дочернего элемента ParentID задан внешний ключ.

Моя информация от ChangeSet.Associate() приходилась: http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx

+0

Это ответ на вопрос, насколько я могу сказать, так что спасибо за сообщение.Я думаю, что наткнулся на что-то подобное, когда я работал над этим, но я считаю, что проблема в том, что на самом деле я работал с более сложным графиком объектов, чем просто с одним родительским/дочерним отношением, и я не думаю, что это решение такого масштаба будет масштабироваться, чтобы справиться с такой сложностью. – nlawalker