RIA 服务:插入多个表示模型对象 [英] RIA Services: Inserting multiple presentation-model objects
问题描述
我使用基于 LINQ to SQL 类的演示模型通过 RIA 服务共享数据.在 Silverlight 客户端上,我创建了几个新实体(专辑和艺术家),将它们相互关联(通过将专辑添加到艺术家的专辑收藏,或在专辑上设置 Artist 属性 - 任一作品),添加将它们添加到上下文中,并提交更改.
在服务器上,我收到两个单独的 Insert 调用 - 一个用于专辑,另一个用于艺术家.这些实体是新的,因此它们的 ID 值都设置为默认的 int 值(0 - 请记住,这取决于我的数据库,这可能是数据库中的有效 ID),因为据我所知,您没有设置 ID对于客户端上的新实体.如果我通过我的 RIA 服务将 LINQ 转移到 SQL 类,这一切都会正常工作,因为即使专辑插入包含艺术家,艺术家插入包含专辑,两者都是实体并且 L2S 上下文识别它们.但是,对于我的自定义表示模型对象,我需要将它们转换回 LINQ to SQL 类维护流程中的关联,以便将它们添加到 L2S 上下文中.
简而言之,据我所知,这是不可能的.每个实体都有自己的 Insert 调用,但您无法只插入一个实体,因为没有 ID,关联就会丢失.如果数据库使用 GUID 标识符,那就是另一回事了,因为我可以在客户端设置这些标识符.
这是可能的,还是我应该追求另一种设计?
如果您创建了正确的父子关联,您只需要跟踪插入的表示模型(PM)-实体关系:
下午:
公共类父级{[钥匙]公众号?父 ID { 获取;放;}[包括][作品][关联(Parent_1-*_Child",ParentID",ParentID",IsForeignKey = false)]公共 IEnumerable儿童{得到;放;}}公开课儿童{[钥匙]公众号?ChildID { 得到;放;}[包括][关联(Parent_1-*_Child",ParentID",ParentID",IsForeignKey = true)]公共父父{获取;放;}}
务必使用 [Composition] 强制 WCF RIA 调用 DomainService 上的 InsertChild 方法.
银光:
<代码>...public Child NewChild(Parent parent){返回新的孩子{ParentID = parent.ParentID,父母 = 父母,};}...public void SubmitChanges(){DomainContext.SubmitChanges(SaveComplete, null);}...
如果 Parent 不是新的,它将有一个 ParentID.如果是新的,则父 ID 将为空.通过将 Child.Parent 设置为新父级的引用,RIA 了解您正在尝试执行的操作,并在将引用发送到服务器后保留该引用.
服务器上的域服务:
[EnableClientAccess]公共类 FamilyDomainService : DomainService{私有只读 IDictionary
这里有两个重要的部分.首先,'_insertedObjectMap' 存储没有设置 ID 的新插入实体之间的关系.由于您是在事务中执行此操作并且对数据库进行单次调用,因此只有在插入所有实体后才会设置 ID.通过存储关系,子 PM 可以使用数据库找到父 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>
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?
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
这篇关于RIA 服务:插入多个表示模型对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!