2012-10-04 52 views
4

我有一個Address類。如何在某些列中使用linq&linq中的Distinct到NHibernate

public class Address : RootEntityBase 
{ 
    virtual public string Province { set; get; } 
    virtual public string City { set; get; }   
    virtual public string PostalCode { set; get; } 
} 

通過這個默認值:

var myList = new List<Address> 
      { 
       new Address {Province = "P1", City = "C1", PostalCode = "A"}, 
       new Address {Province = "P1", City = "C1", PostalCode = "B"}, 
       new Address {Province = "P1", City = "C1", PostalCode = "C"}, 

       new Address {Province = "P1", City = "C2", PostalCode = "D"}, 
       new Address {Province = "P1", City = "C2", PostalCode = "E"}, 

       new Address {Province = "P2", City = "C3", PostalCode = "F"}, 
       new Address {Province = "P2", City = "C3", PostalCode = "G"}, 
       new Address {Province = "P2", City = "C3", PostalCode = "H"}, 

       new Address {Province = "P2", City = "C4", PostalCode = "I"} 
      }; 

我需要兩列進行提取不同的這種myList中的:省&市

即類似於myExpertResult

var myExpertResult = new List<Address> 
         { 
          new Address {Province = "P1", City = "C1"}, 
          new Address {Province = "P1", City = "C2"}, 
          new Address {Province = "P2", City = "C3"}, 
          new Address {Province = "P2", City = "C4"} 
         }; 

所以我使用這個代碼:

var list = myList.Select(x => new Address {City = x.City, Province = x.Province}).Distinct().ToList(); 

但我的結果是無效的,因爲計數結果是9即所有地址。在SQL

Quivalent查詢:select distinct Province , City from tblAddress

我也測試了此查詢的LINQ to NHibernate的。

var q = SessionInstance.Query<Address>(); 
     .Select(x => new Address { Province = x.Province, City = x.City }).Distinct().ToList(); 

但它不支持此查詢。例外的消息是:Expression type 'NhDistinctExpression' is not supported by this SelectClauseVisitor.

我該怎麼辦?

回答

10

您可以使用GroupBy

var result = myList.GroupBy(a => new { a.Province, a.City }) 
     .Select(g => new Address { 
        Province = g.Key.Province, 
        City = g.Key.City 
       }); 

或者使用匿名類型:

myList.Select(a => new { 
      Province = a.Province, 
      City = a.City 
     }) 
     .Distinct(); 

默認情況下,匿名類型使用重視質量進行比較,當所有的屬性都相當於是等價的。

另一種方式是客戶EqualityComparer它採用ProvinceCityEqualGetHashCode方法與另一重載Distincthere

+0

在LINQ to對象匿名類型是正確的,但在LINQ to NH無效。因爲它我必須做什麼? – Ehsan

+0

@Ehsan:嘗試第一個選項 –

+2

我認爲你可以在第一個查詢中使用Key而不是First。 – Euphoric

0

也許這可以幫助:

一個簡單的繼電器比較器

public class RelayComparer<T> : IEqualityComparer<T> 
{ 
private Func<T, T, bool> equals; 
private Func<T, int> getHashCode; 

public RelayComparer(Func<T, T, bool> equals, Func<T,int> getHashCode) 
{ 
    this.equals = equals; 
    this.getHashCode = getHashCode; 
} 

public bool Equals(T x, T y) 
{ 
    return equals(x, y); 
} 

public int GetHashCode(T obj) 
{ 
    return getHashCode(obj); 
} 
} 


var comparer = new RelayComparer<Address>(
(lhs, rhs) => lhs.Province == rhs.Province && lhs.City == rhs.City, 
     t => t.Province.GetHashCode() + t.City.GetHashCode()); 

myList.Distinct(comparer); 
0

