我正在开发一个使用Entity Framework的应用程序,但是我遇到了一些性能问题。 想象一下具有一些组合框和数据网格的用户界面(C#,WPF)。每次在组合框中选择一个值时,它都会更改数据在网格中显示的条件。 看起来实体框架在缓存方面没有我想象的那么灵活。由于条件已更改,底层sql将始终略有不同(=无EF缓存),每个单元更新将导致对数据库的请求。
有什么方法可以在本地缓存表(使用工作导航属性)并仍然使用linq进行选择等而不生成对数据库的任何请求?
也许实体框架从一开始就是一个糟糕的选择,但是使用那些生成的类和linq而不是手动编写很多类和sql非常方便。 (我仍然需要实现一些缓存。)
答案 0 :(得分:8)
有什么办法可以在本地缓存表格吗?
这是DbContext
默认执行的操作,您可以轻松使用该功能。以下是基本模式:
context.Products.Where(p => <some intial condion>).Load();
var dataSource = context.Product.Local.Where(p => <some flexible condition>);
请注意,在第2行中使用了Local
集合。这是一个DbSet
属性,它从上下文缓存中返回实体。
具有工作导航属性
由Load()
语句加载的任何相关实体将通过 relationship fixup 自动相互连接。因此,如果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。假设m:n
和Product
之间存在Manufacturer
关系,那么......
context.Products.Where().Load();
context.Manufacturers.Where().Load();
...不会填充product.Manufacturers
和manufacturer.Products
。应该通过Include
加载多对多关联:
context.Products.Where()
.Include(p => p.Manufacturers)
.Load();
答案 1 :(得分: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拥有数据,我就不需要触及&#39;上下文&#39;再次打到数据库。我现在可以和_allPeople一起玩,因为它自己的收藏已经分离了我需要的东西。当我想向我的前端WPF公开用户看到的内容时,他们会看到一个可观察的集合,我可以根据需要从我的缓存设置中更新。这是一个超简单的简单过程。通常,许多开发人员最终会执行整个存储库模式,其中他们有一个或多个项目,仅用于存储数据和执行CRUD操作。这通常是恕我直言的首选方法,因为您可以根据需要拼凑其他东西。