将实体框架与 WPF 数据绑定结合使用的最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/628437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 20:06:24  来源:igfitidea点击:

Best practices for using the Entity Framework with WPF DataBinding

wpfentity-framework

提问by Ken Smith

I'm in the process of building my first real WPF application (i.e., the first intended to be used by someone besides me), and I'm still wrapping my head around the best way to do things in WPF. It's a fairly simple data access application using the still-fairly-new Entity Framework, but I haven't been able to find a lot of guidance online for the best way to use these two technologies (WPF and EF) together. So I thought I'd toss out how I'm approaching it, and see if anyone has any better suggestions.

我正在构建我的第一个真正的 WPF 应用程序(即第一个打算供除我之外的人使用的应用程序),并且我仍在思考在 WPF 中做事的最佳方式。这是一个使用仍然相当新的实体框架的相当简单的数据访问应用程序,但我无法在网上找到大量指南,以了解将这两种技术(WPF 和 EF)结合使用的最佳方式。所以我想我会扔掉我是如何接近它的,看看是否有人有更好的建议。

  • I'm using the Entity Framework with SQL Server 2008. The EF strikes me as both much more complicated than it needs to be, and not yet mature, but Linq-to-SQL is apparently dead, so I might as well use the technology that MS seems to be focusing on.

  • This is a simple application, so I haven't (yet) seen fit to build a separate data layer around it. When I want to get at data, I use fairly simple Linq-to-Entity queries, usually straight from my code-behind, e.g.:

    var families = from family in entities.Family.Include("Person")
               orderby family.PrimaryLastName, family.Tag
               select family;
    
  • Linq-to-Entity queries return an IOrderedQueryable result, which doesn't automatically reflect changes in the underlying data, e.g., if I add a new record via code to the entity data model, the existence of this new record is not automatically reflected in the various controls referencing the Linq query. Consequently, I'm throwing the results of these queries into an ObservableCollection, to capture underlying data changes:

    familyOC = new ObservableCollection<Family>(families.ToList());
    
  • I then map the ObservableCollection to a CollectionViewSource, so that I can get filtering, sorting, etc., without having to return to the database.

    familyCVS.Source = familyOC;
    familyCVS.View.Filter = new Predicate<object>(ApplyFamilyFilter);
    familyCVS.View.SortDescriptions.Add(new System.ComponentModel.SortDescription("PrimaryLastName", System.ComponentModel.ListSortDirection.Ascending));
    familyCVS.View.SortDescriptions.Add(new System.ComponentModel.SortDescription("Tag", System.ComponentModel.ListSortDirection.Ascending));
    
  • I then bind the various controls and what-not to that CollectionViewSource:

    <ListBox DockPanel.Dock="Bottom" Margin="5,5,5,5" 
        Name="familyList" 
        ItemsSource="{Binding Source={StaticResource familyCVS}, Path=., Mode=TwoWay}" 
        IsSynchronizedWithCurrentItem="True" 
        ItemTemplate="{StaticResource familyTemplate}" 
        SelectionChanged="familyList_SelectionChanged" />
    
  • When I need to add or delete records/objects, I manually do so from both the entity data model, and the ObservableCollection:

    private void DeletePerson(Person person)
    {
        entities.DeleteObject(person);
        entities.SaveChanges();
        personOC.Remove(person);
    }
    
  • I'm generally using StackPanel and DockPanel controls to position elements. Sometimes I'll use a Grid, but it seems hard to maintain: if you want to add a new row to the top of your grid, you have to touch every control directly hosted by the grid to tell it to use a new line. Uggh. (Microsoft has never really seemed to get the DRY concept.)

  • I almost never use the VS WPF designer to add, modify or position controls. The WPF designer that comes with VS is sort of vaguely helpful to see what your form is going to look like, but even then, well, not really, especially if you're using data templates that aren't binding to data that's available at design time. If I need to edit my XAML, I take it like a man and do it manually.

  • Most of my real code is in C# rather than XAML. As I've mentioned elsewhere, entirely aside from the fact that I'm not yet used to "thinking" in it, XAML strikes me as a clunky, ugly language, that also happens to come with poor designer and intellisense support, and that can't be debugged. Uggh. Consequently, whenever I can see clearly how to do something in C# code-behind that I can't easily see how to do in XAML, I do it in C#, with no apologies. There's been plenty written about how it's a good practice to almost never use code-behind in WPF page (say, for event-handling), but so far at least, that makes no sense to me whatsoever. Why should I do something in an ugly, clunky language with god-awful syntax, an astonishingly bad editor, and virtually no type safety, when I can use a nice, clean language like C# that has a world-class editor, near-perfect intellisense, and unparalleled type safety?

  • 我在 SQL Server 2008 中使用实体框架。EF 给我的印象是比它需要的复杂得多,而且还不成熟,但 Linq-to-SQL 显然已经死了,所以我不妨使用该技术那个 MS 似乎正在关注。

  • 这是一个简单的应用程序,所以我还没有(还)认为适合围绕它构建一个单独的数据层。当我想获取数据时,我使用相当简单的 Linq-to-Entity 查询,通常直接来自我的代码隐藏,例如:

    var families = from family in entities.Family.Include("Person")
               orderby family.PrimaryLastName, family.Tag
               select family;
    
  • Linq-to-Entity 查询返回一个 IOrderedQueryable 结果,它不会自动反映底层数据的变化,例如,如果我通过代码向实体数据模型添加一条新记录,这条新记录的存在不会自动反映在引用 Linq 查询的各种控件。因此,我将这些查询的结果放入 ObservableCollection 中,以捕获底层数据更改:

    familyOC = new ObservableCollection<Family>(families.ToList());
    
  • 然后我将 ObservableCollection 映射到 CollectionViewSource,这样我就可以进行过滤、排序等操作,而无需返回数据库。

    familyCVS.Source = familyOC;
    familyCVS.View.Filter = new Predicate<object>(ApplyFamilyFilter);
    familyCVS.View.SortDescriptions.Add(new System.ComponentModel.SortDescription("PrimaryLastName", System.ComponentModel.ListSortDirection.Ascending));
    familyCVS.View.SortDescriptions.Add(new System.ComponentModel.SortDescription("Tag", System.ComponentModel.ListSortDirection.Ascending));
    
  • 然后我将各种控件和其他内容绑定到 CollectionViewSource:

    <ListBox DockPanel.Dock="Bottom" Margin="5,5,5,5" 
        Name="familyList" 
        ItemsSource="{Binding Source={StaticResource familyCVS}, Path=., Mode=TwoWay}" 
        IsSynchronizedWithCurrentItem="True" 
        ItemTemplate="{StaticResource familyTemplate}" 
        SelectionChanged="familyList_SelectionChanged" />
    
  • 当我需要添加或删除记录/对象时,我从实体数据模型和 ObservableCollection 手动执行此操作:

    private void DeletePerson(Person person)
    {
        entities.DeleteObject(person);
        entities.SaveChanges();
        personOC.Remove(person);
    }
    
  • 我通常使用 StackPanel 和 DockPanel 控件来定位元素。有时我会使用网格,但似乎很难维护:如果您想在网格顶部添加新行,则必须触摸网格直接托管的每个控件以告诉它使用新行。呃。(微软似乎从未真正理解 DRY 概念。)

  • 我几乎从不使用 VS WPF 设计器来添加、修改或定位控件。VS 附带的 WPF 设计器对查看表单的外观有点帮助,但即便如此,也不是真的,特别是如果您使用的数据模板未绑定到可用的数据设计时间。如果我需要编辑我的 XAML,我会像人一样对待它并手动完成。

  • 我的大部分真实代码都是用 C# 编写的,而不是 XAML。正如我在别处提到的,完全抛开我还没有习惯在其中“思考”这一事实,XAML 给我的印象是一种笨拙、丑陋的语言,这也恰好伴随着糟糕的设计器和智能感知支持,并且无法调试。呃。因此,每当我可以清楚地看到如何在 C# 代码隐藏中执行某些在 XAML 中无法轻松执行的操作时,我都会在 C# 中执行此操作,而无需道歉。已经有很多关于在 WPF 页面中几乎从不使用代码隐藏(例如,用于事件处理)的好做法的文章,但至少到目前为止,这对我来说毫无意义。当我可以使用像 C# 这样的漂亮、干净的语言,它有一个世界级的编辑器,近乎完美智能感知,

So that's where I'm at. Any suggestions? Am I missing any big parts of this? Anything that I should really think about doing differently?

所以这就是我所在的位置。有什么建议?我错过了其中的任何重要部分吗?我真的应该考虑以不同的方式做些什么吗?

采纳答案by user73993

You need to implement a repository pattern to seperate WPF concerns from EF

您需要实现存储库模式以将 WPF 关注点与 EF 分开

Then you can use generics to reduce the complexity of the EF to CollectionViewSource handling

然后你可以使用泛型来降低 EF 到 CollectionViewSource 处理的复杂性

A well designed repository should reduce code levels and enable any ORM to be substituted (required for decent testing)

一个设计良好的存储库应该降低代码级别并允许任何 ORM 被替换(需要进行体面的测试)

Some ideas for this are in here

一些想法在这里

http://blog.nicktown.info/2008/12/10/using-a-collectionviewsource-to-display-a-sorted-entitycollection.aspx

http://blog.nicktown.info/2008/12/10/using-a-collectionviewsource-to-display-a-sorted-entitycollection.aspx

回答by NotDan

Also, I dont think you need to do a ToList() here. I believe ObservableCollection() takes an IEnumerable which families already is. If you do a ToList, and then pass that to the ObservableCollection, then I think you will loop through all your records twice.

