2017-01-24 73 views
0

我正在開發一個使用實體框架的應用程序,但我有一些性能問題。 想象一下帶有一些組合框和數據網格的用戶界面(C#,WPF)。每次在組合框中選擇一個值時,它都會更改要在網格中顯示的數據的條件。 看起來像實體框架並不像我想到的那樣靈活。由於條件發生了變化,底層的sql將總是略有不同(=沒有EF緩存),並且每個單元更新都會導致對數據庫的請求。實體框架和數據庫表的本地緩存

有什麼辦法可以在本地緩存表(具有工作導航屬性),仍然使用linq進行選擇等,而不會產生任何數據庫請求?

  • 表格很小,所以我不期望任何性能問題。 (I 猜測本地排序等,否則可能是一個問題,因爲數據庫索引不使用)表,並從數據庫連接中分離出 ,這可能沒問題。
  • 我不允許安裝任何第三方工具。

也許實體框架從一開始就是一個糟糕的選擇,但使用那些生成的類和linq而不是手動編寫大量的類和sql非常方便。 (我仍然必須實現一些緩存。)

+0

我想你應該顯示一些你的代碼。聽起來像一個簡單的ToList()在將它提交給組合框和數據網格(有效地從EF linq切換到內存中的linq)之前,可能會對解決您的問題有很大的幫助。 –

+0

獲取對象後,不必使用EntityFramework(EF)。它僅僅是一個獲取數據的對象模型層。由於EF將數據庫客觀化爲數據檢索,因此LINQ與EF分離。您可以簡單地設置一個存儲庫模式,您可以在設置的持續時間內執行檢索方法並緩存這些對象。我可以很容易地做var ls = new List {「a」,「b」,「c」,「d」,「e」}。 (2).Take(3).ToList()。ForEach(x => Console.WriteLine($「I have {x}」)); EF對Linq沒有任何影響,它的用途是,LINQ是一個查詢擴展方法的框架,EF是用於數據複製的。 – djangojazz

+0

另外你在這裏碰到了一個問題:'但是使用那些生成的類真的很方便。 T4模板(產生這些類的東西)是EF真正照亮了IMHO的地方。如果需要,您可以覆蓋寫入代碼的代碼,也可以生成生成的數據。 EG:假設我有一個桌子的顏色:它只存儲一個ID和值「紅色」,「藍色」,「黃色」。檢索這將是一種愚蠢的,所以我甚至可以破解T4來生成數據庫中的枚舉生成對象。由於數據庫更改需要重新運行T4模板,可能不適合生產。 – djangojazz

回答

2

有沒有什麼辦法可以在本地緩存表?

這是默認情況下DbContext所做的,並且您可以輕鬆使用該功能。以下是基本模式:

context.Products.Where(p => <some intial condion>).Load(); 
var dataSource = context.Product.Local.Where(p => <some flexible condition>); 

請注意,在第2行使用Local集合。這是一個DbSet屬性,它從上下文的緩存中返回實體。

與合作導航屬性

了由Load()聲明加載將由關係修正自動連接到彼此的任何相關實體。因此,如果一個Product具有收藏Components,你可以這樣做:

context.Components.Where(c => <some intial condion>).Load(); 

如果這是加載了上述裝載產品的所有組件,你會看到他們的Components收集現在填充。

組合這兩個步驟的另一種方法是:

context.Products.Where(p => <some intial condion>) 
     .Include(p => p.Components) 
     .Load(); 

如果有很多相關的表,你必須要找到個人Load報表和Load語句與Include之間的平衡,因爲許多Includes在一個聲明中可能會碰到的性能。

並且仍然使用LINQ進行選擇

如上所示:柔性狀態。

不會產生數據庫

任何請求。如果你將永遠解決Local集合而已,這些語句將永遠不會查詢數據庫。但是,解決導航屬性可能仍會導致延遲加載。如果你這樣做......

context.Products.Where().Load(); 
context.Components.Where().Load(); 

...這確實填充product.Components集合,但由於裝載,而context.Products.Include(p => p.Components)並不標誌着他們。所以在第一種情況下,尋址product.Components將觸發延遲加載。同樣,解決實體未加載的導航屬性也會觸發延遲加載。所以要絕對確保沒有數據庫的交互被觸發時,應禁用延遲加載,無論是...

context.Configuration.LazyLoadingEnabled = false; 

......或者......

context.Configuration.ProxyCreationEnabled = false; 

後者選項強制EF到創建不能延遲加載的簡單POCO對象。

因此,使用這些技術,您可以將您的上下文用作連接實體的本地緩存。對於上下文應該是短暫的規則,這是一個例外。

一個警告

關係修正doesn't work for many-to-many associations。假設有ProductManufacturer,然後之間的關係m:n ...

context.Products.Where().Load(); 
context.Manufacturers.Where().Load(); 

......不會填充product.Manufacturersmanufacturer.Products。多對多的關聯應該由Include加載:

context.Products.Where() 
     .Include(p => p.Manufacturers) 
     .Load(); 
0

讓我對此稍微解釋一下,因爲我也有我在WPF中使用的生產應用程序,並遵循MVVM模式。你不可以,如果你不知道我在說什麼,我建議你。假設我有一個擁有人員表的數據庫表,它只有三列:PersonId,FirstName,LastName。我目前只有兩排,我的名字和我妻子的名字。我只想檢索一次數據,但後來我可能想要修改它。當然,這是一個簡單的例子:

XAML:

<StackPanel> 
    <DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
     <DataGridTextColumn Header="PersonId" Binding="{Binding PersonId}" /> 
     <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" /> 
     <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" /> 
     </DataGrid.Columns> 
    </DataGrid> 
    <TextBox Text="{Binding Text}" /> 
    <Button Command="{Binding CommandGetFirstName}" Height="30" Content="Get By First Name Above" /> 
</StackPanel> 

這是使用MVVM勢必所以我MainViewModel會是這樣:

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Input; 

namespace WPFCSharpTesting 
{ 
    public class MainWindowViewModel : INotifyPropertyChanged 
    { 
    private string _text; 

    public string Text 
    { 
     get { return _text; } 
     set 
     { 
     _text = value; 
     OnPropertyChanged(nameof(Text)); 
     } 
    } 

    private ObservableCollection<tePerson> _people; 

    public ObservableCollection<tePerson> People 
    { 
     get { return _people; } 
     set 
     { 
     _people = value; 
     OnPropertyChanged(nameof(People)); 
     } 
    } 

    private readonly List<tePerson> _allPeople; 


    public MainWindowViewModel() 
    { 
     Text = "Brett";  
     using (var context = new TesterEntities()) 
     { 
     _allPeople = context.tePerson.ToList(); 
     } 

     People = new ObservableCollection<tePerson>(_allPeople); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(String info) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info)); 
    } 

    DelegateCommand _CommandGetFirstName; 

    public ICommand CommandGetFirstName 
    { 
     get 
     { 
     if (_CommandGetFirstName == null) 
     { 
      _CommandGetFirstName = new DelegateCommand(param => this.CommandGetByFirstNameExecute()); 
     } 
     return _CommandGetFirstName; 
     } 
    } 

    private void CommandGetByFirstNameExecute() 
    { 
     var newitems = _allPeople.Exists(x => x.FirstName == Text) ? _allPeople.Where(x => x.FirstName == Text)?.ToList() : _allPeople; 
     People = new ObservableCollection<tePerson>(newitems); 
    } 

這裏的關鍵部分是什麼在發生的事情我構造函數。我正在使用只讀變量_allPeople,它是私有的,並將信息存儲在那裏,以便稍後處理。一旦_allPeople有數據,我不需要再次觸摸'上下文'來觸發數據庫。我現在可以用_allPeople猴子,因爲它的自己的收藏已脫離我需要的東西。當我想向我的前端WPF展示用戶看到的內容時,他們將看到一個可觀察的集合,我可以根據需要從緩存設置更新該集合。這是簡化它的一個超級簡單。通常情況下,許多開發人員最終都在完成整個存儲庫模式,他們只有一個或多個項目涉及到存儲數據和執行CRUD操作。這通常是一個首選的方法恕我直言,因爲你可以根據需要拼湊其他東西。