2015-09-03 57 views
2

我想通過其基本實體的公共標識符對實體列表進行分組,選擇每個組的第一個實體並返回實體的新列表。有3個不同的實體參與:GenericObject,ObjectVersion和ObjectDependency。基於其基本實體的公共標識符過濾相關實體

public class GenericObject 
{ 
    public int Id { get; set; } 
} 

public class ObjectVersion 
{ 
    public int Id { get; set; } 
    public GenericObject GenericObject { get; set; } 
} 

public class ObjectDependency 
{ 
    public string Name { get; set; } 
    public ObjectVersion ObjectVersion1 { get; set; } 
    public ObjectVersion ObjectVersion2 { get; set; } 
} 

樣本設置是這樣的:

GenericObject go1 = new GenericObject { Id = 1 }; 
GenericObject go2 = new GenericObject { Id = 2 }; 
GenericObject go3 = new GenericObject { Id = 3 }; 

ObjectVersion ov1 = new ObjectVersion { Id = 1, GenericObject = go1 }; 
ObjectVersion ov2 = new ObjectVersion { Id = 2, GenericObject = go2 }; 
ObjectVersion ov3 = new ObjectVersion { Id = 3, GenericObject = go3 }; 
ObjectVersion ov4 = new ObjectVersion { Id = 4, GenericObject = go1 }; 

List<ObjectDependency> dependencies = new List<ObjectDependency> 
{ 
    new ObjectDependency { Name = "d1", ObjectVersion1 = ov1, ObjectVersion2 = ov2 }, 
    new ObjectDependency { Name = "d2", ObjectVersion1 = ov2, ObjectVersion2 = ov3 }, 
    new ObjectDependency { Name = "d3", ObjectVersion1 = ov4, ObjectVersion2 = ov2 } 
}; 

爲了得到含OV2所有ObjectDependencies,我會過濾這樣的:

var ov2Dependencies = dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
               || d.ObjectVersion2.Id == ov2.Id) 
            .OrderBy(d => d.Name); 
foreach (ObjectDependency dependency in ov2Dependencies) 
{ 
    Console.WriteLine(dependency.Name); 
} 
// Output: 
// d1 
// d2 
// d3 

爲了讓所有ObjectVersions依賴在ov2上:

var ov2AllDependentObjectVersions = 
      dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
            || d.ObjectVersion2.Id == ov2.Id) 
         .Select(d => d.ObjectVersion1) 
         .Union(dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
                 || d.ObjectVersion2.Id == ov2.Id) 
         .Select(d => d.ObjectVersion2)) 
         .Where(o => o.Id != ov2.Id) 
         .OrderBy(o => o.Id); 
foreach (ObjectVersion ov in ov2AllDependentObjectVersions) 
{ 
    Console.WriteLine(ov.Id); 
} 
// Output: 
// 1 
// 3 
// 4 

爲了獲得最新的ObjectVersions依賴OV2不同GenericObject:

var ov2LatestDependentObjectVersions = 
     dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
           || d.ObjectVersion2.Id == ov2.Id) 
        .Select(d => d.ObjectVersion1) 
        .Union(dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
                || d.ObjectVersion2.Id == ov2.Id) 
        .Select(d => d.ObjectVersion2)) 
        .Where(o => o.Id != ov2.Id) 
        .GroupBy(o => o.GenericObject.Id) 
        .Select(g => g.OrderByDescending(o => o.Id).FirstOrDefault()) 
        .OrderBy(o => o.Id); 
foreach (ObjectVersion ov in ov2LatestDependentObjectVersions) 
{ 
    Console.WriteLine(ov.Id); 
} 
// Output: 
// 3 
// 4 

如何將過濾的樣子,以達到以下輸出?基本上,我想獲得最新的ObjectDependencies,其中包含具有不同GenericObject的ov2。過濾應該通過使用IQueryable直接轉換成T-SQL。

var ov2LatestDependencies = dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
                || d.ObjectVersion2.Id == ov2.Id) 
              // ??? 
             .OrderBy(d => d.Name); 
foreach (ObjectDependency dependency in ov2LatestDependencies) 
{ 
    Console.WriteLine(dependency.Name); 
} 
// Output: 
// d2 
// d3 

我創建了一個小提琴這裏:https://dotnetfiddle.net/OZQlWO

任何幫助,將不勝感激!

編輯:

我結束了使用基於傑森·博伊德的答案如下解決方案,支持LINQ到實體:https://dotnetfiddle.net/YSj8ki

var ov2LatestDependencies = dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
                || d.ObjectVersion2.Id == ov2.Id) 
    .Where(x => x.ObjectVersion1.Id == ov2.Id) 
    .Select(x => new 
    { 
     ObjectDependency = x, 
     ObjectVersion = x.ObjectVersion2 
    }) 
    .Union(
     dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id 
                || d.ObjectVersion2.Id == ov2.Id) 
     .Where(x => x.ObjectVersion2.Id == ov2.Id) 
     .Select(x => new 
     { 
      ObjectDependency = x, 
      ObjectVersion = x.ObjectVersion1 
     }) 
    ) 
    .GroupBy(x => x.ObjectVersion.GenericObject.Id) 
    .Select(x => x.OrderByDescending(y => y.ObjectVersion.Id).FirstOrDefault()) 
    .Select(x => x.ObjectDependency) 
    .OrderBy(d => d.Name); 
foreach (ObjectDependency dependency in ov2LatestDependencies) 
{ 
    Console.WriteLine(dependency.Name); 
} 
// Output: 
// d2 
// d3 

回答

1

所以我把重構你的LINQ查詢到的自由擴展方法 - 它可以更容易地跟蹤正在發生的事情:

