Damien Boissat Damien Boissat - 11 months ago 60
C# Question

Easy binding of dbcontext with or without WCF

Dear Programmers,

I have a question that I can't succeed to figure out, it is more related to how should I design the application, and until now I have always overcome this issue by writing a lot of code.

I have to design a silverlight application connected to a database, and I want to use a WCF service between these 2 for many reasons.

If I use the WCF service I will loose the dbcontext structure which is very great when we want to use CollectionViewSource with related fields to bind the xaml controls.

By example, lets say we have simple entities like that, generated from the edmx file: Resistor and ResistorCategories

public partial class ResistorCategories
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorCategories()
this.Name = "New";
this.ResistorsSet = new HashSet<ResistorsSet>();

public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorsSet> ResistorsSet { get; set; }

public partial class ResistorsSet
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorsSet()
this.ResistorStockEntriesSet = new HashSet<ResistorStockEntriesSet>();

public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Resistance { get; set; }
public Nullable<decimal> PowerRating { get; set; }
public Nullable<decimal> Price { get; set; }
public Nullable<decimal> Tolerance { get; set; }
public string SupplierCode { get; set; }
public string ManufacturerCode { get; set; }
public int ResistorCategories_Id { get; set; }
public Nullable<int> Suppliers_Id { get; set; }
public Nullable<int> Manufacturers_Id { get; set; }

public virtual ResistorCategories ResistorCategories { get; set; }
public virtual Suppliers Suppliers { get; set; }
public virtual Manufacturers Manufacturers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorStockEntriesSet> ResistorStockEntriesSet { get; set; }

In the xaml, it is very easy to place 2 datagrid and see the list of resistors which belong to a categorie:

In the xaml:

Then I bind each datagrid to its corresponding CollectionViewSource:

ItemsSource="{Binding Source={StaticResource resistorCategoriesViewSource}, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource resistorCategoriesResistorsViewSource}, UpdateSourceTrigger=PropertyChanged}"

And in the code behind file, with lazyloading enabled:

System.Windows.Data.CollectionViewSource resistorCategoriesViewSource =
resistorCategoriesViewSource.Source = _context.ResistorCategoriesSet.Local;

So it can't be easier to display records with their related fields, but in this case the xaml client is querying the database directly and this is what I want to avoid with a WCF service, + adding some standardization and security.

With a WCF, first I have to generate the POCO objects with EF Power Tool, because it can't return IEnumerable by example because of its virtual property (even if I deactivate lazyloading and proxy).
Then I have to query and store in separated collections the POCO objects:
ObservableCollection && ObservableCollection

Then I bind each datagrid to its collection and monitor the selecteditem event to manually change the collection displayed in the datagrid for the resistors!

So I guess there is a solution to use a WCF together with the simplicity of the dbcontext!
Please help me! Thanks in advance

Answer Source

So I have found a solution finally, it was worth searching even if the solution is easy: using WCF Data Services.

It's possible to query the database with all related entities and bind them in the xaml:

As example, consider an entity called ItemsCategory with a list of Items referring to it via a ItemCategory_Id field, and 2 others entities called ItemPrices and ItemImages referring Items via an Item_Id field:

  • ItemsCategory
    • Item
      • ItemPrices
      • ItemImages

It's not perfect but it looks like that.

So then if you want to place a datagrid for the ItemsCategory and see the related Items in a second datagrid, and the related ItemPrices and ItemImages in a third and fourth datagrid in your xaml you defined it like that:

    <CollectionViewSource x:Key="itemsCategoriesSetViewSource" d:DesignSource="{d:DesignInstance SvcCatalogDatabase:ItemsCategories, CreateList=True}"/>
    <CollectionViewSource x:Key="itemsCategoriesSetItemsSetViewSource" Source="{Binding Items, Source={StaticResource itemsCategoriesSetViewSource}}"/>
    <CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsPricesSetViewSource" Source="{Binding ItemsPrices, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>
    <CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsImagesSetViewSource" Source="{Binding ItemsImages, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>

To load the entities you first define a collections like that:

public DataServiceCollection<ItemsCategories> ItemsCategoriesTracked { get; set; }
public CollectionViewSource ItemsCategories_CVSrc { get; set; }       

And to query the database you just need to query ItemCategories with related entities:

ItemsCategories_CVSrc.Source = null;

Of course you have defined a handler for the LoadCompleted event of the DataServiceCollection:

ItemsCategoriesTracked.LoadCompleted += new EventHandler<LoadCompletedEventArgs>((sender, e) => DataServiceCollection_LoadCompleted<ItemsCategories>(sender, e, ItemsCategories_CVSrc));

private void DataServiceCollection_LoadCompleted<T>(object sender, LoadCompletedEventArgs e, CollectionViewSource target)
        if (e.Error == null)
            if ((sender as DataServiceCollection<T>).Continuation != null)
                (sender as DataServiceCollection<T>).LoadNextPartialSetAsync();
                target.Source = (sender as DataServiceCollection<T>);
            MessageBox.Show(string.Format("{0}: An error has occured: {1}", typeof(T).Name, e.Error.Message));

And you've linked the CollectionViewSource to the one defined in the XAML:

ItemsCategories_CVSrc = (CollectionViewSource)this.Resources["itemsCategoriesSetViewSource"];

VoilĂ ! Hope it helps someone!