2010-01-30 8 views
2

Je partage des données via des services RIA en utilisant un modèle de présentation au-dessus des classes LINQ to SQL. Sur le client Silverlight, j'ai créé quelques nouvelles entités (album et artiste), les ai associées les unes aux autres (soit en ajoutant l'album à la collection d'albums de l'artiste, soit en définissant la propriété Artist sur l'album). les au contexte, et soumis des changements.Services RIA: Insertion de plusieurs objets de modèle de présentation

Sur le serveur, j'obtiens deux appels Insert distincts - un pour l'album et un pour l'artiste. Ces entités sont nouvelles et leurs valeurs d'ID sont toutes deux définies sur la valeur int par défaut (0 - gardez à l'esprit que selon ma base de données, il pourrait s'agir d'un ID valide dans la base de données) car vous ne définissez pas d'ID pour les nouvelles entités sur le client. Tout irait bien si je transférais les classes LINQ to SQL via mes services RIA, car même si l'insertion de l'album inclut l'artiste et l'artiste, l'insertion inclut l'album, les deux sont des entités et le contexte L2S les reconnaît. Cependant, avec mes objets de modèle de présentation personnalisés, je dois les reconvertir aux classes LINQ to SQL en maintenant les associations dans le processus afin qu'elles puissent être ajoutées au contexte L2S. Pour simplifier, autant que je sache, c'est impossible. Chaque entité reçoit son propre appel Insert, mais vous ne pouvez pas insérer une seule entité car sans ID, les associations sont perdues. Si la base de données utilisait des identifiants GUID, ce serait une histoire différente car je pourrais les définir sur le client. Est-ce possible ou devrais-je poursuivre un autre design?

Est-ce possible?

Répondre

3

Si vous créez les associations de parents et enfants corrects, vous aurez juste besoin de suivre le modèle de présentation inséré (PM) des relations -entity:

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

Assurez-vous d'utiliser [ Composition] pour forcer WCF RIA à appeler la méthode InsertChild sur le DomainService.

Silverlight:

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

Si le parent n'est pas nouveau, il aura une ParentID. S'il est nouveau, l'ID du parent sera nul. En définissant Child.Parent sur la référence du nouveau parent, RIA comprend ce que vous essayez de faire conserve la référence après qu'elle a été envoyée au serveur.

DomainService sur le serveur:

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

Les deux pièces importantes ici. Premièrement, le '_insertedObjectMap' stocke la relation entre les entités nouvellement insérées qui n'ont pas l'ensemble d'ID. Comme vous le faites dans une transaction et un appel unique à la base de données, l'ID ne sera défini qu'après que toutes les entités ont été insérées. En stockant la relation, le MP enfant peut trouver la version de l'entité du PM Parent en utilisant la base de données. L'entité enfant est ajoutée à la collection Children sur l'entité parent et LINQToSQL ou LINQToEnityFramework doit gérer la clé étrangère pour vous.

La deuxième pièce associe les modifications après l'engagement de la transaction. Dans le scénario où le parent et l'enfant sont tous deux soumis, vous devez vous rappeler de définir la clé étrangère ParentID sur l'enfant.

Mes infos du ChangeSet.Associate() est provenu http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx

+0

Ceci est la réponse à ce que je peux dire, donc je vous remercie de l'affichage.Je pense que je suis tombé sur quelque chose de similaire quand je travaillais là-dessus, mais je crois que le problème était qu'en réalité je travaillais avec un graphe d'objet plus compliqué qu'une simple relation parent/enfant, et je ne pense pas que cela Le type de solution évoluera pour faire face à ce genre de complexité. – nlawalker