另外,我认为您不需要在这里执行 ToList() 。我相信 ObservableCollection() 需要一个 IEnumerable 家庭已经是。如果您执行 ToList,然后将其传递给 ObservableCollection,那么我认为您将遍历所有记录两次。

familyOC = new ObservableCollection<Family>(families.ToList());

Instead, try this, which should be a bit faster:

相反,试试这个,它应该快一点:

familyOC = new ObservableCollection<Family>(families);

回答by amaca

I understand where you're coming from. This article by Josh Smithhelped me change (or start to change) mindset so that you can some benefit from WPF rather than seeing it as a weird, obstructive, hard-to-debug and unfriendly framework!

我明白你来自哪里。Josh Smith 的这篇文章帮助我改变(或开始改变)思维方式,这样您就可以从 WPF 中受益,而不是将其视为一个奇怪、有障碍、难以调试和不友好的框架!

回答by NotDan

My recommendations is, if possible use Expression Blend for designing your interface, instead of Code Behind and instead of using the Visual Studio designer, it will save you lots of time. Also try to rethink using the C# instead of xaml. Xaml isn't so ugly if you are do it the "WPF Way". Often times, when I think it is easier to use the code behind instead of xaml, it is because I'm doing it the wrong way and need to rethink how it should best work with WPF/xaml. Xaml is great once you get used to it. I have also used entity framework which is not too great yet. I prefer NHibernate.

我的建议是,如果可能的话,使用 Expression Blend 来设计你的界面,而不是使用代码隐藏,而不是使用 Visual Studio 设计器,它会为你节省大量时间。还尝试重新考虑使用 C# 而不是 xaml。如果您按照“WPF 方式”进行操作,Xaml 并不会那么难看。很多时候,当我认为使用背后的代码而不是 xaml 更容易时,那是因为我做错了,需要重新考虑它应该如何最好地与 WPF/xaml 配合使用。一旦你习惯了 Xaml,它就很棒。我还使用了实体框架,但还不是很好。我更喜欢NHibernate。

回答by NotDan

I followed this link from my blog and wanted to mention something else I found with EF. Kind of off topic, but not totally.

我从我的博客中点击了这个链接,并想提一下我在 EF 中发现的其他东西。有点偏离主题,但不完全是。

I have noticed some crazy performance issues with EF when using the .Include. MS explains why in an article on their website so I have actually started shifting most of my code to use the .Load method instead.

在使用 .Include 时,我注意到 EF 存在一些疯狂的性能问题。MS 在他们网站上的一篇文章中解释了为什么我实际上已经开始将我的大部分代码转换为使用 .Load 方法。

Because it is a tedious task to do and because I couldn't find another way to do it... I created my own method called "IncludeByRoundTrip". What it does is takes an object path and ensures the entire path is loaded. The end result is the same as when using include however behind the scenes, I am simply calling Load on all the properties in the object graph.

因为这是一项乏味的任务,而且因为我找不到其他方法来做到这一点......我创建了自己的方法,称为“IncludeByRoundTrip”。它所做的是获取一个对象路径并确保加载整个路径。最终结果与使用 include 时相同,但是在幕后,我只是在对象图中的所有属性上调用 Load。

It would be similar to doing something like order.Load("Customer.Address") if such a mechanism existed. Either way, check it out at my blog and let me know your findings. I would be curious to see if others have noticed slowdowns using Include and if you have other approaches at attacking the situation.

如果存在这样的机制,它类似于执行 order.Load("Customer.Address") 之类的操作。无论哪种方式,请在我的博客上查看并告诉我您的发现。我很想知道其他人是否注意到使用 Include 速度变慢,以及您是否有其他方法来解决这种情况。

There is more info on my solution at: http://blog.nicktown.info/2009/07/27/method-to-load-an-entire-object-graph-using-adonet-entity-framework.aspx.

有关我的解决方案的更多信息,请访问: http://blog.nicktown.info/2009/07/27/method-to-load-an-entire-object-graph-using-adonet-entity-framework.aspx

Again, sorry this was a bit off-topic, but I look forward to your responses.

再次抱歉,这有点跑题了,但我期待您的回复。

回答by TFD

Another tool could be BindableLINQ

另一个工具可能是BindableLINQ

Bindable LINQ is a set of extensions to LINQ that add data binding and change propagation capabilities to standard LINQ queries

可绑定的 LINQ 是 LINQ 的一组扩展,用于向标准 LINQ 查询添加数据绑定和更改传播功能