簡體   English   中英

Linq表達式作為參數

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM