代码之家  ›  专栏  ›  技术社区  ›  LIvanov

投影上的NHibernate查询

  •  1
  • LIvanov  · 技术社区  · 7 年前

    我有两个实体 A B A. 与客户有一对多的关系 B . 我想创建一个NHibernate查询来获取所有 具有所有 记录,其中:

    • 这个 实体处于活动状态
    • 那辆车被偷了 B 实体在日期范围内(我有2个 c# DateTime 对象)。
    entity A
    +----+--------+
    | id | active |
    +----+--------+
    |  1 |      1 |
    |  2 |      0 |
    |  3 |      1 |
    +----+--------+
    
    entity B
    +----+------+-------+------------+
    | id | year | month | foreign_id |
    +----+------+-------+------------+
    |  1 | 2000 |    11 |          1 |
    |  2 | 2001 |    12 |          2 |
    |  3 | 2002 |     4 |          1 |
    +----+------+-------+------------+
    

    到目前为止,我已经尝试过:

    return this.sessionFactory.GetCurrentSession()
        .QueryOver<A>()
        .Where(x => x.Active)
        .JoinQueryOver(x => x.BList)
        .WhereRestrictionOn(y => y.Year * 12 + y.Month) // the problem is here, exception below
        .IsBetween(2000 * 12 + 1) // january 2000
        .And(2010 * 12 + 3) // march 2010
        .List();
    
    System.InvalidOperationException: variable 'x' of type 'Domain.A' referenced from scope '', but it is not defined
    

    一般来说,我不喜欢以月为单位计算所有日期的方法(我的应用程序不关心天、小时等),但是,我现在不想更改映射(如下所示)。


    更多详情:

    我的 c# 实体如下所示:

    public class A
    {
        public virtual int Id { get; set; }
        public virtual int Active { get; set; }
        public virtual IEnumerable<B> BList { get; set; }
    }
    
    
    public class B
    {
        public virtual int Month { get; set; }
        public virtual int Year { get; set; }
    }
    
    
    internal class AMapping: ClassMap<A>
    {
        public AMapping()
        {
            Table("AObjects");
    
            Id(x => x.Id, "id");
            Map(x => x.Active, "active");
    
            HasMany(x => x.BList)
                .Table("Bobjects")
                .KeyColumn("foreign_id")
                .Component(y => {
                      y.Map(b => b.Month, "month");
                      y.Map(b => b.Year, "year");
            });
        }
    }
    
    2 回复  |  直到 7 年前
        1
  •  0
  •   Guillermo Gutiérrez    7 年前

    我认为一种方法是使用过滤器。要做的第一件事是通过过滤器类创建过滤器定义,如下所示:

    public class MonthsFilter : FilterDefinition
    {
        public MonthsFilter()
        {
            WithName("MonthsFilter")
                .AddParameter("startMonths", NHibernateUtil.Int32)
                .AddParameter("endMonths", NHibernateUtil.Int32);
        }
    }
    

    然后,您需要将过滤器添加到 ClassMap 为了一个,为了 BList 财产,通过 ApplyFilter 方法:

    internal class AMapping : ClassMap<A>
    {
        public AMapping()
        {
            Table("AObjects");
    
            Id(x => x.Id, "id");
            Map(x => x.Active, "active");
    
            HasMany(x => x.BList)
                .Table("BObjects")
                .KeyColumn("foreign_id")
                .Component(y => {
                    y.Map(b => b.Month, "month");
                    y.Map(b => b.Year, "year");
                }).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
        }
    }
    

    最后,您需要在发出查询之前启用过滤器,类似于以下内容:

    using (var session = sessionFactory.OpenSession())
    {
        // Enable filter and pass parameters
        var startMonthsValue = 2000 * 12 + 1;    // january 2000
        var endMonthsValue = 2010 * 12 + 3;  // march 2010
        session.EnableFilter("MonthsFilter")
            .SetParameter("startMonths", startMonthsValue)
            .SetParameter("endMonths", endMonthsValue);
    
        // Create and execute query (no filter for B needed here)
        var list = session.QueryOver<A>()
            .Fetch(x => x.BList).Eager  // Eager fetch to avoid the N+1 problem due to BList lazy load
            .Where(x => x.Active)
            .TransformUsing(Transformers.DistinctRootEntity)    // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
            .List();
    
        // Do whatever you want with the results
        foreach (var item in list)
        {
            Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
        }
    }
    

    一个完整的工作示例:

    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;
    using FluentNHibernate.Mapping;
    using NHibernate;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    
    namespace NHibernateTests
    {
        public class A
        {
            public virtual int Id { get; set; }
            public virtual bool Active { get; set; }
            public virtual IEnumerable<B> BList { get; set; }
        }
    
    
        public class B
        {
            public virtual int Month { get; set; }
            public virtual int Year { get; set; }
        }
    
    
        internal class AMapping : ClassMap<A>
        {
            public AMapping()
            {
                Table("AObjects");
    
                Id(x => x.Id, "id");
                Map(x => x.Active, "active");
    
                HasMany(x => x.BList)
                    .Table("BObjects")
                    .KeyColumn("foreign_id")
                    .Component(y => {
                        y.Map(b => b.Month, "month");
                        y.Map(b => b.Year, "year");
                    }).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
            }
        }
    
        public class MonthsFilter : FilterDefinition
        {
            public MonthsFilter()
            {
                WithName("MonthsFilter")
                    .AddParameter("startMonths", NHibernateUtil.Int32)
                    .AddParameter("endMonths", NHibernateUtil.Int32);
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var sessionFactory = CreateNHibernateSessionFactory();
                using (var session = sessionFactory.OpenSession())
                {
                    // Enable filter and pass parameters
                    var startMonthsValue = 2000 * 12 + 1;    // january 2000
                    var endMonthsValue = 2010 * 12 + 3;  // march 2010
                    session.EnableFilter("MonthsFilter")
                        .SetParameter("startMonths", startMonthsValue)
                        .SetParameter("endMonths", endMonthsValue);
    
                    // Create and execute query (no filter needed here)
                    var list = session.QueryOver<A>()
                        .Fetch(x => x.BList).Eager  // Eager fetch to avoid the N+1 problem due to BList lazy load
                        .Where(x => x.Active)
                        .TransformUsing(Transformers.DistinctRootEntity)    // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
                        .List();
    
                    // Do whatever you want with the results
                    foreach (var item in list)
                    {
                        Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
                    }
                }
    
                Console.WriteLine("Press ENTER to continue...");
                Console.ReadLine();
            }
    
            static ISessionFactory CreateNHibernateSessionFactory()
            {
                FluentConfiguration fc = Fluently.Configure()
                    .Database(MsSqlConfiguration.MsSql2012.ConnectionString("Server=.\\SQLEXPRESS;Database=NHibernateTests;Trusted_Connection=True;"))
                    .Mappings(m => {
                        m.FluentMappings
                            .AddFromAssembly(Assembly.GetExecutingAssembly());
                    });
    
                var config = fc.BuildConfiguration();
    
                return config.SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close")
                           .BuildSessionFactory();
            }
        }
    }
    

    有关筛选器的详细信息:

        2
  •  0
  •   Roman Artiukhin    5 年前

    自NHibernate 5.3以来,支持QueryOver lambda内的计算。所以

    .WhereRestrictionOn(y => y.Year * 12 + y.Month) 
    

    推荐文章