views:

6967

answers:

7

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.

  • 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?

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?

+1  A: 

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.

NotDan
I'll try with the XAML. But it's difficult to get over the fact that the XAML editors are stone-age (no intellisense in Blend!!??), and that the XAML syntax is so god-awful ugly. Yeccch.
Ken Smith
Editing XAML always feels to me like eating raw broccoli, with no dip. Presumably it's good for me, but it tastes like I'm eating a tree.
Ken Smith
+2  A: 

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.

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

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

familyOC = new ObservableCollection<Family>(families);
NotDan
Thanks. I'd added that in troubleshooting a different error, and never took it out.
Ken Smith
+4  A: 

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

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

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

Some ideas for this are in here

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

This is likely the big piece I was missing, and it seems valuable. Thanks.
Ken Smith
+2  A: 

I understand where you're coming from. This article by Josh Smith helped 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!

amaca
A: 

Another tool could be BindableLINQ

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

TFD
+1  A: 

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

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.

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.

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.

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.

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

A: 

The WPF Application Framework (WAF) provides the BookLibrary sample application which shows how to use WPF and the Entity Framework together.

jbe