views:

149

answers:

3

I have a table with server names and logins. I need to retrieve the logins that are common across a group of servers.

Given the following data:

ServerName    Login
-------------------------------
Server1       User1
Server2       User1
Server2       User2

I would pass in Server1,Server2 and get back only User1 as User2 is not associated Server1.

Can anyone tell me how this would be achieved in LINQ to SQL?

I have tried Contains but that returns me all users on any of the servers which is kind of the opposite to what I'm looking for.

EDIT: One of my colleagues managed to write the SQL version of what I'm after....

SELECT Login
  FROM ServerLogins
  WHERE ServerName IN ('Server1', 'Server2')
GROUP BY Login
HAVING count(Login) = 2

but neither of us know how to translate this into a LINQ query.

ADDITIONAL EDIT:

With Ryan's help and some Googling of the differences in LINQ between VB and C# I got the following to work.

Dim logins = From l In dc.ServerLogins _
             Where servers.Contains(l.ServerName) _
             Group l By l.Login Into Group _
             Where Group.Count() = servers.Count _
             Select Login

Thanks again to everyone for their help.

Nick

A: 

Personally I think this is a good place NOT to use Linq to SQL and instead to use either a sproc or a standard SQL query. I think that even if you were to come up with the correct query in Linq, it would not be very readable and/or efficient.

The SQL you would have would look something like this:

SELECT Login
FROM ServerLogins
WHERE ServerName IN ('Server1', 'Server2')
GROUP BY Login
HAVING COUNT(*) = 2

Note that the "2" in the last line should be replaced with the number of server names in the list above ("IN ('Server1', 'Server2')").

Chris Shaffer
Spooky, that's the exact statement we just came up with :)This is currently done in a sproc but it uses a table of consecutive numbers and a temp table to convert the comma separated list of server names into a temp table against which the IN statement is run and I'd love not to have to do this (or to have to write the sql in a string at runtime)
Nick
+2  A: 

Here is what I came up with. You'll probably want to check and find out what SQL it actually generates if you're worried about it.

List<string> servers = new List<string>{"Server1", "Server2"};

var logins = from l in context.ServerLogins
             where servers.Contains(l.ServerName)
             group l by l.Login into g
             where g.Count() == servers.Count
             select g.Key;
Ryan Versaw
We were getting close to this but I'm getting a compilation error of "Definition of method g is not accessible in this context".Whether this is because I'm working in VB I don't know.
Nick
I think this should work for C#, but I'm not as familiar with Linq in VB. I'm trying to look around so I can maybe fix it up for you.
Ryan Versaw
It looks like you may need to use `Group` instead of just `g`. I'm really not sure though... I'm having some network issues so I can't access many decent resources.
Ryan Versaw
Using this resource http://www.aspfree.com/c/a/.NET/Grouping-and-Aggregating-When-Querying-LINQ-to-SQL/4/ and your pointers I have managed to get it working...see post update for working code.
Nick
A: 

As long as there's a reasonable practical limit on the # of servers being passed in I'd go with something like this:

public ICollection<Login> GetLoginsForServers(params string[] servers)
{
    if (servers == null || servers.Length == 0)
        return new List<Login>();

    var logins = db.Logins.Where(p => p.ServerName == servers[0]);
    for (int i=1; i<servers.Length; i++)
    {
        logins = logins.Intersect(db.Logins.Where(p => p.ServerName == servers[i]));
    }

    return logins.ToList();
}

Basically you're starting with all the logins associated with the first server then limiting it by those associated with each subsequent. Since the query doesn't get executed until ToList() you still only query the database once, and although the query itself is bound to be ugly, the hope is that the LINQ2SQL provider generates something that will result in an efficient query plan.

AndyM