RIA服务:插入多个表示模型对象

时间:2010-01-30 00:22:02

标签: wcf-ria-services presentation-model

我使用基于LINQ to SQL类的表示模型通过RIA服务共享数据。在Silverlight客户端上,我创建了几个新实体(专辑和艺术家),将它们相互关联(通过将专辑添加到艺术家的专辑集合,或者在专辑中设置Artist属性 - 任一个都有效),添加他们在上下文中,并提交了更改。

在服务器上,我得到两个单独的插入调用 - 一个用于专辑,一个用于艺术家。这些权限是新的,因此它们的ID值都设置为默认的int值(0 - 请记住,根据我的数据库,这可能是数据库中的有效ID),因为据我所知你没有设置ID对于客户端上的新实体。如果我通过我的RIA服务将LINQ转移到SQL类,这一切都可以正常工作,因为即使Album插入包括Artist和Artist插入包括Album,两者都是实体,L2S上下文识别它们。但是,使用我的自定义表示模型对象,我需要将它们转换回LINQ to SQL类维护进程中的关联,以便将它们添加到L2S上下文中。

简单地说,就我所知,这是不可能的。每个实体都有自己的Insert调用,但是你无法插入一个实体,因为没有ID会丢失关联。如果数据库使用GUID标识符,那将是一个不同的故事,因为我可以在客户端上设置它们。

这是可能的,还是我应该追求另一种设计?

1 个答案:

答案 0 :(得分:3)

如果您创建了正确的父子关联,您只需跟踪插入的演示模型(PM) - 实体关系:

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调用DomainService上的InsertChild方法。

Silverlight的:

...
public Child NewChild(Parent parent)
{
    return new Child
                {
                    ParentID = parent.ParentID,
                    Parent = parent,
                };
}
...
public void SubmitChanges()
{
    DomainContext.SubmitChanges(SaveComplete, null);
}
...

如果Parent不是新的,它将具有ParentID。如果是新的,则父ID将为空。通过将Child.Parent设置为新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'存储没有ID集的新插入实体之间的关系。由于您在事务中执行此操作并且单次调用DB,因此仅在插入所有实体后才设置ID。通过存储关系,Child PM可以使用数据库找到父PM的实体版本。 Child实体被添加到Parent实体的Children集合中,LINQToSQL或LINQToEnityFramework应该为您处理外键。

第二部分是在提交事务后关联更改。在提交父级和子级的方案中,您必须记住在子级上设置ParentID外键。

来自ChangeSet.Associate()的我的信息来自:http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx