Преображение коллекции дочерних объектов свойством с помощью Fluent nHibernate - PullRequest
0 голосов
/ 26 мая 2011

Прошу прощения за мое невежество, я новичок в nHibernate и у меня возникла концептуальная проблема при попытке фильтрации дочерней коллекции с помощью запроса nHibernate

Моя объектная модель содержит две сущности: User и Task, настроенные так

public class User
{
    public User()
    {
        this.Tasks = new List<Task>();
    }

    public User(int id): this()
    {
        this.Id = id;
    }

    public virtual int Id { get; private set; }

    public virtual IList<Task> Tasks { get; set; }
}

public class Task
{
    public Task() { }

    public Task(int id, bool active): this()
    {
        this.Id = id;
        this.Active = active;
    }

    public virtual int Id { get; set; }

    public virtual bool Active { get; set; }
}

Мои сопоставления nHibernate для них следующие:

public class UserMap: ClassMap<User>
{
    public UserMap()
    {
        Table("user");
        Id(x => x.Id);
        HasMany(x => x.Tasks);
    }
}

public class TaskMap : ClassMap<Task>
{
    public TaskMap()
    {
        Table("task");
        Id(x => x.Id);
        Map(x => x.Active);
    }
}

В моей базе данных есть две таблицы 'task' и 'user', которые я таким образом заполнил

SELECT * FROM task;
+----+--------+---------+
| Id | Active | User_id |
+----+--------+---------+
|  1 |      1 |       3 |
|  2 |      1 |       3 |
|  3 |      1 |       3 |
|  4 |      0 |       3 |
|  5 |      0 |       3 |
|  6 |      1 |       1 |
|  7 |      1 |       1 |
|  8 |      1 |       1 |
|  9 |      0 |       1 |
| 10 |      0 |       1 |
+----+--------+---------+
10 rows in set

SELECT * FROM user;
+----+
| Id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set

Я пытаюсь выполнить запрос, который возвращает конкретного пользователя с только активными задачами из его коллекции «Задачи»

var query = QueryOver.Of<User>()
    .Where(u => u.Id == 3)
    .JoinQueryOver<Task>(x => x.Tasks)
    .Where(t => t.Active == true);

var results = dataProvider.ExcecuteQuery<User>(query);

Когда я выполняю этот запрос, я ожидаю получить один пользовательский объект с3 объекта Task в его коллекции Tasks, вместо этого я получаю 3 копии одного и того же объекта задачи (task.Id = 3), каждый из которых имеет все 5 задач в соответствующих коллекциях.

Возможно ли то, что я пытаюсь сделать, на самом деле возможно, или я должен вместо этого просто запрашивать объект Task?

Надеюсь, что это не тот случай, так как было бы неплохо иметь возможность простопросматривать активные задачи пользователей без необходимости возиться с ручной фильтрацией.

Ответы [ 2 ]

0 голосов
/ 27 мая 2011

Вы можете применить .TransformUsing( Transformers.DistinctRootEntity) перед List() методом, чтобы получить только 1 пользовательский объект. После этого вы получаете еще один запрос к базе данных, чтобы получить список задач этого пользователя (без фильтрации активных), и в этом смысл ленивой загрузки. Добавьте .ShowSql().FormatSql() в ваш раздел конфигурации базы данных, чтобы увидеть, что происходит.

Также вы можете добавить это свойство к вашему Task классу

public virtual User User { set; get; }

Чтобы иметь такие запросы: (что вы хотите в первую очередь)

 var list = session.QueryOver<Task>()                                                        
                            .Fetch(t=>t.User).Eager
                            .Where(t => t.Active && t.User.Id==3)
                            .TransformUsing(Transformers.DistinctRootEntity)
                            .List();
0 голосов
/ 26 мая 2011

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

Я не очень хорошо использую QueryOver.Of<T>(), но вот один из способов сделать это, используя session.QueryOver<T>():

        var users = session.QueryOver<User>()
            .Where(u => u.Id == 3)
            .Fetch(o => o.Tasks)
            .Lazy()
            .SingleOrDefault();

        users.Tasks.TakeWhile(o => o.Active);

Я думаю, вы тоже можете использовать фильтры, но это не похоже на то, что вы хотели сделать. Я не думаю, что QueryOver.Of<T>() делает то, что вы хотите, потому что отключенный запрос будет нуждаться в фильтре для правильного возврата дочерних элементов.

...