EF将几个表组合成一个IQueryable

smo*_*nes 8 c# linq entity-framework iqueryable entity-framework-6

我正在使用OData(WebAPI和EF)来查询数据库.现在我必须三个表" 合并 "成一个结果.

我有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; }
}
Run Code Online (Sandbox Code Playgroud)

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

我正在寻找在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
Run Code Online (Sandbox Code Playgroud)

有没有办法将三个表合并为一个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);
Run Code Online (Sandbox Code Playgroud)

我对odata控制器的用法:

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

不用说,我不想在创建时将整个表读入内存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;
Run Code Online (Sandbox Code Playgroud)

当针对可查询执行时:

_query.Where(x => x.Name.Contains("M")).OrderBy(x => x.Name).Skip(10).Take(50).ToList();
Run Code Online (Sandbox Code Playgroud)

生成的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 
Run Code Online (Sandbox Code Playgroud)

Sam*_*ath 4

你必须使用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
Run Code Online (Sandbox Code Playgroud)

Linq To Entity Query如下所示。

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));
Run Code Online (Sandbox Code Playgroud)