Родительский список NHibernate с подсчетом потомков

Я использую NHibernate 4 с mysql. у меня есть 2 стола. Мои столы - кошка и ответ.

public class cat
{
    [Key]
    public virtual int id { get; set; }
    public virtual string catName { get; set; }

    public virtual IList<answer> answers { get; set; }
}

public class answer
{
    [Key]
    public virtual int id { get; set; }
    public virtual int catId { get; set; }
    public virtual string detail { get; set; }
    public virtual bool stat { get; set; }

    [ForeignKey("catId")]
    public virtual cat cats { get; set; }
}

Я хочу выбрать все записи кошек (со списком ответов) и с их количеством ответов cild.

мой запрос sql вроде этого;

select count(t2.id) as count, cats.*from cat cats left join answer t2 ON(cats.id=t2.catId and t2.stat=0) GROUP BY(cats.id);

Результат такой;

id - catName - count

1 - Книга - 5

2 - Pc - 0

3 - английский - 22

4 - Искусство - 56

Я попробовал также этот запрос NH;

public class myClass {
  public virtual int count { get; set; }
  public virtual cat cats { get; set; }
}

var u = db.CreateCriteria(typeof(cat), "cats")
       .CreateAlias("answer", "t2", NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("t2.stat", false))
      .SetProjection(Projections.ProjectionList()
      .Add(Projections.Count("t2.id"), "count")
      .Add(Projections.Group<cat>(g => g.id)));


var list = u.SetFetchMode("answer", FetchMode.Eager)
        .SetResultTransformer(Transformers.AliasToBean<myClass>())
        .List<myClass>();

Этот возврат запроса NHibernate также подсчитывает количество ответов. но коты всегда возвращают null. Как я могу запросить этот результат?

Редактировать 1, я могу это сделать

public class myClass {
  public virtual int count { get; set; }
  public virtual catId count { get; set; }
  public virtual cat cats { get; set; }
}


cat cats = null;
answer answers = null;

var u = db.QueryOver<cat>(() => cats)
    .JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
    .SelectList(cv => cv
        .SelectCount(() => answers.id)
        .SelectGroup(c => c.id))
    .List<object[]>()
    .Select(ax => new myClass
    {
      count = (int)ax[0],
      catId = (int)ax[1],
      cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe => fe.answers).Eager.SingleOrDefault()
    })
    .ToList();

person ASPMaker    schedule 13.01.2017    source источник
comment
Небольшая подсказка Offtopic: если у вас есть выбор, избавьтесь от api критериев и используйте QueryOver или Query Api от NHibernate   -  person Rabban    schedule 13.01.2017


Ответы (2)


В вашем результате cats всегда имеет значение null, потому что ResultTransformer пытается сопоставить свойства по их именам.

Пожалуйста, проверьте свой лог-файл NHibernate. Вы, вероятно, увидите, что ваш запрос возвращает столбцы count и id, но myClass имеет свойства count и cats.

Изменить:

Новое предложение:

Предыдущее предложение не сработало, поскольку свойство id относится к типу Int32 и не может быть присвоено myClass.cat (имеющему тип cat).

Если вам не нужна ссылка на cat-объект в myClass, вы можете изменить его на это:

public class myClass {
  public virtual int count { get; set; }
  public virtual int catId { get; set; }
}

и получим такую ​​проекцию:

.Add(Projections.Group<cat>(g => g.id), "catId"));

Если вам действительно нужно свойство типа cat в вашем классе результатов, я не думаю, что это можно сделать с помощью простой проекции, но я могу ошибаться.

Изменить 2:

Поскольку вам требуется объект типа cat в вашем результате, я предлагаю вам собрать его вручную после запроса, например:

Новый класс результата:

public class myClassResult {
  public virtual int count { get; set; }
  public virtual cat cats { get; set; }
}

Добавьте это после логики запроса:

IList<myClassResult> result = new List<myClassResult>();
foreach (var idWithCount in list) 
{
  result.Add(new myClassResult() 
  {
    cats = catsOnlyList.FirstOrDefault(x => x.id == idWithCount.catId),
    count = idWithCount.count
  });
}

catsOnlyList относится к простому списку кошек, который вам нужно получить заранее. Я знаю, что это некрасиво, но не думаю, что в запросе можно сгруппировать по самому cat.

Старое предложение (не работает из-за несовместимых типов):

Вместо

.Add(Projections.Group<cat>(g => g.id)));

использовать

.Add(Projections.Group<cat>(g => g.id), "cats"));
person Florian Lim    schedule 13.01.2017
comment
Флориан Лим возвращает те же колонки. выдайте эту ошибку: тип System.Int32 не может быть назначен свойству типа cat - person ASPMaker; 14.01.2017
comment
@ASPMaker Извините, я не тестировал решение. Я должен был знать, что это не сработает, поскольку группировка по идентификатору не даст вам свойства результата типа cat. Я изменил свой ответ. - person Florian Lim; 14.01.2017
comment
Извините, но это изменение дает мне только идентификатор. Я хочу для этого запроса, он должен возвращать количество ответов и таблицу cat с дочерними элементами ответа. Я объясняю их все в своем вопросе. - person ASPMaker; 16.01.2017
comment
@ASPMaker В этом случае вам придется собрать результат вручную. Смотрите мою вторую правку. - person Florian Lim; 16.01.2017

я могу сделать этот запрос вот так;

public class myClass {
  public virtual int count { get; set; }
  public virtual İnt catId { get; set; }
  public virtual cat cats { get; set; }
}

cat cats = null;
answer answers = null;

var u = db.QueryOver<cat>(() => cats)
    .JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
    .SelectList(cv => cv
        .SelectCount(() => answers.id)
        .SelectGroup(c => c.id))
    .List<object[]>()
    .Select(ax => new myClass
    {
      count = (int)ax[0],
      catId = (int)ax[1],
      cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe=>fe.answers).Eager.SingleOrDefault()
    })
    .ToList();
person ASPMaker    schedule 17.01.2017