2012-05-15 86 views
5

我一直在使用實體框架和POCO First方法。我幾乎遵循Steve Sanderson在他的書「Pro ASP.NET MVC 3 Framework」中描述的模式,使用DI容器和DbContext類連接到SQL Server。使用實體框架提高效率

SQL服務器中的基礎表包含由不同應用程序使用的非常大的數據集。正因爲如此,我不得不創建視圖的,我需要在我的應用程序中的實體:

class RemoteServerContext : DbContext 
{ 
    public DbSet<Customer> Customers { get; set; } 
    public DbSet<Order> Orders { get; set; } 
    public DbSet<Contact> Contacts { get; set; } 
    ... 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Customer>().ToTable("vw_Customers"); 
     modelBuilder.Entity<Order>().ToTable("vw_Orders"); 
     ... 
    } 
} 

,這似乎爲我的大部分需求,做工精細。

我的問題是,其中的一些觀點在他們的數據大量這樣,當我打電話是這樣的:

var customers = _repository.Customers().Where(c => c.Location == location).Where(...); 
它似乎被帶回整個數據集,從而可以

在LINQ查詢將集合減少到我需要的集合之前需要一些時間。當條件僅適用於幾條記錄時,這看起來非常低效,而且我正在從SQL Server獲取整個數據集。

我曾嘗試通過使用存儲過程來解決這個問題,比如

public IEnumerable<Customer> CustomersThatMatchACriteria(string criteria1, string criteria2, ...) //or an object passed in! 
{ 
    return Database.SqlQuery<Customer>("Exec pp_GetCustomersForCriteria @crit1 = {0}, @crit2 = {1}...", criteria1, criteria2,...); 
} 

,而這是要快得多,這裏的問題是,它不返回DbSet,所以我失去了所有的我的對象之間的連接,例如即使我包含他們的ID,我也不能引用任何關聯對象,例如訂單或聯繫人,因爲返回類型是'Customers'的集合,而不是它們的DbSet集合。

有沒有人有更好的方式讓SQL服務器執行查詢,以便我不傳遞大量未使用的數據?

回答

4
var customers = _repository.Customers().Where(c => c.Location == location).Where(... 

如果Customers()回報IQueryable,僅此聲明實際上不會「帶回」任何東西 - 上IQueryable調用Where給你另一個IQueryable,直到你做的東西它不是導致查詢執行(例如ToListFirstOrDefault)任何事情將被實際執行並返回結果。

但是,如果這個Customers方法返回一個實例化對象的集合,那麼是的,因爲你要求所有的對象,你得到它們。

我從來沒有使用過代碼優先或實際上甚至是存儲庫模式,所以我不知道要建議什麼,除了儘可能長時間停留在IQueryable的領域之外,並且只執行一次查詢您已應用所有相關的過濾器。

+0

+1。對於更多'可擴展的方法',你可以編寫一個接受謂詞的函數,並且返回'_repository.Customers()。Where(predicate)'或者(如果它不再是IQueryable),用'context.CreateQuery 「Customers」)。where(predicate)',最後可能調用'.ToList()'。它應該構建一個很好的,優化的表達式。 –

+1

嗨。你的建議是留在IQueryable領域。我正在使用IEnumerable,它不會將查詢傳遞給服務器,而是獲取所有記錄,然後對其進行過濾。看到這裏的文章:http://www.fascinatedwithsoftware.com/blog/post/2011/06/27/IEnumerable-IQueryable-and-the-Entity-Framework-40.aspx我檢查了SQL事件探查器,他是正確的, IQueryable作爲查詢傳遞參數 – GrahamJRoy

0

您需要LINQ查詢才能返回較少的數據,如sql分頁中的sql分頁,如top函數或使用存儲過程進行手動查詢。在任何情況下,您都需要重新編寫查詢機制。這是我沒有使用EF的原因之一,因爲你看起來對代碼沒有太多的控制。

2

我會做回只是一組數據會被以下幾點:

var customers = (from x in Repository.Customers where <boolean statement> &&/|| <boolean statement select new {variableName = x.Name , ...).Take(<integer amount for amount of records you need>); 

所以例如:

var customers = (from x in _repository.Customers where x.ID == id select new {variableName = x.Name}).take(1000); 

然後遍歷這些結果得到的數據: (請記住,LINQ語句返回一個IQueryable)...

foreach (var data in customers) 
{ 
    string doSomething = data.variableName; //to get data from your query. 
} 

希望這可以幫助,而不是完全同樣的方法,但我發現在我的代碼中很方便

1

可能是因爲您的存儲庫中的Cusomters()方法正在執行GetAll()類的事情並首先獲取整個列表。這禁止LINQ和您的SQL Server創建智能查詢。

我不知道是否有對你的倉庫一個很好的解決辦法,但如果你會做這樣的事情:

using(var db = new RemoteServerContext()) 
{ 
    var custs = db.Customers.Where(...); 
} 

我認爲這將是快了很多。如果你的項目足夠小,你可以不使用倉庫。當然,你會失去一個抽象層,但對於小項目來說,這可能不是一個大問題。

另一方面,您可以將所有客戶加載到存儲庫中一次,直接使用結果集合(而不是填充列表的方法調用)。小心添加,刪除和修改客戶。