如何使用nHibernate和QueryOver API获得明显的结果?

时间:2011-01-06 14:01:13

标签: c# nhibernate queryover

我有这个存储库方法

    public IList<Message> ListMessagesBy(string text, IList<Tag> tags, int pageIndex, out int count, out int pageSize)
    {
        pageSize = 10;
        var likeString = string.Format("%{0}%", text);
        var query = session.QueryOver<Message>()
            .Where(Restrictions.On<Message>(m => m.Text).IsLike(likeString) || 
            Restrictions.On<Message>(m => m.Fullname).IsLike(likeString));

        if (tags.Count > 0)
        {
            var tagIds = tags.Select(t => t.Id).ToList();
            query
                .JoinQueryOver<Tag>(m => m.Tags)
                .WhereRestrictionOn(t => t.Id).IsInG(tagIds);
        }            

        count = 0;
        if(pageIndex < 0)
        {
            count = query.ToRowCountQuery().FutureValue<int>().Value;
            pageIndex = 0;
        }
        return query.OrderBy(m => m.Created).Desc.Skip(pageIndex * pageSize).Take(pageSize).List();
    }

您提供自由文本搜索字符串和标签列表。 问题是,如果邮件有多个标签,则会列出重复的时间。 我想要一个基于Message实体的独特结果。我看了

Projections.Distinct

但它需要一个属性列表来处理不同的问题。这个消息是我的实体根,大多数是在没有提供所有实体属性的情况下获得此行为的方法吗?

提前致谢,Anders

4 个答案:

答案 0 :(得分:64)

如果您使用的是ICriteria API,则需要:

.SetResultTransformer(new DistinctEntityRootTransformer())

如果您使用的是QueryOver API,则需要:

.TransformUsing(Transformers.DistinctRootEntity)

但要注意,这一切都发生在客户端,因此仍然会拉出所有重复的行。

答案 1 :(得分:27)

尝试这样的事情

public IPagedList<Client> Find(int pageIndex, int pageSize)
{
    Client clientAlias = null;

    var query = Session.QueryOver<Client>(() => clientAlias)

        .Select(
            Projections.Distinct(
                Projections.ProjectionList()
                    .Add(Projections.Property<Client>(x => x.Id).As("Id"))
                    .Add(Projections.Property<Client>(x => x.Name).As("Name"))
                    .Add(Projections.Property<Client>(x => x.Surname).As("Surname"))
                    .Add(Projections.Property<Client>(x => x.GivenName).As("GivenName"))
                    .Add(Projections.Property<Client>(x => x.EmailAddress).As("EmailAddress"))
                    .Add(Projections.Property<Client>(x => x.MobilePhone).As("MobilePhone"))
            )
        )
        .TransformUsing(Transformers.AliasToBean<Client>())

        .OrderBy(() => clientAlias.Surname).Asc
        .ThenBy(() => clientAlias.GivenName).Asc;

    var count = query
        .ToRowCountQuery()
        .FutureValue<int>();

    return query
        .Take(pageSize)
        .Skip(Pagination.FirstResult(pageIndex, pageSize))
        .List<Client>()
        .ToPagedList(pageIndex, pageSize, count.Value);
}

答案 2 :(得分:13)

您可以使用SelectList和GroupBy,例如:

tags.SelectList(t => t.SelectGroup(x => x.Id))

应该工作并生成相同的查询计划。

如果您需要组中的多个项目,请执行以下操作:

tags.SelectList(t => t.SelectGroup(x => x.Id)
                      .SelectGroup(x => x.Name)
               )

答案 3 :(得分:0)

我最近创建了一个基于映射对象类型应用select distinct的方法。 它将此应用于IQueryOver对象(类的属性)。方法还可以访问nhibernate配置。您可以将这些添加为方法参数。需要为生产工作,但方法在开发中工作得很好,到目前为止只用于一个实体。

创建此方法是因为我尝试在服务器级别对页面进行分页,并且不同的结果转换器不起作用。

获取对象集合(query.List())后,可能必须重新加载对象以填充一个到多个子对象。多个映射将被代理用于延迟加载。

 public void DistinctRootProjectionList<E>()
    {
        var classMapping = Context.Config.GetClassMapping(typeof(E));
        var propertyIterator = classMapping.UnjoinedPropertyIterator;
        List<IProjection> projections = new List<IProjection>();
        ProjectionList list = Projections.ProjectionList();

        list.Add(Projections.Property(classMapping.IdentifierProperty.Name), classMapping.IdentifierProperty.Name);

        foreach (var item in propertyIterator)
        {
            if (item.Value.IsSimpleValue || item.Value.Type.IsEntityType)
            {
                list.Add(Projections.Property(item.Name), item.Name);
            }
        }
        query.UnderlyingCriteria.SetProjection(Projections.Distinct(list));
        query.TransformUsing(Transformers.AliasToBean<E>());
    }

代码我曾经加载一对多关系... T是实体类型。

for (int i = 0; i < resp.Data.Count; i++)
        {
            resp.Data[i] = session.Load<T>(GetInstanceIdValue(resp.Data[i]));
        }