簡體   English   中英

實體框架 where 子句從特定列過濾

[英]Entity Framework where clause filtering from specific column

我遇到了 Entity Framework 6 的問題,我不知道如何解決它。 我想用 EntityFramework 從表中讀取數據。 目標是使用 where 子句讀取該數據,該子句從特定列中過濾數據。 我要搜索的列在方法參數中指定。

一個例子:我有一張桌子

姓名 地址 Email
康納 布賴恩 紐約 abc@abc.com
施瓦辛格 阿諾德 洛杉磯 abc@abc.com

通常我會 select 數據是這樣的:

public List<Person> getData(string searchTerm)
{
    using (var db = new myDB())
    {
      return db.Person.Where(x=> x.Name == searchTerm).ToList();
    } 
}

但我也想靈活處理我要過濾的列。 像這樣的東西:

public getData(string columnToSearch, string searchTerm)
{
     using (var db = new myDB())
    {
      return db.Person.Where(columnToSearch == searchTerm).ToList();
    } 
}

我怎樣才能做到這一點?

我不想使用普通 SQL 並且我無法編輯數據庫。

感謝您的幫助。

鑒於您只有 4 列,我真的認為我會:

public List<Person> GetData(string columnToSearch, string searchTerm)
{
    using var db = new myDB();
  
    if(columnToSearch == "Name")
      return db.Person.Where(p => p.Name == searchTerm).ToList();
    else if(columnToSearch == "FirstName")
      return db.Person.Where(p => p.FirstName == searchTerm).ToList();
    else if(columnToSearch == "Adress")
      return db.Person.Where(p => p.Adress == searchTerm).ToList();
    else if(columnToSearch == "Email")
      return db.Person.Where(p => p.Email == searchTerm).ToList();
    else
       throw ..
}

..或一些類似的選擇結構..

public List<Person> GetData(string columnToSearch, string searchTerm)
{
    using var db = new myDB();
  
    return (columnToSearch switch {
        "Name" => db.Person.Where(p => p.Name == searchTerm),
        "FirstName" => db.Person.Where(p => p.FirstName == searchTerm)
        "Adress" => db.Person.Where(p => p.Adress == searchTerm)
        "Email" => db.Person.Where(p => p.Email == searchTerm)
        _ => throw new ArgumentException(nameof(columnToSearch) + " should be one of: Name, FirstName, Adress, Email")
    }).ToList();
}

..甚至可以切換進行挑選的代表:

public List<Person> GetData(string columnToSearch, string searchTerm)
{
    Func<Person, bool> what = columnToSearch switch {
        "Name" => p => p.Name == searchTerm,
        "FirstName" => p => p.FirstName == searchTerm
        "Adress" => p => p.Adress == searchTerm
        "Email" => p => p.Email == searchTerm
        _ => throw new ArgumentException(nameof(columnToSearch) + " should be one of: Name, FirstName, Adress, Email")
    };

    using var db = new myDB();
    return db..Person.Where(what).ToList();
}

即使您在此表中有 20 列,使用多行編輯器編寫這樣的重復代碼也相當容易:

在此處輸入圖像描述

那是一個名為 Sublime 的編輯器,但 VS 也這樣做,要么使用ctrl + alt +click 放置多個光標,要么選擇一些常見的東西並按shift + alt + . 如果你使用 VS,你會想要安裝“Multiple Carets Booster”擴展,否則粘貼會很瘋狂; 在大多數執行多個插入符號的編輯器中,如果您有例如 20 個插入符號並從剪貼板粘貼 20 行(例如 20 個列名),則每個插入符號會得到一個剪輯行。 在 VS 中,通常每個插入符號都有 20 行(粘貼 400 行),這使得將所有 20 列都放在剪貼板上並將它們粘貼到代碼流中非常困難,就像上面的動畫一樣

您可以使用以下擴展方法:

public static class QueryableExtensions
{
    public static IQueryable<T> FilterByColumn<T>(this IQueryable<T> query, string columnToSearch, string searchTerm)
    {
        var param = Expression.Parameter(typeof(T), "e");

        var body = Expression.Equal(
            Expression.PropertyOrField(param, columnToSearch),
            Expression.Constatnt(searchTerm));

        var filter = Expression.Lambda<Func<T, bool>>(body, param);

        return query.Where(filter);
    }
}

和用法:

public getData(string columnToSearch, string searchTerm)
{
    using (var db = new myDB())
    {
        return db.Person.FilterByColumn(columnToSearch, searchTerm).ToList();
    } 
}

您正在使用實體框架,它可以訪問您的Persons表。 您可能會有一個DbContext具有訪問此表的屬性:

public DbSet<Person> Persons {get; set;}

你也有兩個字符串。 一個字符串表示要過濾的Person屬性的名稱,另一個字符串表示該屬性應具有的值。

為了使您的代碼更易於閱讀、更易於重用、更易於維護和單元測試,我的建議是將您的問題分成兩個子問題:

  • 將字符串columnToSearch (實際上是您要搜索的列的名稱)翻譯成一個 propertySelector,類似於 GroupBy 中的 keySelector。
  • 創建一個擴展方法,使用此 KeySelector 到Persons屬性,並使用searchTermWhere僅保留那些具有相同值的 Person。

這種分離的好處是,您可以在訪問數據庫之前檢測到 columnToSearch 的錯誤值。 如果您不想支持某些屬性,例如外鍵,可以將它們排除在外。 該方法易於理解、易於重用、易於單元測試並且易於更改,例如,如果您想添加一個新列。

Expression<Func<Person, string>> ToPropertySelector(string columnName)
{
    switch (columName)
    {
        case nameof(Person.Name):
           return person => person.Name;
        case nameof(Person.FirstName):
           return person => person.FirstName;
        ...

        default:  // unsupported columnName
            throw new InvalidArgumentException(...);
    }
}

如果您的列有其他名稱,而不是您想與用戶交流的名稱,例如,如果 extenal 使用考慮LastName而不是 name,您可以輕松更改過程:

case "Name":
case "LastName":
    return person => person.Name;

此外,如果以后列名更改,外部用戶也不必更改。

用法:

string columName = "Email";
string value = "MyName.Gmail.Com";

using (var dbContext = new MyDbContext(...))
{
    var propertySelector = ToPropertySelector(columnName);
    return dbContext.Persons
        .Where(person => propertySelector(person) == value)
        .ToList();
}

當然,這僅在您的屬性具有字符串作為值時才有效。 如果您還想支持其他屬性類型,則需要創建一個具有正確類型的擴展方法:

Expression<Func<Person, TProperty>> ToPropertySelector<TProperty>(string columnName)
{
    ... see above
}

public static IQueryable<Person> Where<TProperty>(IQueryable<Person> persons,
    string columName,
    TProperty value)
{
    var propertySelector = ToPropertySelector<TProperty>(columnName);
    return persons.Where(person => propertySelector(person) == value);
}

public static IQueryable<Person> Where<TProperty>(IQueryable<Person> persons,
    string columName,
    string valueTxt)
{
    Type propertyType = typeof(TProperty);
    TypeConverter converter = propertyType.GetConverter();

    TProperty value = converter.ConvertFromString(valueTxt)
    return persons.Where(columnName, value);
}

用法:

DateTime birthDay = new DateTime(1993, 11, 23);
IQueryable<Person> persons = ...

var personsBornOnDate = persons.Where("BirthDay", birthDay);

重用示例:您不必將 BirthDay 用作字符串,可以使用屬性選擇器:

var personsBornOnDate = persons.Where(person => person.BirthDay, birthDay);

后者的好處是,如果您使用不存在的屬性,編譯器會警告您。 字符串版本只會在運行時警告您。

在 EF Core 6 中,您可以使用EF.Property

因此,您的查詢將是:

string field = "Name"; // You can pass dynamically your actual field here (e.g. "Email")
db.Person.Where(x => EF.Property<string>(x, field) == searchTerm).ToList();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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