public static class Extensions 
{ 
    public static IQueryable<ObjectDependency> WhereContainsObjectVersion(this IQueryable<ObjectDependency> source, int objectVersionId) 
    { 
     return 
      source 
      .Where(x => x.ObjectVersion1.Id == objectVersionId || x.ObjectVersion2.Id == objectVersionId); 
    } 

    public static IQueryable<ObjectVersion> SelectDependentObjectVersions(this IQueryable<ObjectDependency> source, int objectVersionId) 
    { 
     return 
      source 
      .WhereContainsObjectVersion(objectVersionId) 
      .Select(x => x.ObjectVersion1.Id == objectVersionId ? x.ObjectVersion2 : x.ObjectVersion1); 
    } 

    public static IQueryable<TResult> SelectDependentObjectVersions<TResult>(this IQueryable<ObjectDependency> source, int objectVersionId, Func<ObjectDependency, ObjectVersion, TResult> selector) 
    { 
     return 
      source 
      .WhereContainsObjectVersion(objectVersionId) 
      .Select(x => x.ObjectVersion1.Id == objectVersionId ? selector(x, x.ObjectVersion2) : selector(x, x.ObjectVersion1)); 
    } 

    public static IQueryable<ObjectVersion> SelectByLatestDistinctGenericObject(this IQueryable<ObjectVersion> source) 
    { 
     return 
      source 
      .GroupBy(x => x.GenericObject.Id) 
      .Select(x => x.OrderByDescending(y => y.Id).FirstOrDefault()); 
    } 

    public static IQueryable<ObjectDependency> SelectByLatestDistinctGenericObject(this IQueryable<ObjectDependency> source, int objectVersionId) 
    { 
     return 
      source 
      .SelectDependentObjectVersions(objectVersionId, (x, y) => new { ObjectDependency = x, ObjectVersion = y }) 
      .GroupBy(x => x.ObjectVersion.GenericObject.Id) 
      .Select(x => x.OrderByDescending(y => y.ObjectVersion.Id).FirstOrDefault()) 
      .Select(x => x.ObjectDependency); 
    } 
} 

然後,你可以打電話給他們以下面的方式(我評論了你的LINQ查詢的部分,並插入我的擴展方法,這樣就可以看到擴展方法更換哪部分每個查詢的):

// Get all ObjectDependencies containing ov2 
// Output: 
// d1 
// d2 
// d3 
Console.WriteLine("Get all ObjectDependencies containing ov2"); 
IEnumerable<ObjectDependency> ov2Dependencies = 
    dependencies 
    //.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    .WhereContainsObjectVersion(ov2.Id) 
    .OrderBy(d => d.Name); 
foreach (ObjectDependency dependency in ov2Dependencies) 
{ 
    Console.WriteLine(dependency.Name); 
} 

// Get all ObjectVersions dependent on ov2 
// Output: 
// 1 
// 3 
// 4 
Console.WriteLine("Get all ObjectVersions dependent on ov2"); 
IEnumerable<ObjectVersion> ov2AllDependentObjectVersions = 
    dependencies 
    //.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    //.Select(d => d.ObjectVersion1) 
    //.Union(dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    //.Select(d => d.ObjectVersion2)) 
    //.Where(o => o.Id != ov2.Id) 
    .SelectDependentObjectVersions(ov2.Id) 
    .OrderBy(o => o.Id); 
foreach (ObjectVersion ov in ov2AllDependentObjectVersions) 
{ 
    Console.WriteLine(ov.Id); 
} 

// Get newest ObjectVersions dependent on ov2 with different GenericObject 
// Output: 
// 3 
// 4 
Console.WriteLine("Get newest ObjectVersions dependent on ov2 with different GenericObject"); 
IEnumerable<ObjectVersion> ov2NewestDependentObjectVersions = 
    dependencies 
    //.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    //.Select(d => d.ObjectVersion1) 
    //.Union(dependencies.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    //.Select(d => d.ObjectVersion2)) 
    //.Where(o => o.Id != ov2.Id) 
    //.GroupBy(o => o.GenericObject.Id) 
    //.Select(g => g.OrderByDescending(o => o.Id).FirstOrDefault()) 
    .SelectDependentObjectVersions(ov2.Id) 
    .SelectByLatestDistinctGenericObject() 
    .OrderBy(o => o.Id); 
foreach (ObjectVersion ov in ov2NewestDependentObjectVersions) 
{ 
    Console.WriteLine(ov.Id); 
} 

// Get newest ObjectDependencies containing ov2 with different GenericObject 
// Output: 
// d2 
// d3 
Console.WriteLine("Get newest ObjectDependencies containing ov2 with different GenericObject"); 
IEnumerable<ObjectDependency> ov2NewestDependencies = 
    dependencies 
    //.Where(d => d.ObjectVersion1.Id == ov2.Id || d.ObjectVersion2.Id == ov2.Id) 
    // ??? 
    .SelectByLatestDistinctGenericObject(ov2.Id) 
    .OrderBy(d => d.Name); 
foreach (ObjectDependency dependency in ov2NewestDependencies) 
{ 
    Console.WriteLine(dependency.Name); 
} 
+0

這看起來很有希望,但我認爲這不會很好地轉化爲IQueryable/T-SQL? – niklr

+1

我錯過了'iqueryable'標籤。無論如何,我認爲這不重要。我相當肯定,你可以將所有'IEnumerable'類型更改爲'IQueryable'(參數和返回類型),它應該可以正常工作。 –

+1

@nilr - 根據新信息更新答案。用'IQueryable'替換了所有'IEnumerable'並刪除了迭代器塊。我相信這應該適合你。 –