For more information (here)


When it comes to extending the entity classes and adding some features not supported (yet) by RIA services, I had to add a lot of boilerplate code, this is because it’s impossible to change the behaviour of the base class (Entity), so the only way was to add interfaces to support the required behaviour. Hence, in ObservableCollection library you’ll find ICollectionChanged interface, which should be implemented by any Entity that would expose some ObservableEntityCollection(s).
<!--more-->
This is of course a huge burden in this solution, for example to wrap few entity collections with ObservableEntityCollection which supports tracking of child changes, the developer should repeat the same task of writing the same code over and over again, sometimes the cost will be hundreds of lines of code!!

Then I came up with this idea of code snippets, let's ship the library with some code snippets to generate the required code, the generated code of course will be the default implementation of some interfaces. Certainly it’s better than writing the same code every time, but it is still not elegant and it’s beyond the usual purpose of code snippets, we usually write snippets for simple tasks like: generating a template for dependency properties, but code snippets are not suitable for generating the default implementation of some interfaces.

This problem was a result of the single inheritance model in C#, while in C++ the solution would be simpler by creating a new base class with default implementation, but unfortunately the multiple-inheritance is not supported in C #. This problem made me appreciate for the first time in my life the MI model used in C++.

It seemed like there is no way to avoid the most difficult option, creating a Visual studio add-in, that will enumerate the entities in Silverlight class library, the user then would be able to mark some entities as Observable Collection aware or duplicate aware entities, with one click the boilerplate code (most of it) would be generated automatically through some T4 templates.

Creating this add-in wasn’t an easy task, it should be smart enough to read the mapping file (which entities support which interfaces), and should be able to load the assemblies in new AppDomain and clear it when the plug in is closed, this particular feature was very difficult to implement and it still requires further enhancements.

So let's see how to put everything together … 

What is provided in the OEC (ObservableEntityCollection)

For more information why this project was created in the first place, make sure to read this article (Here)
The main feature is to: wrap the EntityCollection class generated by RIA services with ObservableEntityCollection class, which can be used in MVVM models, let’s define some terms:

  1. OEC -Parent: An entity hosting ObservableEntityCollection(s), like Person entity hosting ObservableContacts collection.
  2. OEC -Child: an ObservableEntityCollection, like ObservableContacts that wraps Contacts EntityCollection.
Current features of OEC:
  1. No need to use composition attribute, which means that the EntitySets would be generated for both parent and children entities.
  2. ObservableEntityCollection would track changes in OEC-Parent and OEC-Child entities, any change (change parent attribute, change child attribute, adding child, removing child) would cause the parent to be marked as dirty, this state is represented by a new property (IsEntityChanged in the OEC-Parent).
  3. ObservableEntityCollection can undo all changes reverting both OEC-Parent and OEC-Child to their original state, the restore operation includes deleting the new added rows and restoring the removed ones.
  4. ObservableEntityCollection can clear the dirty status through (ClearDirty() in OEC-Parent), which comes in handy after a successful save operation.
  5. OEC-Child can be marked as Duplicate aware, by defining a general comparer function. This feature is very useful when the entity should be unique but based on some business rules not implemented in the database (for example the business rule requires both fields sender and receiver to be unique) but not implemented in the database for reason like old database or changed business rules. The comparer function also allows complicated scenarios that are difficult to be implemented in the database.
  6. OEC-Child duplicate aware entity provides IsDuplicate property that could be used in MVVM and Prism applications to give the user a visual alert, such as changing the row background.
  7. ObservableEntityCollection has IsDuplicate property which returns true if any OEC-Child entity has a duplicate.
  8. OEC-Child duplicate aware entity provides Duplicates properties, which returns an ObservableCollection of matched entities according to the user-defined comparer function; this also can be useful in designing the UX in business applications.
  9. The Visual studio plugin is called OECModeler, to generate the boilerplate code in OCE_GenerateCode folder in the solution, this plugin would save mapping (which parent contains wrapped collection) in a XML file given the name: project.ocemap.
  10. Some extensions are provided to facilitate the serialization operation; this feature is covered in this article (here). Briefly, when an Entity is passed as a parameter in RIA Invoke operation, the child entities are not serialized as well, so I came up with a solution (although it’s a mechanical) to serialize the ObservableEntityCollections with the help of some extension methods as illustrated in the mentioned article.
 

