2010-06-11 4 views
1

Salut à tous. Je me rends compte que c'est une question assez longue, mais j'apprécierais vraiment l'aide de toute personne expérimentée avec les services RIA. Merci!Silverlight 4 + WCF RIA - Meilleures pratiques de conception de services de données

Je travaille sur une application Silverlight 4 qui affiche les données du serveur. Je suis relativement inexpérimenté avec RIA Services, donc j'ai travaillé sur les tâches d'obtenir les données dont j'ai besoin pour le client, mais chaque nouvelle pièce que j'ajoute au puzzle semble être de plus en plus problématique. J'ai l'impression de manquer certains concepts de base ici, et il me semble que je ne fais que «pirater» des morceaux de manière chronophage, chacun cassant les précédents en essayant de les ajouter. J'aimerais avoir la rétroaction des développeurs expérimentés avec les services RIA, pour trouver la façon de faire ce que je veux faire. Permettez-moi d'exposer ce que je suis en train de faire:

D'abord, les données. La source de ces données est une variété de sources, principalement créées par une bibliothèque partagée qui lit les données de notre base de données, et les expose comme des POCO (Plain Old CLR Objects). Je crée mes propres POCO pour représenter les différents types de données que je dois passer entre le serveur et le client.

DataA - Cette application est pour visualiser un certain type de données, appelez DataA, en temps quasi-réel. Toutes les 3 minutes, le client doit extraire des données du serveur, de toutes les nouvelles DataA depuis la dernière fois qu'il a demandé des données.

DataB - Les utilisateurs peuvent afficher les objets DataA dans l'application et en sélectionner un dans la liste, qui affiche des détails supplémentaires sur cette DataA. J'apporte ces détails supplémentaires du serveur en tant que DataB.

DataC - L'une des choses que DataB contient est l'historique de quelques valeurs importantes dans le temps. J'appelle chaque point de données de cet historique un objet DataC et chaque objet DataB contient plusieurs DataC.

Le modèle de données - Du côté du serveur, j'ai un seul DomainService:

[EnableClientAccess] 
public class MyDomainService : DomainService 
{ 
    public IEnumerable<DataA> GetDataA(DateTime? startDate) 
    { 
     /*Pieces together the DataAs that have been created 
     since startDate, and returns them*/ 
    } 

    public DataB GetDataB(int dataAID) 
    { 
     /*Looks up the extended info for that dataAID, 
     constructs a new DataB with that DataA's data, 
     plus the extended info (with multiple DataCs in a 
     List<DataC> property on the DataB), and returns it*/ 
    } 

    //Not exactly sure why these are here, but I think it 
    //wouldn't compile without them for some reason? The data 
    //is entirely read-only, so I don't need to update. 
    public void UpdateDataA(DataA dataA) 
    { 
     throw new NotSupportedException(); 
    } 
    public void UpdateDataB(DataB dataB) 
    { 
     throw new NotSupportedException(); 
    } 
} 

Les classes pour DATAA/B/C se présentent comme suit:

[KnownType(typeof(DataB))] 
public partial class DataA 
{ 
    [Key] 
    [DataMember] 
    public int DataAID { get; set; } 
    [DataMember] 
    public decimal MyDecimalA { get; set; } 
    [DataMember] 
    public string MyStringA { get; set; } 
    [DataMember] 
    public DataTime MyDateTimeA { get; set; } 
} 

public partial class DataB : DataA 
{ 
    [Key] 
    [DataMember] 
    public int DataAID { get; set; } 
    [DataMember] 
    public decimal MyDecimalB { get; set; } 
    [DataMember] 
    public string MyStringB { get; set; } 
    [Include] //I don't know which of these, if any, I need? 
    [Composition] 
    [Association("DataAToC","DataAID","DataAID")] 
    public List<DataC> DataCs { get; set; } 
} 

public partial class DataC 
{ 
    [Key] 
    [DataMember] 
    public int DataAID { get; set; } 
    [Key] 
    [DataMember] 
    public DateTime Timestamp { get; set; } 
    [DataMember] 
    public decimal MyHistoricDecimal { get; set; } 
} 

Je suppose que grande question que j'ai ici est ... Devrais-je utiliser Entities au lieu de POCOs? Mes classes sont-elles construites correctement pour pouvoir transmettre les données correctement? Dois-je utiliser des méthodes Invoke au lieu de méthodes Query (Get) sur le DomainService?

