The EntityManager Provider

The core abstraction layer in the DevForce Application Framework is the EntityManger provider. It shields the application from the EntityManager to be used when querying for or saving entities. By switching between different implementations of the EntityManager provider, the application can run against the database, the fake backing store or provide sample data at design time in Blend or Cider without the need for changing the application itself.

An EntityManager provider is implemented by extending one of the three base classes provided by the DevForce Application Framework and illustrated in the following diagram.

image

BaseEntityManagerProvider<T>

The main EntityManager provider implementation is BaseEntityManagerProvider<T>, where T is the type of the EntityManager. It contains most of the core logic and is used when the intent is to connect to a real database. The following snippet shows how to implement an EntityManager provider based on BaseEntityManagerProvider<T>.

using IdeaBlade.Application.Framework.Core.Persistence;

namespace SampleModel
{
    public class EntityManagerProvider : BaseEntityManagerProvider<NorthwindIBEntities>
    {
        protected override NorthwindIBEntities CreateEntityManager()
        {
            return new NorthwindIBEntities();
        }
    }
}

BaseFakeStoreEntityManagerProvider<T>

The next EntityManager provider implementation is the BaseFakeStoreEntityManagerProvider<T>. It inherits from BaseEntityManagerProvider<T> and adds the necessary pieces to initialize and restore the DevForce Fake Backing Store with sample data. The DevForce Fake Backing Store allows to develop an application without needing an actual database. Eliminating all the issues associated with a shared developer database and data corruption, which slows down the entire development team. By using the Fake Backing Store, one can always develop against known sample data and with a simple method call, the data in the Fake Backing Store can be reset to the original values, which is useful for testing. The following snippet shows how to implement an EntityManager provider that uses the Fake Backing Store.

using IdeaBlade.Application.Framework.Core.Persistence;
using IdeaBlade.Core.Composition;

namespace SampleModel
{
    public class DevelopmentEntityManagerProvider : BaseFakeStoreEntityManagerProvider<NorthwindIBEntities>
    {
        protected override NorthwindIBEntities CreateEntityManager()
        {
            return new NorthwindIBEntities(compositionContextName: CompositionContext.Fake.Name);
        }
    }
}

The implementation is similar to the first EntityManager provider, except we are specifying the fake DevForce CompositionContext in the EntityManager constructor, which will ensure that the EntityManager connects to the Fake Backing Store instead of the database.

BaseDesignTimeEntityManagerProvider<T>

The third EntityManager provider implementation is the BaseDesignTimeEntityManagerProvider<T>. It is similar to the Fake Store EntityManager provider in the sense that it uses sample data, but it never connects to the BOS (DevForce Business Object Server). The sample data is loaded into the EntityManager cache and all queries are run against the cache instead of either a database or the Fake Backing Store. This EntityManager provider is primarily used to provide sample data during design time in Blend or Cider. The following snippet shows how to implement a design time EntityManager provider.

using System.Collections.Generic;
using IdeaBlade.Application.Framework.Core.DesignTimeSupport;

namespace SampleModel
{
    public class DesignTimeEntityManagerProvider : BaseDesignTimeEntityManagerProvider<NorthwindIBEntities>
    {
        protected override NorthwindIBEntities CreateEntityManager()
        {
            return new NorthwindIBEntities(false);
        }
    }
}

In case of the design time EntityManager provider, we make sure that the EnityManager gets created in disconnected mode. This forces all queries to automatically run against the cache and the EntityManager never attempts to connect to a server.

Sample Data

Both the BaseFakeStoreEntityManagerProvider<T> and BaseDesignTimeEntityManagerProvider<T> require sample data. The sample data is provided by means of a SampleDataProvider. Multiple SampleDataProviders are supported. The DevForce Application Framework automatically discovers all SampleDataProviders through MEF and uses them to populate the EntityManager cache and/or Fake Backing Store. The following code snippet shows how to implement a SampleDataProvider.

using System;
using System.ComponentModel.Composition;
using IdeaBlade.Application.Framework.Core.DesignTimeSupport;

namespace SampleModel
{
    [Export(typeof(ISampleDataProvider<NorthwindIBEntities>))]
    public class SampleDataProvider : ISampleDataProvider<NorthwindIBEntities>
    {
        #region ISampleDataProvider<NorthwindIBEntities> Members

        void ISampleDataProvider<NorthwindIBEntities>.AddSampleData(NorthwindIBEntities manager)
        {
            AddCustomers(manager);
        }

        #endregion