How to install OEC through nuget

I’ve created a nuget package for installing the OEC library, to install it in your solution in the package manager console, hit the following command:

image

Or you can install it through NUGet packages manager in Visual studio by searching for ObservableEntityCollection:

image

How to install Add-in

Installing the add-in is a straightforward process, download the add in from (here)
Copy all files to the following path: ..\Documents\Visual Studio 2012\Addins
Then you should see the addin loaded in Visual studio 2012 Add-in manager:

image

Check the startup option, restart VS and the addin will be available in the Tools menu:

image

Simple Example step-by-step

1. Database

Let’s keep it simple, in the database there are two tables, the master table represents a person entity, while the details table represents the contacts.

image

2. Solution structure

Create a silverlight application, don’t forget to uncheck (Enable WCF RIA Service):

image

the solution structure is very simple:
  1. Solution name is Demo
  2. Silverlight application name is DemoShell
  3. ASP.NET website name is DemoShell.Web
  4. Business layer solution (Class library project) name is DBServer
  5. RIA generated entities would be in (Silverlight class library) called DBClient
image

3. Create EF (ORM) file

in DBServer solution add a new ADO.NET EntityFramework Model:

image

in DemoShell.Web add a reference to DBServer library.

4. Generate RIA proxy classes

In DBClient add RIA Link to the website:

image

This will generate the WCF RIA classes in the client library.

5. Add Domain services

Build the solution, and in the DemoShell.Web create a folder with the name (Services), then add a new Domain service with the name (DemoService), you should be able to see the Entities generated in DBServer library.
If you didn’t see the generated entities (which is a known problem): go to DBServer project and delete *.tt files in Model.edmx, assign the code generation strategy to (Default).

image

the web solution structure should like the following:

image

Rebuild the solution, this will generate the RIA proxy classes in the DBClient.

6. Wrap the Contacts entities with ObservableEntityCollection

Checking the generated code in DBClient reveals the following Person Entity:

image

Contacts is of type EntityCollection:

image

to wrap this collection with ObservableEntityCollection follow these steps:

(1) Search in NUget for ObservableEntityCollection and install it in DBClient project

image

this will install the required library in the references section:

image

(2) Use the OEC Modeler tool
Launch the OEC Modeler tool in VS and select DBClient project, then press (Load Entities From Assembly):

image

Because Person Entity will contain ObservableEntityCollection we mark it as ObservableCollection:

image

We choose Generate Mapping, the result would be the following files generated in DBClient:


image

Checking the file oce_boilerplate.cs you will see that the entity Person is implementing some interfaces, that you don’t have to write manually. And in the first few lines it asks you to define some partial methods.
(3) Implement the partial method
Now the client should now implement some partial methods, which are very easy and self-explanatory.
We create file Person_Extender.cs and implement the following partial methods:
partial void OnResetObservableCollections();
partial void OnCreateObservableCollections();
partial void OnUndoObservableCollections();
partial void OnCreateValidationResults();
partial void OnSerializingStart();
partial void OnMarkDirty();
partial void OnClearDirty();

For this simple example we need only to declare the observable collection, in this case it’s only one (ObservableContacts) and implement two partial methods (the other methods would be explained in upcoming articles):
namespace DBServer
{
    public partial class Person 
    {
        public ObservableCollections.ObservableEntityCollection<Contact> ObservableContacts { get; set; }

        
        partial void OnCreateObservableCollections()
        {
            this.ObservableContacts = new ObservableCollections.ObservableEntityCollection<Contact>(this.Contacts, this);
        }
        partial void OnUndoObservableCollections()
        {
            this.ObservableContacts.Undo();
        }
       
    }
}

that’s all you need to get observable collection that wraps the EntityCollection. I’m thinking to generate this code in future versions of this addin.

7. Using the ObservableEntityCollection

In DemoShell project add a reference to DBClient project.

It’s worth to mention that, the any time you invoke OECModeler in Visual studio, you can always load the same mapping generated previously, because this tool creates a XML file to store your mapping settings.

image

I’m not going to explain how to create MVVM in DemoShell, you can download the example from CodePlex (here)

