1
votes

I have a pretty simple problem with a not-so-obvious solution. I have a relational mapping in my database between Users and Roles, and each user can be mapped to one or more roles. So the mapping is like so:

User < 1:n > UserRole < n:1 > Role

In my generated EF4 POCOs, User and Role each have an ICollection of the other:

public class User 
{
    //Bunch of other properties/methods

    public virtual ICollection<Role> Roles
}

public class Role
{
    //Bunch of other properties/methods

    public virtual ICollection<User> Users
}

Now, I've implemented the IoC, UoW, and repository patterns illustrated in this article, which uses an ObjectSet to fetch/persist the data via repositories.

My question is, how do I implement this:

public bool UserIsInRole(int userId, int roleId)

I have tried the following:

public bool UserIsInRole(int userId, int roleId)
{
    Role role = _roleRepository.Single(r => r.Id == roleId);
    return _userRepository.SingleOrDefault(u => u.Roles.Contains(role)) != null;
}

But it fails with:

Unable to create a constant value of type 'Data.Models.Role'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.

Plus, it's not a very elegant implementation as it's having to hit the database twice.

I was looking for something like this:

return _userRepository.SingleOrDefault(u => u.Roles.Where(r => r.Id = roleId));

But ICollection doesn't support LINQ.

How can I do this, and ideally, how can I do it with one LINQ expression and one trip to the database?

Or, am I going about this completely wrong?

Thanks in advance.

Solved: Thanks to all who posted. All gave an acceptable answer. I accepted the one that was the most elegant.

3

3 Answers

3
votes

There is a more concise way to do it:

public bool UserIsInRole(int userId, int roleId)
{
    return _userRepository.
            Any(u => u.Id == userId && 
                     u.Roles.Any(r => r.Id == roleId));
}
1
votes

Try this:

var result = _userRepository
              .Where(u => u.Id == userId)
              .SelectMany(u => u.Roles)
              .Where(r => r.Id == roleId)
              .SingleOrDefault();
return result != null;
1
votes

If I understand correctly you are trying to select all of the users in a specific role.

If that is the case then:

public bool UserIsInRole(int userId, int roleId){
  var user = (from u in _userRepository where u.Id == userId select u).SingleOrDefult();
  if (user != null)
  {
     return (from r in user.Roles where r.Id = roleId select r).Any();
  }
  //you may not want to do this if you couldn't find the passed in user.
  return false;
}

You still have to hit the database twice but both of the queries should be pretty small.


Since you say, in comments, that you already have the user that you want to use the above method should still work. I was about to write something to explain how you could do it using the repository pattern that is mentioned in the article but at the surface it isn't function any differently than using a context would, at least for querying.

Since you are passing in a the User.Id as userId and not a complete User you still need to query for the appropriate user.

Now we can shorten the query some with

return _userReposity.Where(u => u.Id == userId)
                    .SelectMany(u => u.Roles)
                    .Where(r => r.id == roleId)
                    .SingleOrDefault() != null;

Or Alternatively

return (from u in _userRepository
        from r in u.Roles
        Where u.Id == userId && r.Id = roleId
        Select r).Any();