2

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

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

简而言之,据我所知,这是不可能的。每个实体都有自己的 Insert 调用,但您不能只插入一个实体,因为没有 ID,关联就会丢失。如果数据库使用 GUID 标识符,那将是另一回事,因为我可以在客户端上设置这些标识符。

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

4

1 回答 1

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

银光:

...
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 了解您尝试执行的操作,从而在将引用发送到服务器后保留该引用。

服务器上的域服务:

[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 的实体之间的关系。由于您是在事务和对数据库的单次调用中执行此操作,因此只有在插入所有实体后才会设置 ID。通过存储关系,Child PM可以使用数据库找到Parent PM的实体版本。Child 实体被添加到 Parent 实体上的 Children 集合中,并且 LINQToSQL 或 LINQToEnityFramework 应该为您处理外键。

第二部分是在事务提交后关联更改。在Parent和Child都提交的场景下,一定要记得在Child上设置ParentID外键。

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

于 2010-05-13T22:26:35.823 回答