EF 将多个表合并为一个 IQueryable
EF Combine several tables into one IQueryable
我正在使用 OData(WebAPI 和 EF)查询数据库。现在我必须“合并”三 table 成一个结果。
我有 3 个 table 和一个界面,看起来像这样:
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; }
}
如您所见,三个 table 之间有一些明显的相似之处,但由于某些原因,它们需要保持在不同的 table 中。我需要的是使用分页并按名称搜索所有三个,并将它们呈现在同一个列表中供用户使用。
我在 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
有没有办法将三个 table 合并为一个 IQueryable
?
我想把这三个table合并成一个IQueryable<IAssociationEntity>
。我确实需要使用接口(或者可能是基础 class)并将结果作为我的 OData 实现的 IQueryable
。类似这样的东西,但无法编译:
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
时将整个 table 读入内存。我实际上确实需要使用分页,因为这三个 table 中的几个有数百万行。
最终解决方案为:
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
你为什么不按如下方式做呢:
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
方法中的 Anonymous 对象替换为基础 class。
你必须使用 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 到实体查询,如下所示。
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));
试试这个:
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);
生成的SQL为:
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
我正在使用 OData(WebAPI 和 EF)查询数据库。现在我必须“合并”三 table 成一个结果。
我有 3 个 table 和一个界面,看起来像这样:
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; }
}
如您所见,三个 table 之间有一些明显的相似之处,但由于某些原因,它们需要保持在不同的 table 中。我需要的是使用分页并按名称搜索所有三个,并将它们呈现在同一个列表中供用户使用。
我在 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
有没有办法将三个 table 合并为一个 IQueryable
?
我想把这三个table合并成一个IQueryable<IAssociationEntity>
。我确实需要使用接口(或者可能是基础 class)并将结果作为我的 OData 实现的 IQueryable
。类似这样的东西,但无法编译:
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
时将整个 table 读入内存。我实际上确实需要使用分页,因为这三个 table 中的几个有数百万行。
最终解决方案为:
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
你为什么不按如下方式做呢:
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
方法中的 Anonymous 对象替换为基础 class。
你必须使用 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 到实体查询,如下所示。
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));
试试这个:
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);
生成的SQL为:
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