使用Entity Framework进行WPF数据绑定的最佳实践。

53

我正在构建我的第一个真正的WPF应用程序(即第一个打算供他人使用的应用程序),而我仍在思考在WPF中最好的做事方式。这是一个相当简单的数据访问应用程序,使用了仍然相当新的Entity Framework,但我没有找到很多关于如何最好地将这两种技术(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?

这就是我的情况。有什么建议吗?我是否遗漏了任何重要部分?有什么我应该真正考虑改变的事情吗?


vs2010/fw4.0:看起来ItemsSource必须绑定到familyCVS.View(而不是familyCVS)。 - Eric Bole-Feysot
6个回答

19

这很可能是我缺少的重要部分,看起来很有价值。谢谢。 - Ken Smith
这个答案末尾的链接已经失效了。 - Cylindric
1
是的,但是Wayback Machine仍然有一份副本(也考虑捐赠!)。 - sorrell
1
网络存档机现在对我来说显示“由于robots.txt文件,无法爬取或显示页面。” - Sam Hobbs

7

此外,我认为您不需要在这里执行ToList()。 我相信ObservableCollection()接受一个IEnumerable,而Families已经是它了。 如果您执行ToList,然后将其传递给ObservableCollection,那么我认为您将两次循环遍历所有记录。

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

相比之下,尝试使用以下方法,速度应该会更快:
familyOC = new ObservableCollection<Family>(families);

谢谢。我在解决另一个错误时添加了它,但从未将其删除。 - Ken Smith

5

我理解你的想法。这篇由Josh Smith撰写的文章帮助我改变(或开始改变)思维方式,让你可以从WPF中获得一些好处,而不是把它看作是一个奇怪、阻碍、难以调试和不友好的框架!


4

我的建议是,如果可能的话,使用Expression Blend来设计界面,而不是使用Code Behind或Visual Studio设计器,这将节省您大量时间。另外,请尝试重新考虑使用C#而不是xaml。如果按照“WPF Way”进行操作,xaml并不难看。通常情况下,当我认为使用Code Behind比xaml更容易时,那是因为我使用的方法不正确,需要重新思考如何最好地与WPF/xaml配合使用。一旦您习惯了它,xaml就很棒。我还使用过实体框架,但它还不太好。我更喜欢NHibernate。


我会尝试使用XAML。但很难克服XAML编辑器的落后(Blend中没有智能感知!?),以及XAML语法非常丑陋的事实。呸。 - Ken Smith
7
编辑XAML对我来说总是感觉像是吃生花椰菜一样,没有调料。大概它对我有好处,但尝起来就像在吃树。 - Ken Smith

2

我从我的博客上跟随这个链接,想提到我在使用EF时发现的另一件事情。虽然有点离题,但并不完全。

我注意到在使用.Include时,EF存在一些性能问题。微软在他们的网站上解释了原因,所以我实际上已经开始将大部分代码转换为使用.Load方法。

由于这是一项繁琐的任务,而且我找不到其他方法来完成它... 我创建了自己的方法叫做“IncludeByRoundTrip”。它的作用是获取一个对象路径并确保整个路径被加载。最终结果与使用include相同,但在幕后,我只是调用对象图中所有属性的Load方法。

这类似于执行类似order.Load("Customer.Address")的操作,如果存在这样的机制。无论如何,请查看我的博客,并让我知道你的发现。我很想知道其他人是否注意到了使用Include时的减速情况,以及你是否有其他攻击该情况的方法。

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

再次抱歉这有点离题,但我期待你们的回复。


1

另一个工具可以是BindableLINQ

Bindable LINQ是一组扩展LINQ的工具,它为标准LINQ查询添加了数据绑定和更改传播功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接