2016-09-26 10 views
7

私はOData(WebAPIとEF)を使用してデータベースを照会しています。そして今私は "マージ" 3つの結果に1つのテーブル。EF複数のテーブルを1つのIQueryableに結合します。

私は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; } 
} 

あなたがそこに3つのテーブルの間にいくつかの明白な類似点がありますが、いくつかの理由のために、彼らは別々のテーブルに滞在する必要が見ることができるように。私が必要とするのは、ページングを使用して3つすべてを名前で検索し、それらを同じリストにユーザー用に提示することです。

私は1つIQueryableに3つのテーブルをマージする方法はあり

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 

SQL

で次のようになります何かを探していますか?

これらの3つのテーブルをIQueryable<IAssociationEntity>に結合したいと思います。私は本当にインターフェイス(またはおそらく基本クラス)を使用し、結果を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 IQuearyable<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。私は実際にページングを使う必要があります。なぜなら、これらの3つのテーブルのいくつかは何百万もの行を持っているからです。

最終的な解決策は、AS終わった:

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 
+0

心に来る最初の事は結果を、その後、個別に3つのすべてを照会マージすることです。ページング、順序付けなど、それはどのように複数のテーブルにまたがって行われますか? – Daniel

+0

あなたはなぜこの違いがあるのか​​教えてください: 'WHERE Name LIKE '%Bob%''は一箇所に、他の場所は 'Where(x => x.Name ==" Bob ")'なぜですか? – Sampath

+0

@Sampath - 良いキャッチ!これは、SQL出力の​​可能性の例であり、私が実際に行ったことではありません。私は質問を更新しました。 – smoksnes

答えて

2

InterfaceIAssociationEntityの代わりにClassを使用する必要があります。これをAssociationEntityと命名しました。

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クエリへのエンティティ下図のように:

私はあなたの元TSQLクエリこれを変換しています。

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)); 
3

を次のようになります。

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オブジェクトを基本クラスに置き換えることができます。

3

これを試してみてください:

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 
+0

ありがとうございます。それは他のものと非常に似ていますが、クエリをIQueryableとして返す必要があったため、匿名の型を扱うことができません。 – smoksnes

関連する問題