你需要建立一個表達式樹。
首先,因爲你需要引入IsDefault
條件下,表達可以是這樣的:
s.ContactPoints
.Where(x => x.Type == ContactPointType.Email && x.IsDefault)
.Select(x => x.Value)
.DefaultIfEmpty(string.Empty)
.FirstOrDefault()
然後,接觸點選擇轉換成一種表達。
private static Expression<Func<Site, string>> GetContactPoint(ParameterExpression siteParam, ParameterExpression cpeParam, ContactPointType type)
{
// Where.
var typeCondition = Expression.Equal(Expression.Property(cpeParam, "Type"), Expression.Constant(type));
var defaultCondition = Expression.Equal(Expression.Property(cpeParam, "IsDefault"), Expression.Constant(true));
var condition = Expression.AndAlso(typeCondition, defaultCondition);
var predicateExp = Expression.Lambda<Func<ContactPointEntity, bool>>(condition, cpeParam);
var whereExp = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(ContactPointEntity) }, Expression.Property(siteParam, "ContactPoints"), predicateExp);
// Select.
var valueExp = Expression.Lambda<Func<ContactPointEntity, string>>(Expression.Property(cpeParam, "Value"), cpeParam);
var selectExp = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(ContactPointEntity), typeof(string) }, whereExp, valueExp);
// DefaultIfEmpty.
var defaultIfEmptyExp = Expression.Call(typeof(Enumerable), "DefaultIfEmpty", new[] { typeof(string) }, selectExp, Expression.Constant(string.Empty));
// FirstOrDefault.
var firstOrDefaultExp = Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { typeof(string) }, defaultIfEmptyExp);
var selector = Expression.Lambda<Func<Site, string>>(firstOrDefaultExp, siteParam);
return selector;
}
並且還創建站點標籤dto選擇器。
private static Expression<Func<Site, SiteLabelDto>> GetSite(ParameterExpression siteParam, ParameterExpression cpeParam)
{
var newExp = Expression.New(typeof(SiteLabelDto));
var initExp = Expression.MemberInit(
newExp,
Expression.Bind(typeof(SiteLabelDto).GetProperty("Id"), Expression.Lambda<Func<Site, int>>(Expression.Property(siteParam, "Id"), siteParam).Body),
Expression.Bind(typeof(SiteLabelDto).GetProperty("Name"), Expression.Lambda<Func<Site, string>>(Expression.Property(siteParam, "Name"), siteParam).Body),
/* other basic information */
Expression.Bind(typeof(SiteLabelDto).GetProperty("Email"), GetContactPoint(siteParam, cpeParam, ContactPointType.Email).Body),
Expression.Bind(typeof(SiteLabelDto).GetProperty("Phone"), GetContactPoint(siteParam, cpeParam, ContactPointType.Phone).Body)
/* other types */
);
var selector = Expression.Lambda<Func<Site, SiteLabelDto>>(initExp, siteParam);
return selector;
}
用法。
var siteParam = Expression.Parameter(typeof(Site), "s");
var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
var selector = GetSite(siteParam, cpeParam);
return Context.Sites
.Where(s => !s.IsDeleted && s.Vendor.Id == vendorId)
.Select(selector)
.ToArray();
PS:
也許有些上面需要的代碼進行重構,這只是給出了基本思路是如何做到這一點。
更新
你也可以創建一個包裝類與所有接觸點一起包含EF實例。
public class ContactPointExt<T>
{
public T Instance { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
然後改變GetSite
爲GetContactPoints
返回結果爲ContactPointExt<T>
。
private static Expression<Func<Site, ContactPointExt<T>>> GetContactPoints<T>(ParameterExpression siteParam, ParameterExpression cpeParam)
{
var type = typeof(ContactPointExt<T>);
var newExp = Expression.New(type);
var initExp = Expression.MemberInit(
newExp,
Expression.Bind(type.GetProperty("Instance"), siteParam),
Expression.Bind(type.GetProperty("Email"), GetContactPoint(siteParam, cpeParam, ContactPointType.Email).Body),
Expression.Bind(type.GetProperty("Phone"), GetContactPoint(siteParam, cpeParam, ContactPointType.Phone).Body)
);
var selector = Expression.Lambda<Func<Site, ContactPointExt<T>>>(initExp, siteParam);
return selector;
}
的ContactPointExt<T>
結果可以被重新投影到SiteLabelDto
與另一Select
。從OP
var siteParam = Expression.Parameter(typeof(Site), "s");
var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
var selector = GetContactPoints<Site>(siteParam, cpeParam);
return Context.Sites
.Where(s => !s.IsDeleted && s.Vendor.Id == vendorId)
.Select(selector)
.Select(s => new SiteLabelDto
{
Id = s.Instance.Id,
Name = s.Instance.Name,
Email = s.Email,
Phone = s.Phone
})
.ToArray();
EDIT我們創建了一個包裝方法,使這個一點點簡單的重複使用,把它在這裏只是爲了證明別人:
/// <summary>
/// Wraps up a each of a query's objects in a ContactPointExt<T> object, providing the default contact point of each type.
/// The original query object is accessed via the "Instance" property on each result.
/// Assumes that the query object type has a property called ContactPoints - if different, supply the property name as the first argument.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="query"></param>
/// <param name="contactPointsPropertyName"></param>
/// <returns></returns>
public static IQueryable<ContactPointExt<T>> WithContactPointProcessing<T>(this IQueryable<T> query, string contactPointsPropertyName = "ContactPoints") where T : class
{
var siteParam = Expression.Parameter(typeof(T), "s");
var cpeParam = Expression.Parameter(typeof(ContactPointEntity), "cpe");
var selector = ContactPointHelpers.GetContactPoints<T>(siteParam, cpeParam, contactPointsPropertyName);
return query.Select(selector);
}
我確實喜歡你的答案,但我需要在接觸點上這樣做,以便它可以重複使用,而不必將網站作爲表達樹來使用,因爲這意味着必須做很多我們的回購層作爲表達髮束。但答案很接近。我不確定是否可以「重新調整」getcontactpoint表達式方法以便在接觸點上使用,因爲如何在這一點上將選擇表達式傳遞給它們。我會認爲,如果我做了s.contactpoints.select(exp())它只能在單個接觸點上工作,而不是在整個列表中。 – Jon 2014-09-30 08:30:20
@Jon,看到我的更新,我創建了另一個擴展,以便聯繫點可以重用(並重新投影到另一個類) – 2014-09-30 11:28:40
謝謝。這很棒,就像一個魅力..我已經編輯了你的答案,以顯示我們寫的幫手,如果其他人遇到這個問題,它會多一點。再次感謝 – Jon 2014-09-30 14:49:12