Tuesday, November 6, 2012

Fast prototyping requires an initial data import mechanism

To improve development time and achieve better results we need to use frameworks like XAF. Moreover with XAF it’s really easy to create applications that are maintainable at runtime! After designing a domain model in VS we are almost ready to go. At this point, however we need a smart and flexible importing mechanism for feeding our domain models with initial data. Customers are impressed when they see real world data! So lets build together this mechanism proving once more the XAF power.

Requirements

  1. Domain model agnostic.

    This really means that our importing algorithms should depend only on object types. Therefore we can implement this as an attribute and decorate our domain objects. 
     
  2. Database agnostic

    XPO that supports all common database engines with its rich metadata API is our answer here.
     
  3. Flexible mapping configuration

    For this we are going to create the an XPO dictionary on the fly. Querying our domain object attributes we can map this dictionary correctly.
     
  4. Inheritance mapping configuration

    Take as example an Artist, Customer both inheriting a Person object. The attribute should allow to map different the properties of Person when Artist and when Customer.

Examples

  1. The following example imports all members that belong to MovieArtist from a table with same name columns (AllOwnMembers=true). In addition it maps the oid column in the table of the importing data store to the Oid property of VideoRentalBaseObject (BaseNMembers=”oid|Oid”). Finally it does lookup data in a data store in a table named vMovieArtist (Name=”vMovieArtist”).

    [InitialData(BaseMembers = "oid|Oid", AllOwnMembers = true,Name = "vMovieArtist")]

    public class MovieArtist : VideoRentalBaseObject {

        Artist artist;

        Movie movie;

  2. The next one imports from a table named CompanyType only the column named Name (AllOwnMembers=false, BaseMembers=null, only Name property is decorated)

    [InitialData()]

    public class CompanyType : VideoRentalBaseObject {

        [InitialData]

        public string Name {

            get { return _name; }

            set { SetPropertyValue("Name", ref _name, value); }

        }


  3. By default all object relations are auto imported unless the related object is not included in the configuration. Then only the key value is imported. It is also possible to control the mapping in many to many relations as

    public class Movie : VideoRentalBaseObject {

     

        [InitialData(DataProviderTableName = "CountryMovies", DataProviderQueryColumnName = "Movies", DataProviderResultColumnName = "Countries")]

        [Association("Movies-Countries")]

        public XPCollection<Country> Countries {

            get { return GetCollection<Country>("Countries"); }

        }

The algorithm

  1. This is an initial data import mechanism. For such operations XAF provides the ModuleUpdater. We need the XafApplication’s IObjectSpace and one UnitOfWork that connects to an external data source so we can simply design an extension method like,

    public static void Import(this IObjectSpace objectSpace, UnitOfWork unitOfWork) {

    }

    and invoke it inside UpdateDatabaseAfterUpdateSchema as,

     

    public class Updater : ModuleUpdater {

        public Updater(IObjectSpace objectSpace, Version currentDBVersion) : base(objectSpace, currentDBVersion) { }

        public override void UpdateDatabaseAfterUpdateSchema() {

            base.UpdateDatabaseAfterUpdateSchema();

            var connectionString = ConfigurationManager.ConnectionStrings["initialdata"].ConnectionString;

            ObjectSpace.Import(new UnitOfWork{ConnectionString = connectionString});

     

  2. Using XAF’s rich reflection API we can enumerate all our domain objects, search for the InitialData attribute and create an XPO dictionary on the fly! This is rather easy as XPO follows a similar to .NET structure, so we have to create XPClassInfo for Classses and XPMemberInfo for members. A great sample exists in our code central so we can get a fast start with this API (E269). Following are the 2 main methods that implement the import mapping.

    public static void ImportCore(this IObjectSpace objectSpace, UnitOfWork unitOfWork) {

        var builder = new InitDataDictionaryBuilder(objectSpace);

        builder.InitDictionary(unitOfWork);

        foreach (var persistentTypeInfo in builder.GetPersistentTypes()) {

            Import(objectSpace, builder, unitOfWork, persistentTypeInfo);

        }

        unitOfWork.CommitChanges();

    }


    internal class InitDataDictionaryBuilder {

        public void InitDictionary(UnitOfWork unitOfWork) {

            var persistentTypes = GetPersistentTypes();

            CreateMemberInfos(unitOfWork.Dictionary, persistentTypes);

        }

     
    A complete and up to date version can be found in expandframework’s github repository.


In this post we improved the already rapid XAF development, by creating the import mechanism from almost any type of data into our domain model. In the next one we will visit another great tool offered by XAF the dashboard views.

We would appreciate your feedback on this post. Has it been useful to you? Feel free to contact us with any further questions.

Happing XAFing as always!

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment