Как запросить сопоставление «многие ко многим» в EF4? - PullRequest
1 голос
/ 08 марта 2011

У меня довольно простая проблема с неочевидным решением. У меня есть реляционное отображение в моей базе данных между пользователями и ролями, и каждый пользователь может быть сопоставлен с одной или несколькими ролями. Таким образом, отображение выглядит так:

User <1: n> UserRole Role

В моих сгенерированных POCO EF4 каждый пользователь и роль имеют ICollection другого:

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
}

Теперь я реализовал шаблоны IoC, UoW и репозитория, показанные в этой статье , которая использует ObjectSet для извлечения / сохранения данных через репозитории.

У меня вопрос, как мне это реализовать :

public bool UserIsInRole(int userId, int roleId)

Я пробовал следующее:

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

Но это не с:

Невозможно создать постоянное значение типа «Data.Models.Role». В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid).

Кроме того, это не очень элегантная реализация, поскольку приходится дважды обращаться к базе данных.

Я искал что-то вроде этого:

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

Но ICollection не поддерживает LINQ.

Как я могу это сделать, и в идеале, как я могу сделать это с одним выражением LINQ и одной поездкой в ​​базу данных?

Или я совершенно не прав?

Заранее спасибо.

Решено: Спасибо всем, кто написал. Все дали приемлемый ответ. Я принял тот, который был самым элегантным.

Ответы [ 3 ]

3 голосов
/ 09 марта 2011

Есть более краткий способ сделать это:

public bool UserIsInRole(int userId, int roleId)
{
    return _userRepository.
            Any(u => u.Id == userId && 
                     u.Roles.Any(r => r.Id == roleId));
}
1 голос
/ 08 марта 2011

Попробуйте это:

var result = _userRepository
              .Where(u => u.Id == userId)
              .SelectMany(u => u.Roles)
              .Where(r => r.Id == roleId)
              .SingleOrDefault();
return result != null;
1 голос
/ 08 марта 2011

Если я правильно понимаю, вы пытаетесь выбрать всех пользователей в определенной роли.

Если это так, то:

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

Вам все равно придется дважды обратиться к базе данных, но оба запроса должны быть довольно маленькими.


Поскольку в комментариях вы говорите, что у вас уже есть пользователь, которого вы хотите использовать вышеуказанным способом, он все равно должен работать. Я собирался написать что-то, чтобы объяснить, как вы могли бы сделать это, используя шаблон репозитория, который упоминается в статье, но на первый взгляд он не функционирует ничем иным, как использование контекста, по крайней мере, для запросов.

Поскольку вы передаете User.Id как userId, а не как завершенный User, вам все равно нужно запросить соответствующего пользователя.

Теперь мы можем сократить запрос с помощью

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

Или в качестве альтернативы

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