2012-10-04 4 views
4

나는 Address 클래스가 있습니다.사용 방법 일부 linq 및 linq에서 NHibernate와 구별

나는 두 개의 열하여이 myList에의 독특한 추출 할 필요가
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"} 
      }; 

: 지방 & 도시

즉 유사한 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"} 
         }; 

이 기본 값으로

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

그래서이 코드를 사용합니다 :

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

결과가 유효하지 않으므로 결과가 유효하지 않습니다. 즉 모든 주소가 9입니다. SQL에서

Quivalent 쿼리는 다음과 같습니다

select distinct Province , City from tblAddress도 내가 NHibernate에에 LINQ하여이 쿼리를 테스트했다.

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.

어떻게하면됩니까?

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

을 또는 익명 형식을 사용합니다 :

답변

10

당신은 GroupBy를 사용할 수있는 모든 특성이 동일 할 때 기본적으로

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

는, 익명 형식 비교하기 값 품질를 사용, 그것은 동일합니다.

또 다른 방법은 어쩌면이 도움이 될 수 있습니다 ProvinceEqual에 대한 Cityhere

+0

익명 형식이 올바르지 만 NH에 LINQ에 유효하지 않습니다. 그것을 위해 내가 뭘해야합니까? – Ehsan

+0

@Ehsan : 첫 번째 옵션을 시도하십시오 –

+2

첫 번째 쿼리에서 첫 번째 키 대신 키를 사용할 수 있다고 생각합니다. – Euphoric

0

다른 과부하 DistinctGetHashCode 방법을 사용하는 고객 EqualityComparer이다 :

간단한 릴레이 비교 자

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

당신은 다음과 같이 호출 할 수 있습니다 : 반대하는 LINQ에서

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