In nutshell I’m using the MVVM Light framework, to create the ViewModel.

The XAML file contains two grids one for the parent and the other one is for related contacts.

We bind the first grid to the People collection, while the second grid to the CurrentPerson.ObservableContacts:

image



To download the full example check this link (Here).

the MVVM model is very simple:
using DemoShell.Web.Services;
using GalaSoft.MvvmLight;
using System.Windows;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using DBServer;
using GalaSoft.MvvmLight.Command;

namespace DemoShell.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        private DemoContext _context=new DemoContext();
        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
            }
            else
            {
                // Code runs "for real"
                _context.Load(_context.GetPeopleQuery(), operation =>
                    {
                       
                    }, null);
            }

            
        }

        /// <summary>
        /// The <see cref="People" /> property's name.
        /// </summary>
        public const string PeoplePropertyName = "People";

        private EntitySet<Person> _People = new EntitySet<Person>();
        private EntitySet<Person> _sampleData = new EntitySet<Person>();

        /// <summary>
        /// Sets and gets the People property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public EntitySet<Person> People
        {
            get
            {
                if (!IsInDesignMode)
                    return _context.Persons;
                else
                {
                    for (int i = 0; i < 100; i++)
                    {
                        var person = new Person() { FirstName = string.Format("FirstName{0}", i), LastName = string.Format("LastName{0}", i), Id = i };
                        person.Contacts.Add(new Contact() { Address = "Address in canada" });
                        _sampleData.Add(person);
                    }
                    return _sampleData;
                }
            }

            set
            {               
                RaisePropertyChanged(PeoplePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SelectedPerson" /> property's name.
        /// </summary>
        public const string SelectedPersonPropertyName = "SelectedPerson";

        private int _selectedPerson = -1;

        /// <summary>
        /// Sets and gets the SelectedPerson property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int SelectedPerson
        {
            get
            {
                return _selectedPerson;
            }

            set
            {
                if (_selectedPerson == value)
                {
                    return;
                }

                RaisePropertyChanging(SelectedPersonPropertyName);
                _selectedPerson = value;
                RaisePropertyChanged(SelectedPersonPropertyName);
                RaisePropertyChanged(CurrentPersonPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="CurrentPerson" /> property's name.
        /// </summary>
        public const string CurrentPersonPropertyName = "CurrentPerson";

       

        /// <summary>
        /// Sets and gets the CurrentPerson property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public Person CurrentPerson
        {
            get
            {
                if (SelectedPerson > -1) return People.ElementAt(SelectedPerson);
                return null;
            }

            set
            {              
              
                RaisePropertyChanged(CurrentPersonPropertyName);
            }
        }

        private RelayCommand _undoPerson;

        /// <summary>
        /// Gets the UndoPerson.
        /// </summary>
        public RelayCommand UndoPerson
        {
            get
            {
                return _undoPerson
                    ?? (_undoPerson = new RelayCommand(
                                          () =>
                                          {
                                              if (CurrentPerson != null)
                                                  CurrentPerson.Undo();
                                          }));
            }
        }
       
    }
}
                 
As we note there is a binding to command undo the changes in the parent and child collections with one line of code:
if (CurrentPerson != null)
                     CurrentPerson.Undo();

This will revert the ObservableEntityCollection to its original state, although It’s not included in the example, these changes include adding or removing child row.

How can you help?

This solution including the add-in is an open source project hosted on CodePlex, so feel free to review the code, and modify it.
Enhancements that could be done are:
  1. OEC-Modeler: it could be improved further to provide a more professional designer surface similar to EntityFramework designer that is integrated in Visual studio 2012 IDE.
  2. Loading the Silverlight assembly (which contains the Entities) in OECModeler is still buggy, some dependencies (Silverlight and RIA libraries) should be loaded automatically from predefined paths, and should be generalized to support future Silverlight releases.
  3. The OEC-Modeler could be extended to enumerate the EntityCollections in each Entity, with the option to specify it as Wrapped.
  4. Wrapped EntityCollection requires the modification of the T4 template to automatically generate OEC wrappers. This would be another step to minimize the work to be done by the developer.
  5. typos mistakes :)
Reporting bugs and features' requests are welcome through codeplex.

Last edited Feb 17, 2013 at 5:47 AM by maherali, version 13