2014-03-04 90 views
3

我有一個表,讓說tblCar與像標識,品牌,型號,顏色等加入表對象列表

所有相關列我有一個包含兩個參數標識和型號汽車的搜索模式。

public class CarSearch 
{ 
    public int Id { get; set; } 
    public string Model { get; set; } 
} 

var carSearchObjets = new List<CarSearch>(); 

有了原始數據(如編號列表)的列表,讓汽車行駛我可以做那些ID:

var idList = new List<int> { 1, 2, 3 }; 
var carsFromQuery = context.Cars.Where(x => idList.Contains(x.Id); 

但如果我必須給所有的汽車行駛Id和從模型獲取該清單,我該怎麼辦?在內存對象和表之間不能完成簡單的連接。

我需要這樣的東西,

from m in context.Cars 
join n in carSearchObjets 
    on new { Id = n.Id, Model = n.Model } equals new { Id = m.Id, Model = m.Model } 
select m; 

這顯然是行不通的。

請忽略任何錯別字。如果您需要更多信息或問題不明確,請告訴我。

回答

1

一個(醜陋的但工作)的方式來管理,就是使用「從未使用過的」concat char連接。

我的意思是應該永遠不會出現在數據中的字符。這永遠是危險的,因爲...從來都不肯定,但你已經明白了。

比如,我們會說,我們的「從來沒有使用過」 CONCAT字符將~

這是不好的PERF,但至少工作:

var carSearchObjectsConcatenated = carSearchObjets.Select(m => new { m.Id + "~" + m.Model}); 

那麼你可以使用Contains再次(連接在分貝上):如果你想連接數據庫端的字符串和數字,你需要使用SqlFunctions.StringConvert

var result = context.Cars.Where(m => 
       carSearchObjectsConcatenated.Contains(SqlFunctions.StringConvert((double)m.Id) + "~" + m.Model); 

編輯

另一種解決方案是使用PredicateBuilder,通過Sorax作爲mentionned,或建立自己的篩選方法,如果您不希望第三方LIB(但PredicateBuilder是真精)。

類似的東西在一個靜態類:

public static IQueryable<Car> FilterCars(this IQueryable<Car> cars, IEnumerable<SearchCar> searchCars) 
     { 
      var parameter = Expression.Parameter(typeof (Car), "m"); 

      var idExpression = Expression.Property(parameter, "Id"); 
      var modelExpression = Expression.Property(parameter, "Model"); 

      Expression body = null; 
      foreach (var search in searchCars) 
      { 
       var idConstant = Expression.Constant(search.Id); 
       var modelConstant = Expression.Constant(search.Model); 

       Expression innerExpression = Expression.AndAlso(Expression.Equal(idExpression, idConstant), Expression.Equal(modelExpression, modelConstant)); 
       body = body == null 
        ? innerExpression 
        : Expression.OrElse(body, innerExpression); 
      } 
      var lambda = Expression.Lambda<Func<Car, bool>>(body, new[] {parameter}); 
      return cars.Where(lambda); 
     } 

使用

var result = context.Cars.FilterCars(carSearchObjets); 

這將產生一個SQL看上去就像

select ... 
from Car 
where 
(Id = 1 And Model = "ax") or 
(Id = 2 And Model = "az") or 
(Id = 3 And Model = "ft") 
+0

與所有轉換相比,查詢的性能是否會顯着降低,而不是簡單的連接? – aaa

+0

那麼,你不能使用連接對象和數據庫實體。性能的降低只是來自事實(如果我沒有錯的話),生成的sql將不會使用連接數據的索引。 –

1

'PredicateBuilder'可能會有所幫助。

var predicate = PredicateBuilder.False<Car>(); 
carSearchObjects 
.ForEach(a => predicate = predicate.Or(p => p.Id == a.Id && p.Model == a.Model)); 

var carsFromQuery = context.Cars.AsExpandable().Where(predicate); 

注意,在關於EF鏈接的文本:

如果您使用實體框架,你需要完整的LINQKit - 爲AsExpandable功能。您可以參考 LINQKit.dll或將LINQKit的源代碼複製到您的應用程序中。

0

老同學的解決方案..

//in case you have a 
List<CarSearch> search_list; //already filled 

List<Cars> cars_found = new List<Cars>(); 
foreach(CarSearch carSearch in search_list) 
{ 
    List<Cars> carsFromQuery = context.Cars.Where(x => x.Id == carSearch.Id && x.Model == carSearch.Model).ToList(); 
    cars_found.AddRange(carsFromQuery); 
} 

阿卜杜勒不用擔心的for循環。

+0

不要擔心循環,真的嗎?在這種情況下(db查詢),每個循環都是db調用。所以這可能是一個問題...(當然,取決於search_list的大小)。順便說一下,你可以使用'cars_found.AddRange(carsFromQuery)',但這是一個細節。 –

+0

我認爲汽車搜索列表不會太大。無論如何,問題中沒有提到它的時機。無論如何,我認爲這不會是一個像世界即將結束的問題。感謝我編輯了我的答案的細節。 –