[英]Entity Framework where clause filtering from specific column
我遇到了 Entity Framework 6 的問題,我不知道如何解決它。 我想用 EntityFramework 從表中讀取數據。 目標是使用 where 子句讀取該數據,該子句從特定列中過濾數據。 我要搜索的列在方法參數中指定。
一個例子:我有一張桌子
姓名 | 名 | 地址 | |
---|---|---|---|
康納 | 布賴恩 | 紐約 | 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。Persons
屬性,並使用searchTerm
和Where
僅保留那些具有相同值的 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.