[英]Linq expressions as params
我想從我的存儲庫中調用IQueryable<T>
方法,但是我想在我的ORM中熱切地加載子對象。 為了將持久性邏輯保留在持久性層中,我想傳遞一個表達式列表,這些表達式表示我要熱切加載的屬性。
我的IRepository<TClass>
方法如下所示:
IQueryable<TClass> AsQueryable();
我想使它看起來像:
IQueryable<TClass> AsQueryable(params --something here--[] includeProperties);
...這樣我可以這樣稱呼:
var q = myFooRepository.AsQueryable(x => x.Property1, x => x.Property2, ...);
並在后端分解委托以熱切加載指定的屬性。
我應該為此使用什么?
您的AsQueryable<TClass>
應該具有屬性表達式作為參數,並且需要具有以下簽名:
public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)
注意,我們使用的是Func<TClass, object>
,它是一個接受TClass作為輸入並返回對象的函數。 這樣我們就可以撥打電話如下:
IQueryable<TClass> tClassQueryable = tClassObj.AsQueryable(x => x.Property1, x => x.Property2);
還要注意,我沒有偶然地選擇一個object
作為Func<TClass, object>
的TResult。 由於函數委托的TResult泛型參數是協變的,因此即使我們使用不同的屬性類型,也可以傳遞表達式。 因此,上面示例中的Property1和Property2不必為同一類型。
就我的問題而言,就是這樣,但是這里還有一些額外的內容:
如果您偶然需要評估傳遞的表達式以便將其與ORM一起使用(例如,您只需要屬性名稱,但想將它們作為表達式傳遞,以避免對名稱進行硬編碼並保留編譯時檢查),則需要像這樣的東西:
public static IQueryable<TClass> AsQueryable<TClass>(this TClass obj, params Expression<Func<TClass, object>>[] propertyExpressions)
{
foreach (var propertyExpression in propertyExpressions)
{
MemberExpression memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
// this is needed for value types properties.
UnaryExpression unaryExpression = (UnaryExpression)propertyExpression.Body;
memberExpression = unaryExpression.Operand as MemberExpression;
}
if (memberExpression == null)
throw new ArgumentException(string.Format("Expression '{0}' is not a member expression.", propertyExpression.ToString()));
PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new ArgumentException("MemberExpression.Member is not a PropertyInfo.");
// at this point we have PropertyInfo which you can use with your OR Mapper to further implement logic which will eager load the property
// e.g. property name can be retrieved with:
string propertyName = propertyInfo.Name;
// do your ORM stuff here
}
}
上面的代碼確保傳遞的表達式是屬性表達式,並從中提取PropertyInfo。
這些選項似乎不應該成為此方法的關注點,因為您可能希望整個存儲庫懶惰地或急切地加載屬性。 我假設除了AsQueryable
之外,您在此接口中還有其他方法。
因此,一種不同的方法是將此功能保留在AsQueryable()
方法之外,而是創建其他方法,這些方法將創建一個配置為熱切加載的新包裝器(我不會更改基礎實例)。
即
// default repo is lazy
var myRepo = GetRepository<TClass>();
// these cool fluent methods create an eager repo
var myEagerRepo = myRepo
.LoadEagerly(x => x.Property1)
.LoadEagerly(x => x.Property2);
這很干凈,因為您不必讓調用者代碼急切地確定要加載的內容,並且很可能減少了在各處編寫的管道代碼的數量。 這樣,您可以創建熱切的存儲庫,並將其轉發給一堆毫無疑問的代碼。
我想到的是假設您需要使所有屬性都具有相同的類型(在以下示例代碼中使用通用參數O表示):
class SampleClass
{
public string Property { get; set; }
public SampleClass()
{
Property = DateTime.Now.ToString();
}
}
class Program
{
public static O Something<I, O>(params System.Linq.Expressions.Expression<Func<I, O>>[] funks)
where I: new()
{
I i = new I();
var output = funks[0].Compile()(i);
return output;
}
static void Main(string[] args)
{
var dx= Something<SampleClass, string>(x => x.Property);
}
}
希望對您有所幫助。 我仍在思考如何使自己與眾不同。 好問題! 實際上,您不需要使用System.Linq.Expression.Expression>,而只需使用Func。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.