Silverlight ADO.Net 데이터 서비스 클라이언트 API (및 따라서 Linq To Entities)를 사용하여 where 절의 ID 목록을 사용하는 쿼리를 만들려고합니다. 누구든지 지원되지 않는 포함에 대한 해결 방법을 알고 있습니까?
다음과 같이하고 싶습니다.
List<long?> txnIds = new List<long?>();
// Fill list
var q = from t in svc.OpenTransaction
where txnIds.Contains(t.OpenTransactionId)
select t;
이것을 시도 :
var q = from t in svc.OpenTransaction
where txnIds.Any<long>(tt => tt == t.OpenTransactionId)
select t;
그러나 ” ‘Any’방법은 지원되지 않습니다.”
답변
업데이트 : EF ≥ 4는 Contains
직접 (체크 아웃 Any
)을 지원 하므로 해결 방법이 필요하지 않습니다.
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
IEnumerable<TValue> collection
)
{
if (selector == null) throw new ArgumentNullException("selector");
if (collection == null) throw new ArgumentNullException("collection");
if (!collection.Any())
return query.Where(t => false);
ParameterExpression p = selector.Parameters.Single();
IEnumerable<Expression> equals = collection.Select(value =>
(Expression)Expression.Equal(selector.Body,
Expression.Constant(value, typeof(TValue))));
Expression body = equals.Aggregate((accumulate, equal) =>
Expression.Or(accumulate, equal));
return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, p));
}
//Optional - to allow static collection:
public static IQueryable<TEntity> WhereIn<TEntity, TValue>
(
this ObjectQuery<TEntity> query,
Expression<Func<TEntity, TValue>> selector,
params TValue[] collection
)
{
return WhereIn(query, selector, (IEnumerable<TValue>)collection);
}
용법:
public static void Main()
{
using (MyObjectContext context = new MyObjectContext())
{
//Using method 1 - collection provided as collection
var contacts1 =
context.Contacts.WhereIn(c => c.Name, GetContactNames());
//Using method 2 - collection provided statically
var contacts2 = context.Contacts.WhereIn(c => c.Name,
"Contact1",
"Contact2",
"Contact3",
"Contact4"
);
}
}
답변
일부 e-sql을 직접 코딩 할 수 있습니다 (키워드 “it”참고).
return CurrentDataSource.Product.Where("it.ID IN {4,5,6}");
다음은 YMMV 컬렉션에서 e-sql을 생성하는 데 사용한 코드입니다.
string[] ids = orders.Select(x=>x.ProductID.ToString()).ToArray();
return CurrentDataSource.Products.Where("it.ID IN {" + string.Join(",", ids) + "}");
답변
에서 MSDN :
static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
// p => valueSelector(p) == values[0] || valueSelector(p) == ...
if (!values.Any())
{
return e => false;
}
var equals = values.Select(
value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
쿼리는 다음과 같습니다.
var query2 = context.Entities.Where(BuildContainsExpression<Entity, int>(e => e.ID, ids));
답변
Silverligth에 대해 잘 모르겠지만 linq to objects에서는 항상 이러한 쿼리에 any ()를 사용합니다.
var q = from t in svc.OpenTranaction
where txnIds.Any(t.OpenTransactionId)
select t;
답변
기록을 완료하기 위해 마지막으로 사용한 코드는 다음과 같습니다 (명확성을 위해 오류 검사 생략).
// How the function is called
var q = (from t in svc.OpenTransaction.Expand("Currency,LineItem")
select t)
.Where(BuildContainsExpression<OpenTransaction, long>(tt => tt.OpenTransactionId, txnIds));
// The function to build the contains expression
static System.Linq.Expressions.Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
System.Linq.Expressions.Expression<Func<TElement, TValue>> valueSelector,
IEnumerable<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
System.Linq.Expressions.ParameterExpression p = valueSelector.Parameters.Single();
// p => valueSelector(p) == values[0] || valueSelector(p) == ...
if (!values.Any())
{
return e => false;
}
var equals = values.Select(value => (System.Linq.Expressions.Expression)System.Linq.Expressions.Expression.Equal(valueSelector.Body, System.Linq.Expressions.Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<System.Linq.Expressions.Expression>((accumulate, equal) => System.Linq.Expressions.Expression.Or(accumulate, equal));
return System.Linq.Expressions.Expression.Lambda<Func<TElement, bool>>(body, p);
}
답변
다음은 DataServiceContext를 사용하여 집합 기반 쿼리를 작성하는 방법을 보여주는 예입니다.
http://blogs.msdn.com/phaniraj/archive/2008/07/17/set-based-operations-in-ado-net-data -services.aspx
답변
매우 감사합니다. WhereIn 확장 방법으로 충분했습니다. 나는 그것을 프로파일 링하고 e-sql과 동일한 SQL 명령을 데이터베이스에 생성했습니다.
public Estado[] GetSomeOtherMore(int[] values)
{
var result = _context.Estados.WhereIn(args => args.Id, values) ;
return result.ToArray();
}
생성 :
SELECT
[Extent1].[intIdFRLEstado] AS [intIdFRLEstado],
[Extent1].[varDescripcion] AS [varDescripcion]
FROM [dbo].[PVN_FRLEstados] AS [Extent1]
WHERE (2 = [Extent1].[intIdFRLEstado]) OR (4 = [Extent1].[intIdFRLEstado]) OR (8 = [Extent1].[intIdFRLEstado])