C#LINQ to SQL:重構此通用GetByID方法

c# expression-trees generics linq-to-sql

我寫了以下方法。

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

基本上它是Generic類中的一個方法,其中T是DataContext中的一個類。

該方法從T( GetTable )的類型獲取表,並檢查輸入參數的第一個屬性(始終是ID)。

這個問題是我必須首先將元素表轉換為列表以在屬性上執行GetType ,但這不是很方便,因為必須枚舉表的所有元素並將其轉換為List

如何重構此方法以避免整個表上的ToList

[更新]

我無法直接在表上執行Where的原因是因為我收到此異常:

方法'System.Reflection.PropertyInfo [] GetProperties()'沒有支持的SQL轉換。

因為GetProperties無法轉換為SQL。

[更新]

有人建議使用T的接口,但問題是T參數將是[DataContextName] .designer.cs中自動生成的類,因此我無法使其實現接口(並且它不可行實現LINQ的所有這些“數據庫類”的接口;並且,一旦我將新表添加到DataContext,該文件將被重新生成,從而丟失所有寫入的數據)。

所以,必須有一個更好的方法來做到這一點......

[更新]

我現在已經按照Neil Williams的建議實現了我的代碼,但我仍然遇到問題。以下是代碼的摘錄:

接口:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

DataContext [查看代碼]:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

通用方法:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

在這一行拋出異常: return table.SingleOrDefault(e => e.ID.Equals(id));例外是:

System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.

[更新]解決方案:

Denis Troller發布的答案以及Code Rant博客上帖子的鏈接的幫助下,我終於找到了解決方案:

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

一般承認的答案

您需要的是構建LINQ to SQL可以理解的表達式樹。假設您的“id”屬性始終命名為“id”:

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

這應該可以解決問題。它是從這個博客無恥地借來的。這基本上是LINQ to SQL在編寫查詢時所執行的操作

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

你只是為LTS做的工作,因為編譯器不能為你創建,因為沒有什麼可以強制T具有“id”屬性,並且你不能將任意“id”屬性從接口映射到數據庫。

====更新====

好的,這是一個簡單的實現,用於查找主鍵名稱,假設只有一個(不是複合主鍵),並假設所有類型都很好(也就是說,您的主鍵與“短”類型兼容)在GetById函數中使用):

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

熱門答案

一些想法......

只需刪除ToList()調用,SingleOrDefault就可以使用我認為是表的IEnumerably。

將調用緩存到e.GetType()。GetProperties()。First()以獲取返回的PropertyInfo。

你不能只為T添加一個約束來強制它們實現暴露Id屬性的接口嗎?




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因