2010-05-30 11 views
0

J'essaie de comprendre comment effectuer une jointure mixte dans LINQ avec un accès spécifique à 2 objets LINQ. Voici un exemple de la façon dont la requête TSQL réelle pourrait ressembler:Aide avec LINQ: Jointures jointes et spécification de valeurs par défaut

SELECT 
    * 
FROM 
    [User] AS [a] 
INNER JOIN 
    [GroupUser] AS [b] 
ON 
    [a].[UserID] = [b].[UserID] 
INNER JOIN 
    [Group] AS [c] 
ON 
    [b].[GroupID] = [c].[GroupID] 
LEFT JOIN 
    [GroupEntries] AS [d] 
ON 
    [a].[GroupID] = [d].[GroupID] 
WHERE [a].[UserID] = @UserID 

A la fin, au fond ce que je voudrais est un objet dénombrable plein d'objets GroupEntry. Ce qui m'intéresse, ce sont les deux derniers tableaux/objets dans cette requête. Je vais afficher les groupes en tant qu'en-tête de groupe et toutes les entrées sous le titre de groupe. S'il n'y a pas d'entrées pour un groupe, je veux toujours voir ce groupe comme un en-tête sans aucune entrée. Voici ce que j'ai jusqu'à présent:

Donc, de ce que je voudrais faire une fonction:

public void DisplayEntriesByUser(int user_id) 
{ 
    MyDataContext db = new MyDataContext(); 

    IEnumberable<GroupEntries> entries = 
    (
     from user in db.Users 
     where user.UserID == user_id 
     join group_user in db.GroupUsers 
      on user.UserID = group_user.UserID 
     into a 

     from join1 in a 
     join group in db.Groups 
      on join1.GroupID equals group.GroupID 
     into b 

     from join2 in b 
     join entry in db.Entries.DefaultIfEmpty() 
      on join2.GroupID equals entry.GroupID 
     select entry 
    ); 


    Group last_group_id = 0; 
    foreach(GroupEntry entry in entries) 
    { 
     if (last_group_id == 0 || entry.GroupID != last_group_id) 
     { 
      last_group_id = entry.GroupID; 
      System.Console.WriteLine("---{0}---", entry.Group.GroupName.ToString().ToUpper()); 

     } 
     if (entry.EntryID) 
     { 
      System.Console.WriteLine(" {0}: {1}", entry.Title, entry.Text); 
     } 
    } 
} 

L'exemple ci-dessus ne fonctionne pas tout à fait comme prévu. Il y a 2 problèmes que je ne l'ai pas été en mesure de résoudre:

  1. Il me semble encore être obtenir un INNER JOIN au lieu d'un LEFT JOIN sur la dernière jointure. Je n'obtiens aucun résultat vide, donc les groupes sans entrées n'apparaissent pas.

  2. J'ai besoin de trouver un moyen pour que je puisse remplir les valeurs par défaut pour les ensembles d'entrées vierges. C'est-à-dire, s'il y a un groupe sans une entrée, je voudrais avoir une entrée principalement vide, sauf que je voudrais que l'EntryID soit nul ou 0, le GroupID soit celui du groupe vide qu'il représente , et j'ai besoin d'un handle sur l'objet entry.Group (c'est-à-dire c'est parent, objet Group vide).

Toute aide à ce sujet serait grandement appréciée.

Remarque: Les noms de tables et la représentation du monde réel ont été dérivés uniquement pour cet exemple, mais leurs relations simplifient ce que j'essaie de faire.

Répondre

0

Ce n'est pas testé, mais je pense qu'il est assez proche:

var groupEntries = 
    from 
     u in db.Users 
    where 
     user.Id == user_id 
    join 
     gu in db.GroupUsers 
     on u.UserId equals gu.UserId 
    join 
     g in db.Groups 
     on gu.GroupId equals g.GroupId 
    join 
     ge in db.GroupEntries 
     on u.GroupdId equals ge.GroupId 
     into ges 
    from 
     ge in ges.DefaultIfEmpty(new GroupEntry { EntryId = 0, GroupId = g.GroupId }) 
    select 
     ge; 

Je ne pense pas que vous devez utiliser into à moins que vous avez l'intention de faire un traitement ultérieur, comme DefaultIfEmpty(). Et notez que la deuxième surcharge de DefaultIfEmpty() vous permet d'entrer une valeur par défaut personnalisée. Ainsi, vous pouvez créer un nouvel objet GroupEntry et affecter les valeurs que vous voulez pour chaque propriété (ou laisser les propriétés vides).

+0

Merci beaucoup Dan. C'est une requête beaucoup plus simple. Je tombe dans des ennuis plus tard dans le code derrière. Ma fonction actuelle attend un type d'objet IEnumberable à travers lequel il peut itérer. À l'heure actuelle, dans ma boucle foreach, après la requête, j'obtiens l'erreur «Default de surcharge non utilisée pour l'opérateur de requête 'DefaultEmpty'». Sur une note relative à la curiosité, si vous utilisez le mot clé "into" dans linq, crée-t-il nécessairement une sous-requête à la couche db inférieure? –

+0

Corey, est-ce une requête LinqToSql? – devuxer

+0

Yessir. La base de données sous-jacente est un serveur MSSQL 2005. –

0
//set this to see all queries issued. 
myDC.Log = Console.Out; 

    //setup to load the GroupEntries property of each group 
DataLoadOptions o = new DataLoadOptions(); 
o.LoadWith<Group>(g => g.GroupEntries); 
myDC.LoadOptions = o; 

    //query to get the groups 
IQueryable<Group> groupQuery = 
    from g in myDC.Groups 
    where g.GroupUsers.Any(gu => gu.User.UserID == user_id) 
    select g; 
+0

Bonjour David. En ce qui concerne les deux dernières sections, je dois admettre que c'est un peu au-dessus de ma compréhension. Vous utilisez des fonctions anonymes (expressions lambda) ici? Seriez-vous prêt à donner une explication rapide de ce qui se passe ici, ou un lien rapide qui pourrait aider à l'expliquer? Merci d'avance. –