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

发布于 2024-08-19 20:16:41 字数 599 浏览 13 评论 0 原文

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

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

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

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

I'm sharing data via RIA services using a presentation model on top of LINQ to SQL classes. On the Silverlight client, I created a couple of new entities (album and artist), associated them with each other (by either adding the album to the artist's album collection, or setting the Artist property on the album - either one works), added them to the context, and submitted changes.

On the server, I get two separate Insert calls - one for the album and one for the artist. These entitites are new so their ID values are both set to the default int value (0 - keep in mind that depending on my DB, this could be a valid ID in the DB) because as far as I know you don't set IDs for new entities on the client. This all would work fine if I was transferring the LINQ to SQL classes via my RIA services, because even though the Album insert includes the Artist and the Artist insert includes the Album, both are Entities and the L2S context recognizes them. However, with my custom presentation model objects, I need to convert them back to the LINQ to SQL classes maintaining the associations in the process so they can be added to the L2S context.

Put simply, as far as I can tell, this is impossible. Each entity gets its own Insert call, but there's no way you can just insert the one entity because without IDs the associations are lost. If the database used GUID identifiers it would be a different story because I could set those on the client.

Is this possible, or should I be pursuing another design?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

听不够的曲调 2024-08-26 20:16:41

如果创建正确的父子关联,则只需跟踪插入的表示模型 (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。如果是新的,Parent 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 的新插入实体之间的关系。由于您是在事务和对数据库的单次调用中执行此操作,因此只有在插入所有实体后才会设置 ID。通过存储关系,子 PM 可以使用数据库找到父 PM 的实体版本。子实体将添加到父实体的子集合中,并且 LINQToSQL 或 LINQToEnityFramework 应为您处理外键。

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

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

If you create the correct parent-child associations, you'll just need to track the inserted presentation model(PM)-entity relationships:

PM's:

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; }
}

Be sure to use [Composition] to force WCF RIA to call the InsertChild method on the DomainService.

Silverlight:

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

If the Parent is not new, it will have a ParentID. If it is new, the Parent ID will be null. By setting the Child.Parent to the reference of the new Parent, RIA understands what you are trying to do preserves the reference after it has been sent to the server.

DomainService on the server:

[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;
    }
}

The two important pieces here. First, the '_insertedObjectMap' stores the relationship between newly inserted entities that do not have the ID set. Since you are doing this in a transaction and single call to the DB, the ID will only be set after all entities have been inserted. By storing the relationship, the Child PM can find the entity version of the Parent PM using the database. The Child entity is added to the Children collection on the Parent entity and LINQToSQL or LINQToEnityFramework should handle the foreign key for you.

The second piece is associating the changes after the transaction is committed. In the scenario where the Parent and Child are both submitted, you must remember to set the ParentID foreign key on the Child.

My info from the ChangeSet.Associate() came from: http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文