Du côté client, j'ai un certain nombre de problèmes. Étonnamment, un de mes plus gros a été threading. Je ne m'attendais pas à ce qu'il y ait autant de problèmes de threading avec MyDomainContext. Ce que j'ai appris est que vous semblez être capable de créer MyDomainContextObjects sur le thread d'interface utilisateur, toutes les requêtes que vous pouvez faire sont effectuées de manière asynchrone seulement, et que si vous essayez de le faire de manière synchrone en bloquant le thread appelant jusqu'à ce que LoadOperation se termine, vous devez le faire sur un thread d'arrière-plan, car il utilise le thread d'interface utilisateur pour effectuer la requête. Alors, voici ce que j'ai jusqu'ici. L'application devrait afficher un flux des objets DataA, en étalant chaque morceau de 3min au cours des 3 prochaines minutes (de sorte qu'ils finissent affichés 3min après l'événement, ressemblant à un flux continu, mais ne doivent être téléchargés en 3min éclats). Pour ce faire, le formulaire principal initialise, crée un MyDomainContext privé et démarre un worker d'arrière-plan, qui boucle continuellement dans un certain temps (true). Sur chaque boucle, il vérifie s'il reste des données DataAs à afficher. Si tel est le cas, il affiche Data et Thread.Sleep() jusqu'à ce que le prochain DataA soit programmé pour être affiché.S'il est hors de données, des requêtes pour plus, en utilisant les méthodes suivantes:

public DataA[] GetDataAs(DateTime? startDate) 
{ 
    _loadOperationGetDataACompletion = new AutoResetEvent(false); 
    LoadOperation<DataA> loadOperationGetDataA = null; 

    loadOperationGetDataA = 
     _context.Load(_context.GetDataAQuery(startDate), 
     System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false); 
    loadOperationGetDataA.Completed += new 
     EventHandler(loadOperationGetDataA_Completed); 

    _loadOperationGetDataACompletion.WaitOne(); 
    List<DataA> dataAs = new List<DataA>(); 
    foreach (var dataA in loadOperationGetDataA.Entities) 
     dataAs.Add(dataA); 

    return dataAs.ToArray(); 
} 

private static AutoResetEvent _loadOperationGetDataACompletion; 
private static void loadOperationGetDataA_Completed(object sender, EventArgs e) 
{ 
    _loadOperationGetDataACompletion.Set(); 
} 

Semble sorte de maladroit d'essayer de le forcer à être synchrone, mais ce qui est déjà sur un fil de fond, je pense que c'est OK ? Jusqu'à présent, tout fonctionne réellement, autant d'un bidouillage que cela puisse paraître. Il est important de noter que si j'essaie d'exécuter ce code sur le thread de l'interface utilisateur, il se verrouille, car il attend le WaitOne() à jamais, verrouillant le thread, de sorte qu'il ne peut pas charger le serveur. Ainsi, une fois les données affichées, les utilisateurs peuvent cliquer sur l'un d'eux pour remplir un volet de détails avec les données DataB complètes concernant cet objet. Pour ce faire, j'ai le contrôle utilisateur du panneau de détails souscrit à un événement de sélection que j'ai configuré, qui se déclenche lorsque la sélection change (sur le thread de l'interface utilisateur). J'utilise une technique similaire là-bas, pour obtenir l'objet Datab:

void SelectionService_SelectedDataAChanged(object sender, EventArgs e) 
{ 
    DataA dataA = /*Get the selected DataA*/; 

    MyDomainContext context = new MyDomainContext(); 
    var loadOperationGetDataB = 
     context.Load(context.GetDataBQuery(dataA.DataAID), 
     System.ServiceModel.DomainServices.Client.LoadBehavior.RefreshCurrent, false); 
    loadOperationGetDataB.Completed += new 
     EventHandler(loadOperationGetDataB_Completed);   
} 

private void loadOperationGetDataB_Completed(object sender, EventArgs e) 
{ 
    this.DataContext = 
     ((LoadOperation<DataB>)sender).Entities.SingleOrDefault(); 
} 

Encore une fois, il semble un peu hacky, mais il fonctionne ... sauf sur le Datab qu'il charge, la liste des DataCs est vide. J'ai essayé toutes sortes de choses là-bas, et je ne vois pas ce que je fais de mal pour permettre aux DataC de descendre avec le DataB. Je suis sur le point de faire une troisième requête pour les DataCs, mais cela me crie encore plus de hackiness. J'ai vraiment l'impression que je me bats contre le grain ici, comme si je le faisais d'une manière tout à fait involontaire. Si quelqu'un pouvait offrir de l'aide et souligner ce que je fais de mal ici, j'apprécierais beaucoup!

Merci!

Répondre

0

Je dois dire que cela semble un peu trop complexe. Si vous utilisez le framework d'entité (qui avec la version 4 peut générer des POCO si vous avez besoin) et Ria/LINQ Vous pouvez faire tout cela implicitement en utilisant le chargement différé, les instructions 'expand' et l'héritage table par type.

Questions connexes