        private static void AddCustomers(NorthwindIBEntities manager)
        {
            var customer1 = new Customer
                                {
                                    CustomerID = Guid.NewGuid(),
                                    CompanyName = "Company1",
                                    ContactName = "John Doe",
                                    Address = "SomeAddress",
                                    City = "SomeCity",
                                    PostalCode = "11111"
                                };
            manager.AttachEntity(customer1);

            var customer2 = new Customer
                                {
                                    CustomerID = Guid.NewGuid(),
                                    CompanyName = "Company2",
                                    ContactName = "Jane Doe",
                                    Address = "SomeAddress",
                                    City = "SomeCity",
                                    PostalCode = "11111"
                                };
            manager.AttachEntity(customer2);
        }
    }
}

Synchronizing Changes

In DevForce, each instance of an EntityManager manages a client-side cache of entities. If developing an application that uses multiple EntityManagers one will deliberately or inadvertently end up with multiple caches and potentially have multiple copies of an entity spread among these caches. Often this is desired, so that an entity can be edited in a sandbox for example. When entities get changed and saved in an EntityManager, one might want to synchronize these changes to all other EntityManagers keeping copies of the same entities. The DevForce Application Framework has built-in support for change synchronization. By default the synchronization is disabled. To enable change synchronization, implement an EntityManagerSyncInterceptor to control which entities should be exported from the source EntityManager and which entities should be imported to the target EntityManagers. The following snippet shows how to implement an EntityManagerSyncInterceptor.

using IdeaBlade.Application.Framework.Core.Persistence;
using IdeaBlade.EntityModel;

namespace HelloWorld
{
    public class SyncInterceptor : EntityManagerSyncInterceptor
    {
        public override bool ShouldExportEntity(object entity)
        {
            return true;
        }

        public override bool ShouldImportEntity(object entity)
        {
            // Only import if the importing EntityManager holds a copy of the entity in its cache
            return EntityManager.FindEntity(EntityAspect.Wrap(entity).EntityKey) != null;
        }
    }
}

Note: The synchronization preserves pending changes in the importing EntityManager, so if an EntityManager holds a copy of an entity with pending changes and another EntityManager saves the same entity triggering change synchronization, the pending changes will be merged with the saved changes.

Exporting the EntityManagerProvider

In order for the framework to discover the proper implementation of the EntityManagerProvider, it must be exported.

Multiple non-shared EntityManagerProviders

If the application needs to support multiple EntityManagerProvider instances, that is different components of the application require a separate EntityManagerProvider, for example for sandbox editing, then we utilize a property export in MEF. The following snippet illustrates how to export the EntityManagerProvider using a property export:

using System.ComponentModel.Composition;
using IdeaBlade.Application.Framework.Core.Persistence;

namespace HelloWorld
{
    public class EntityManagerProviderFactory
    {
        ///<summary>
        /// Returns a new instance of the correct EntityMangerProvider to the CompositionHost
        // when requested
        ///</summary>
        [Export]
        public static IEntityManagerProvider<NorthwindIBEntities> EntityManagerProvider
        {
            get
            {
#if DEBUG
                // For the Debug build, use the fake store EntityManagerProvider
                return new DevelopmentEntityManagerProvider(null);
#else
                // For the Release build, use the production EntityManagerProvider
                return new EntityManagerProvider();
#endif
            }
        }
    }
}

Sharing a single EntityManagerProvider

If on the other hand, the entire application should use a shared EntityManagerProvider, then simply inject it in the Bootstrapper. The following snippet illustrates how to inject a single shared EntityManagerProvider:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Caliburn.Micro.Extensions;
using IdeaBlade.Application.Framework.Core.Persistence;
using SampleModel;

namespace SampleApplication
{
    public class AppBootstrapper : FrameworkBootstrapper<HarnessViewModel>
    {
        protected override void InitializeCompositionBatch(CompositionBatch batch)
        {
            base.InitializeCompositionBatch(batch);

            // Inject the shared EntityManagerProvider
#if DEBUG
            // For the Debug build, use the fake store EntityManagerProvider
            batch.AddExportedValue<IEntityManagerProvider<NorthwindIBEntities>>(new DevelopmentEntityManagerProvider());
#else
            // For the Release build, use the production EntityManagerProvider
            batch.AddExportedValue<IEntityManagerProvider<NorthwindIBEntities>>(new EntityManagerProvider());
#endif
        }
    }
}


Last edited Dec 15, 2011 at 9:46 AM by marcelgood, version 30

Comments

No comments yet.