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

EF将多个表合并为一个IQueryable

  •  9
  • smoksnes  · 技术社区  · 8 年前

    合并 “三张表合成一个结果。

    我有3个表和一个界面,如下所示:

    public class Authority : IAssociationEntity
    {
        public string Name { get; set; }
        public int AuthorityId { get; set; }
    }
    
    public class Company : IAssociationEntity
    {
        public string Name { get; set; }
        public int CompanyId { get; set; }
    }
    
    public class Organization : IAssociationEntity
    {
        public string Name { get; set; }
        public int OrganizationId { get; set; }
    }
    
    public interface IAssociationEntity
    {
        string Name { get; set; }
    }
    

    正如您所看到的,这三个表之间有一些明显的相似之处,但由于某些原因,它们需要保留在单独的表中。我需要的是使用分页并按名称搜索这三个页面,并将它们显示在用户的同一列表中。

    我正在寻找在SQL中看起来像这样的东西

    SELECT TOP 4 a.* FROM
    (
        SELECT CompanyID, Name from Company WHERE Name = 'Bob'
        UNION
        SELECT OrganizationID, Name from Organization WHERE Name = 'Bob'
        UNION
        SELECT AuthorityID, Name from Authority WHERE Name = 'Bob'
    ) AS a
    

    有没有办法把三个表合并成一个 IQueryable ?

    我想把这三个表合并成一个 IQueryable<IAssociationEntity> 。我确实需要使用接口(或者可能是基类)并将结果作为 IQueryable(查询) 用于我的OData实现。类似于此,但无法编译:

    var query = db.Companies
        .Concat(db.Organizations)
        .Concat(db.Authorities);
    IQueryable<IAssociationEntity> mergedTables = query.Cast<IAssociationEntity>();
    
    // Here is an EXAMPLE usage.
    // What I really need is to return the IQueryable<IAssociationEntity> for my OData.
    var result = mergedTables.Where(x => x.Name == "Bob").OrderBy(x => x.Name).Skip(2).Take(10);
    

    以及我对odata控制器的用法:

    public class AssociationController : ODataController
    {
        [EnableQuery]
        public override IQueryable<IAssociationEntity> Get(ODataQueryOptions<IAssociationEntity> q)
        {
            // return my IQueryable here...
        }
    }
    

    不用说,在创建 IQueryable(查询) 。实际上我确实需要使用分页,因为这三个表中有几个表有数百万行。

    最终解决方案结束时为:

      var query = db.Companies.Select(x => new AssociationEntity { Name = x.Name })
    .Concat(db.Organizations.Select(x => new AssociationEntity { Name = x.Name }))
    .Concat(db.Authorities.Select(x => new AssociationEntity { Name = x.Name }));
        return query;
    

    在对查询表执行时:

    _query.Where(x => x.Name.Contains("M")).OrderBy(x => x.Name).Skip(10).Take(50).ToList();
    

    生成的SQL:

    SELECT 
        [UnionAll2].[C1] AS [C1], 
        [UnionAll2].[Name] AS [C2]
        FROM  (SELECT 
            1 AS [C1], 
            [Extent1].[Name] AS [Name]
            FROM [dbo].[Company] AS [Extent1]
            WHERE [Extent1].[Name] LIKE N'%M%'
        UNION ALL
            SELECT 
            1 AS [C1], 
            [Extent2].[Name] AS [Name]
            FROM [dbo].[Organization] AS [Extent2]
            WHERE [Extent2].[Name] LIKE N'%M%'
        UNION ALL
            SELECT 
            1 AS [C1], 
            [Extent3].[Name] AS [Name]
            FROM [dbo].[Authority] AS [Extent3]
            WHERE [Extent3].[Name] LIKE N'%M%') AS [UnionAll2]
        ORDER BY [UnionAll2].[Name] ASC
        OFFSET 10 ROWS FETCH NEXT 50 ROWS ONLY 
    
    3 回复  |  直到 6 年前
        1
  •  4
  •   Sampath    8 年前

    你必须使用 Class 而不是 Interface IAssociationEntity .我将其命名为 AssociationEntity .

    我已经转换了你的原件 TSQL查询 这个:

    SELECT TOP 4 a.* FROM
    (
        SELECT CompanyID, Name from Company WHERE Name = 'Bob'
        UNION
        SELECT OrganizationID, Name from Organization WHERE Name = 'Bob'
        UNION
        SELECT AuthorityID, Name from Authority WHERE Name = 'Bob'
    ) AS a
    

    Linq To实体查询 如下所示。

    var queryKey ="Bob";
    
    var query = ((from c in db.Company  where (c.Name = queryKey) select new AssociationEntity { Name = c.Name }).Take(4))
    .Concat((from o in db.Organization  where (o.Name = queryKey) select new AssociationEntity { Name = o.Name }).Take(4))
    .Concat((from a in db.Authority  where (a.Name = queryKey) select new AssociationEntity { Name = a.Name }).Take(4));
    
        2
  •  3
  •   user449689    8 年前

    你为什么不这样做呢

    var result = db.Companies.Where(x => x.Name == "Bob").Select(x => new { x.Name })
                 .Concat(db.Organizations.Where(y => y.Name == "Bob").Select(y => new { y.Name }))
                 .Concat(db.Authorities.Where(z => z.Name == "Bob").Select(z => new { z.Name })
                 .OrderBy(x => x.Name).Skip(2).Take(10);
    

    您可以替换 Select 方法。

        3
  •  3
  •   haim770    8 年前

    试试这个:

    var query = db.Companies.Select(x => new { Id = x.CompanyId, x.Name })
                .Concat(db.Organizations.Select(x => new { Id = x.OrganizationId, x.Name }))
                .Concat(db.Authorities.Select(x => new { Id = x.AuthorityId, x.Name }));
    
    var result = query.Where(x => x.Name == "Bob").OrderBy(x => x.Name).Skip(2).Take(10);
    

    SELECT
        [UnionAll2].[CompanyId] AS [C1],
        [UnionAll2].[CompanyId1] AS [C2],
        [UnionAll2].[Name] AS [C3]
        FROM  (SELECT
            [Extent1].[CompanyId] AS [CompanyId],
            [Extent1].[CompanyId] AS [CompanyId1],
            [Extent1].[Name] AS [Name]
            FROM [dbo].[Companies] AS [Extent1]
            WHERE N'Bob' = [Extent1].[Name]
        UNION ALL
            SELECT
            [Extent2].[OrganizationId] AS [OrganizationId],
            [Extent2].[OrganizationId] AS [OrganizationId1],
            [Extent2].[Name] AS [Name]
            FROM [dbo].[Organizations] AS [Extent2]
            WHERE N'Bob' = [Extent2].[Name]
        UNION ALL
            SELECT
            [Extent3].[AuthorityId] AS [AuthorityId],
            [Extent3].[AuthorityId] AS [AuthorityId1],
            [Extent3].[Name] AS [Name]
            FROM [dbo].[Authorities] AS [Extent3]
            WHERE N'Bob' = [Extent3].[Name]) AS [UnionAll2]
        ORDER BY [UnionAll2].[Name] ASC
        OFFSET 2 ROWS FETCH NEXT 10 ROWS ONLY