使用 Entity Framework 的基本 WPF – MVVM 项目教程






4.56/5 (6投票s)
这是“使用 Entity Framework 的基本 WPF – MVVM 项目教程”的替代方案。
引言
Richard Protzel 创建的教程是使用 WPF 中的 Entity Framework 的一个很好的例子,但是像大多数类似例子一样 - 它不是为扩展到大型数据集而设计的。
本文的想法是展示如何采用一个简单的例子,并通过放入 VirtualObservableCollection,最终得到一个可以扩展到几乎任何数据集大小的系统。
背景
这是在以下教程的扩展:https://codeproject.org.cn/Articles/873592/Tutorial-for-a-Basic-WPF-MVVM-Project-Using-Entity
使用 VirtualizingObservableCollection,请参见:https://codeproject.org.cn/Articles/874363/Virtualizing-Data-with-XAML
使用代码
放入 VirtualizingObservableCollection 非常简单,只需为数据定义一个提供程序,而不是“填充”列表,而是使用提供程序返回 VirtualizingObservableCollection,而不是返回填充的列表。
在本例中,我们有两个列表 - 一个是作者列表,另一个是该作者的书籍列表。
在我们的例子中,我们将使用分页系统创建一个作者提供程序。 这样,我们一次只加载小块,而不是每次都等待整个列表完全加载。
如果您已从第一篇文章 (Richards) 下载了该项目,我们将定义一个返回作者集合的提供程序 - 在 MainWindowViewModel.cs 中,我们需要添加
public class AuthorProvider : IPagedSourceProvider<Author>
{
private AuthorBookEntities _ctx = null;
public AuthorProvider(AuthorBookEntities ctx)
{
_ctx = ctx;
}
public int Count
{
get { return (from a in _ctx.Authors select a).Count(); }
}
public PagedSourceItemsPacket<Author> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
{
return new PagedSourceItemsPacket<Author>() { LoadedAt = DateTime.Now, Items = (from a in _ctx.Authors orderby a.AuthorName select a).Skip(pageoffset).Take(count) };
}
public int IndexOf(Author item)
{
return (from a in _ctx.Authors orderby a.AuthorName where a.AuthorName.CompareTo(item.AuthorName)<0 select a).Count();
}
public void OnReset(int count)
{
}
}
在我们的例子中,我们将实体上下文传递到构造函数中,因为我们需要它,然后我们实现 IPagedSourceProvider 约定,它是三个方法和一个属性:-
Count - 用于告知 ItemsSource 定义集合中有多少项。请注意,我们实际上并没有获取项目,而是只返回计数。
GetItemsAt - 在这种情况下,我们只需使用 Skip 和 Take 返回项目 - 基本上是取回一页数据 - 我们正在使用默认值,因此它一次取回大约 100 个项目。
IndexOf - 当项目未连接(即不在我们“看到”的页面中)时使用此方法,该方法由 Combo 选择系统使用 - 但很少使用...
OnReset - 我们不需要在这里做任何聪明的事情。一个空方法就足够了。
接下来,我们将原始 Authors List 替换为 VirtualizingObservableCollection,而不是填充该列表,而是返回该集合。
所以:
private void FillAuthors()
{
var q = (from a in ctx.Authors
select a).ToList();
this.Authors = q;
}
private List<Author> _authors;
public List<Author> Authors
{
get
{
return _authors;
}
set
{
_authors = value;
NotifyPropertyChanged();
}
}
被替换为
private void FillAuthors()
{
Authors = new VirtualizingObservableCollection<Author>(new PaginationManager<Author>(new AuthorProvider(this.ctx)));
}
private VirtualizingObservableCollection<Author> _authors;
public VirtualizingObservableCollection<Author> Authors
{
get
{
return _authors;
}
set
{
_authors = value;
NotifyPropertyChanged();
}
}
接下来,我们需要编写一个提供程序来访问作者的书籍 - 在这种情况下,我们需要传入两件事 - 实体上下文,以及我们要用来过滤书籍的作者。
在我们的例子中,我们通过构造函数传入这些,所以提供程序看起来像这样
public class BookProvider : IPagedSourceProvider<Book>
{
private AuthorBookEntities _ctx = null;
private Author _Author = null;
public BookProvider(AuthorBookEntities ctx, Author author)
{
_ctx = ctx;
_Author = author;
}
public int Count
{
get { return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Count(); }
}
public PagedSourceItemsPacket<Book> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
{
return new PagedSourceItemsPacket<Book>() { LoadedAt = DateTime.Now, Items = (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Skip(pageoffset).Take(count) };
}
public int IndexOf(Book item)
{
return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId && b.Title.CompareTo(item.Title)<0 orderby b.Title select b).Count() ;
}
public void OnReset(int count)
{
}
}
如您所见,它与 Authors 提供程序非常相似(除了它返回书籍!),唯一的区别是构造函数的额外参数 Author,以及在 Where 子句中使用该 Author 对象将其仅过滤为该作者。
接下来,我们需要替换“fillbooks”以使用提供程序,并将 List<Books> 替换为虚拟化集合,因此原始代码是
private void FillBook()
{
Author author = this.SelectedAuthor;
var q = (from book in ctx.Books
orderby book.Title
where book.AuthorId == author.AuthorId
select book).ToList();
this.Books = q;
}
private List<Book> _books;
public List<Book> Books
{
get
{
return _books;
}
set
{
_books = value;
NotifyPropertyChanged();
}
}
变为
private void FillBook()
{
Author author = this.SelectedAuthor;
this.Books = new VirtualizingObservableCollection<Book>(new PaginationManager<Book>(new BookProvider(this.ctx, author)));
}
private VirtualizingObservableCollection<Book> _books = null;
public VirtualizingObservableCollection<Book> Books
{
get
{
return _books;
}
set
{
_books = value;
NotifyPropertyChanged();
}
}
所以,我们的新 ViewModel 看起来像这样
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using AlphaChiTech.Virtualization;
namespace Wpf_EF_Mvvm_sample
{
/// <summary>
///
/// Before running this proejct please create the required tables from the CreateAuthorBook.sql file
/// and then update the connection string in app.config
/// note: if the following error message comes up after compiling the project
///
/// >>Error 1 No connection string named 'AuthorBookEntities' could be found in the application config file.<<
///
/// this error message can be ignored. The project should run fine.
/// </summary>
class MainWindowViewModel : INotifyPropertyChanged
{
public class AuthorProvider : IPagedSourceProvider<Author>
{
private AuthorBookEntities _ctx = null;
public AuthorProvider(AuthorBookEntities ctx)
{
_ctx = ctx;
}
public int Count
{
get { return (from a in _ctx.Authors select a).Count(); }
}
public PagedSourceItemsPacket<Author> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
{
return new PagedSourceItemsPacket<Author>() { LoadedAt = DateTime.Now, Items = (from a in _ctx.Authors orderby a.AuthorName select a).Skip(pageoffset).Take(count) };
}
public int IndexOf(Author item)
{
return (from a in _ctx.Authors orderby a.AuthorName where a.AuthorName.CompareTo(item.AuthorName)<0 select a).Count();
}
public void OnReset(int count)
{
}
}
public class BookProvider : IPagedSourceProvider<Book>
{
private AuthorBookEntities _ctx = null;
private Author _Author = null;
public BookProvider(AuthorBookEntities ctx, Author author)
{
_ctx = ctx;
_Author = author;
}
public int Count
{
get { return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Count(); }
}
public PagedSourceItemsPacket<Book> GetItemsAt(int pageoffset, int count, bool usePlaceholder)
{
return new PagedSourceItemsPacket<Book>() { LoadedAt = DateTime.Now, Items = (from b in _ctx.Books where b.AuthorId == _Author.AuthorId orderby b.Title select b).Skip(pageoffset).Take(count) };
}
public int IndexOf(Book item)
{
return (from b in _ctx.Books where b.AuthorId == _Author.AuthorId && b.Title.CompareTo(item.Title)<0 orderby b.Title select b).Count() ;
}
public void OnReset(int count)
{
}
}
AuthorBookEntities ctx = new AuthorBookEntities();
public MainWindowViewModel()
{
FillAuthors();
}
private void FillAuthors()
{
Authors = new VirtualizingObservableCollection<Author>(new PaginationManager<Author>(new AuthorProvider(this.ctx)));
}
private VirtualizingObservableCollection<Author> _authors;
public VirtualizingObservableCollection<Author> Authors
{
get
{
return _authors;
}
set
{
_authors = value;
NotifyPropertyChanged();
}
}
private Author _selectedAuthor;
public Author SelectedAuthor
{
get
{
return _selectedAuthor;
}
set
{
_selectedAuthor = value;
NotifyPropertyChanged();
FillBook();
}
}
private void FillBook()
{
Author author = this.SelectedAuthor;
this.Books = new VirtualizingObservableCollection<Book>(new PaginationManager<Book>(new BookProvider(this.ctx, author)));
}
private VirtualizingObservableCollection<Book> _books = null;
public VirtualizingObservableCollection<Book> Books
{
get
{
return _books;
}
set
{
_books = value;
NotifyPropertyChanged();
}
}
private Book _selectedBook;
public Book SelectedBook
{
get
{
return _selectedBook;
}
set
{
_selectedBook = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
现在,我们需要做的就是引导管理器,所以在 MainWindow.xaml.cs 中,我们扩展构造函数如下
public MainWindow()
{
if (!VirtualizationManager.IsInitialized)
{
VirtualizationManager.Instance.UIThreadExcecuteAction = (a) => Dispatcher.Invoke(a);
new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Background, delegate(object s, EventArgs a) { VirtualizationManager.Instance.ProcessActions(); }, this.Dispatcher).Start();
}
InitializeComponent();
}
就是这样 - 我们完成了! 现在我们可以放心,如果我们有三个或三十万作者/书籍,系统将以相似的性能响应。
关注点
VirtualizingObservableCollection 可以在 nuget 上找到 - 请参阅 https://nuget.net.cn/packages/VirtualizingObservableCollection/
欢迎访问 https://alphachitech.wordpress.com/ 获取更多信息和更新。
历史
初始版本。 感谢 Richard 提供如此干净的示例!