Fast prototyping requires an initial data import mechanism

XAF Team Blog
05 November 2012

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! 

 Updated: The code and screen shots are taken from the XVideoRental application. This demo can be found along with the other XAF demos in v12.2  

7 comment(s)
Martin Brekhof
Martin Brekhof

Hi Tolis,

any chance you could provide an example demonstrating just the functionality in this blog post?

regards

6 November, 2012
Apostolis Bekiaris (DevExpress)
Apostolis Bekiaris (DevExpress)

Hi Martin

Of course we will :),  real interesting posts and samples are coming!

6 November, 2012
James Zhong
James Zhong

Thanks for this useful article! Could you please show some examples using .csv and .xml as data seed?

6 November, 2012
Edilson Junior
Edilson Junior

Really good article! I second James, csv or xml import would be great!

7 November, 2012
Apostolis Bekiaris (DevExpress)
Apostolis Bekiaris (DevExpress)

we will provide! however the solutions are pretty obvious.

For dataset-compatible XML -- you can use InMemoryDataStore

For the rest of XML and the CSV -- you will need to write a custom provider or imported them in a compatible engine like Access

7 November, 2012
James Zhong
James Zhong

Thanks Apostolis for your solutions for dataset-compatible XML and flat CSV (exported from spreadsheet like Microsoft Excel). Any further examples will be very helpful!

9 November, 2012
Alain Bismark
Alain Bismark

Hi

I think that a complete example, importing Master-Detail data from a XLX or CSV will be great.

Thanks in advance

12 November, 2012

Please login or register to post comments.