zoukankan      html  css  js  c++  java
  • 实战 EF(LINQ) 如何以子查询的形式来 Join

    如题,大多数网上关于 LINQ Join 的示例都是以 from x in TableA  join ... 这样的形式,这种有好处,也有劣势,就是在比如我们使用的框架如果已经封装了很多方法,比如分页方法。而我们的业务方法只需要在 Service 层调用框架的分页方法,同时注入条件拼接的委托就可以了。而这时候,为了简单,就会以调用 Join() 方法来实现关联查询,外部看起来好像是子查询,而实际上 Entity Framework 生成 SQL 时,还是会以 Inner join 的形式来生成 SQL 语句,但不管怎样,我们还是解决了我们的实际需求,也就可以说成功了。

    1. 数据库

    假设有 2 张表。Person 表和 City 表。Person 表的 CityID 关联 City 表的 ID。

    City 表:

    Person 表:

    2. 需求

    现在,前台界面有一个 checkbox 复选框组,列出所有的城市,然后用户可以选择一个或多个,比如“深圳”和“北京”,分页列出对应的人员。注意,前台界面只需要列出 Person 的 Id 和 Name 就可以了,不需要列出城市的名称。

    3. 代码

    PersonQueryCondition.cs

        public class PersonQueryCondition
        {
            public IEnumerable<string> CityNameList { get; set; }
        }

    PersonService.cs

        public class PersonService : IPersonService
        {
            private readonly IRepository<Person> _personRepository;
            private readonly IRepository<City> _cityRepository;
    
            public PersonService(IRepository<Person> personRepository,
                IRepository<City> cityRepository
                )
            {
                this._personRepository = personRepository;
                this._cityRepository = cityRepository;
            }
    
            /// <summary>
            /// 根据城市名称列表获取分页实体
            /// </summary>
            /// <param name="cityIdList"></param>
            /// <returns></returns>
            public List<Person> GetPagedList(PersonQueryCondition queryCondition, int pageIndex, int pageSize, out int recordCount)
            {
                return this._personRepository.GetListPagedByCondition<PersonQueryCondition>(c => c.Id > 0, 
                    CombineQuery, 
                    queryCondition, 
                    pageIndex, 
                    pageSize, 
                    out recordCount);
            }
    
            protected virtual IQueryable<Person> CombineQuery(IQueryable<Person> query, PersonQueryCondition queryCondition)
            {
                if (queryCondition == null)
                {
                    return query;
                }
                if (queryCondition.CityNameList != null && queryCondition.CityNameList.Any())
                {
                    query = query.Join(_cityRepository.Table, p => p.CityID, c => c.Id, (p, c) => new { Person = p, City = c })
                        .Where(a => queryCondition.CityNameList.Contains(a.City.Name))
                        .Select(c => c.Person)
                        .Distinct();
                }
                return query;
            }
        }

    请注意:如果 Person 和 City 是多对多的关系,即一个用户可以在多个城市生活,一个城市也可以有多个用户。那么如果要筛选即在 “深圳” 生活的人,又曾经在 “北京” 生活过的人,则 LINQ 的语句需要更改为:

    if (queryCondition.CityNameList != null && queryCondition.CityNameList.Any())
    {
        //这里需要循环嵌套 Join 来实现
        foreach (string cityNameItem in queryCondition.CityNameList)
        {
            query = query.Join(_cityRepository.Table, p => p.CityID, c => c.Id, (p, c) => new { Person = p, City = c })
                .Where(a => a.City.Name == cityNameItem) // 注意:这里不再是调用 .Contains 方法,而是直接等于判断。
                .Select(c => c.Person)
                .Distinct();
        }
    }
    return query;

    代码解释截图如下(注意:.Select(c => c.Person) 后必须加一个 Distinct(),因为可能包含重复元素。):

    基类方法如下:

    4. 生成 的SQL 语句

     SQL 语句如下:

    SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Name] AS [Name], 
        [Extent1].[CityID] AS [CityID] 
    FROM  
        [dbo].[Person] AS [Extent1]
        INNER JOIN [dbo].[City] AS [Extent2] ON [Extent1].[CityID] = [Extent2].[Id]
    WHERE 
    (
        [Extent1].[Id] > 0
    ) 
    AND
    (
        [Extent2].[Name] IN (N'深圳', N'北京')
    ) 
    AND 
    (
        [Extent2].[Name] IS NOT NULL
    )

    谢谢浏览!

  • 相关阅读:
    【物联网】传感器+wifi传输+回复+显示
    【物联网】esp8266+LCD
    【物联网】esp8266
    windows渗透被人忽视的一些小命令
    一段刻骨铭心的爱
    网站安全狗最新版绕过测试
    Coremail邮件系统存储型XSS两个
    ......
    一个钓鱼站爆裤小记
    ASPX版菜刀一句话,留后门专用
  • 原文地址:https://www.cnblogs.com/Music/p/linq-join-sample.html
Copyright © 2011-2022 走看看