如果你想要一個通用的方式來製作一個disti NCT由一個特定的屬性,你可以使用這個:

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Linq.Dynamic.Core; 
using System.Linq.Expressions; 

    /// <summary> Gets property information.</summary> 
    /// <exception cref="ArgumentException"> 
    /// Thrown when one or more arguments have unsupported or illegal values. 
    /// </exception> 
    /// <typeparam name="TSource"> Type of the source. </typeparam> 
    /// <typeparam name="TProperty"> Type of the property. </typeparam> 
    /// <param name="source">   Source for the. </param> 
    /// <param name="propertyLambda"> The property lambda. </param> 
    /// <returns> The property information.</returns> 
    public static PropertyInfo GetPropertyInfo<TSource, TProperty>(
     TSource source, 
     Expression<Func<TSource, TProperty>> propertyLambda) 
    { 
    Type type = typeof(TSource); 

    MemberExpression member = propertyLambda.Body as MemberExpression; 
    if (member == null) 
     throw new ArgumentException(string.Format(
      "Expression '{0}' refers to a method, not a property.", 
      propertyLambda.ToString())); 
PropertyInfo propInfo = member.Member as PropertyInfo; 
if (propInfo == null) 
    throw new ArgumentException(string.Format(
     "Expression '{0}' refers to a field, not a property.", 
     propertyLambda.ToString())); 

if (propInfo.ReflectedType != null && (type != propInfo.ReflectedType && 
             !type.IsSubclassOf(propInfo.ReflectedType))) 
    throw new ArgumentException(string.Format(
     "Expresion '{0}' refers to a property that is not from type {1}.", 
     propertyLambda.ToString(), 
     type)); 

return propInfo; 
    } 

     /// <summary> An IQueryable&lt;T&gt; extension method that distinct by a certain property.</summary> 
     /// <exception cref="NullReferenceException"> Thrown when the underlying Session value was unexpectedly null. </exception> 
     /// <typeparam name="T"> The result type of the IQueryable. </typeparam> 
     /// <typeparam name="T1"> The property type. </typeparam> 
     /// <param name="query">  The query to act on. </param> 
     /// <param name="expression"> The expression, that references the property. </param> 
     /// <returns> An IQueryable&lt;T&gt;</returns> 
     public static IQueryable<T> DistinctBy<T, T1>(this IQueryable<T> query, Expression<Func<T, T1>> expression) 
     { 
     var distinctSelection = query.Select(expression); 
     var info = GetPropertyInfo(default(T), expression); 
     var propertyInfo = query.Provider.GetType().GetProperty("Session", BindingFlags.NonPublic | BindingFlags.Instance); 
     if (propertyInfo == null) 
     { 
      throw new NullReferenceException("The underliying Session is not defined!"); 
     } 
     ISession session = propertyInfo.GetValue(query.Provider, null) as ISession; 
     var result = session.Query<T>().Where("x => @0.Contains(x." + info.Name + ")", distinctSelection); 
     return result; 
     } 
    } 

     /// <summary> An IQueryable&lt;T&gt; extension method that distinct by two properties (composite key).</summary> 
     /// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are null. </exception> 
     /// <exception cref="NullReferenceException"> Thrown when a value was unexpectedly null. </exception> 
     /// <typeparam name="T"> The resulting type. </typeparam> 
     /// <typeparam name="T1"> The type of the first property. </typeparam> 
     /// <typeparam name="T2"> The type of the second property. </typeparam> 
     /// <param name="query">   The query to act on. </param> 
     /// <param name="expressionKey1"> The first expression key (property 1 or key 1). </param> 
     /// <param name="expressionKey2"> The second expression key (property 2 or key 2). </param> 
     /// <returns> An IQueryable&lt;T&gt;</returns> 
     public static IQueryable<T> DistinctBy<T, T1, T2>(this IQueryable<T> query, Expression<Func<T, T1>> expressionKey1, Expression<Func<T, T2>> expressionKey2) 
     { 
     if (expressionKey1 == null) 
     { 
      throw new ArgumentNullException("expressionKey1"); 
     } 
     if (expressionKey2 == null) 
     { 
      return query.DistinctBy(expressionKey1); 
     } 
     var propertyInfo = query.Provider.GetType().GetProperty("Session", BindingFlags.NonPublic | BindingFlags.Instance); 
     if (propertyInfo == null) 
     { 
      throw new NullReferenceException("The underliying Session is not defined!"); 
     } 
     ISession session = propertyInfo.GetValue(query.Provider, null) as ISession; 
     var info1 = GetPropertyInfo(default(T), expressionKey1); 
     var info2 = GetPropertyInfo(default(T), expressionKey2); 

     var result = session.Query<T>().Where("k => @0.Any(k1 => k1." + info1.Name + " == k." + info1.Name + " && k1." + info2.Name + " == k." + info2.Name + ")", query); 

     return result; 
     } 

你可以這樣調用:

var query = Session.Query<Person>().DistinctBy(p => p.FirstName);