Multiple files for MappingConfiguration for each entity

5 posts, 2 answers
  1. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 27 Aug 2015 Link to this post

    I am trying to clean up my code a bit and attempted to break out the mapping code for each entity into it's own file, accessible via a static method Map(). These are all in the same assembly, project, namespace, and even folder. I am however, running into some issues ... 

    Here's an example of one on my mapping classes ... 

     

    public class ModelBaseMap
    {
        public static MappingConfiguration<ModelBase> Map()
        {
            //
            // We need to map the ModelBase as well using Horizontal Inheritance
            MappingConfiguration<ModelBase> map = new MappingConfiguration<ModelBase>();
            map.MapType().Inheritance(Telerik.OpenAccess.InheritanceStrategy.Horizontal);
     
            return map;
        }
    }

     And here is the metadatsource file ... 

     

    public partial class DbContextMetadataSource : FluentMetadataSource
    {
        protected override IList<MappingConfiguration> PrepareMapping()
        {
            List<MappingConfiguration> configurations = new List<MappingConfiguration>();
     
            MappingConfiguration<ModelBase> modelBaseMap         = ModelBaseMap.Map();           
            MappingConfiguration<DeliverableType> typeMap        = DeliverableTypeMap.Map();
            MappingConfiguration<Deliverable> delMap             = DeliverableMap.Map();
            MappingConfiguration<DeliverablePackage> pkgMap      = DeliverablePackageMap.Map();
            MappingConfiguration<DeliverablePackageItem> itemMap = DeliverablePackageItemMap.Map();
             
            configurations.Add(modelBaseMap);
            configurations.Add(typeMap);
            configurations.Add(delMap);
            configurations.Add(pkgMap);
            configurations.Add(itemMap);
     
            return configurations;
        }
    }

     I am getting the following error ... 

     

    Telerik.OpenAccess.Exceptions.ConfigurationException: Found configurations for property 'CreatedDate' of class 'InnovativeFoto.BLL.Models.ModelBase' both in the PrepareMapping method of the FluentMetadataContext and in the class static method returning MappingConfiguration

     

     Is this not allowed? MUST the mapping all exist in the actual MetaDataSource file?

     

     

     

     

     

  2. Answer
    George
    George avatar
    9 posts
    Member since:
    Mar 2015

    Posted 27 Aug 2015 in reply to Gary Link to this post

     I did this in my company's code base so it can be done.

    The problem is, There is a class scanner that looks for Public Static methods that return a MappingConfiguration<T>.  This means both the ModelBaseMap and the DbContextMetadataSource methods get called, and you get an 'already mapped' style exception.

    You can either remove the call to the static method in DbContextMetadataSource  and let the scanner pick up the mapping methods from the other classes or make the ModelBaseMap.Map() methods non-static.  In my case, I went with removing the mapping code from the MetaDataSource and let that define the global properties of the container, and pushed the map methods out to the other classes.

     

    public class MappedClass
    {
      //Properties stuff {get; set;}
     
      [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Called by the reflection from Data Access.")]
      public static MappingConfiguration<MappedClass> GetMappedClassMappingConfiguration()
      {
        MappingConfiguration<MappedClass> configuration = GetMappedClassClassConfiguration();
        PrepareMappedClassPropertyConfigurations(configuration);
     
        return configuration;
      }
     
      private static MappingConfiguration<MappedClass> GetMappedClassClassConfiguration()
      {
        MappingConfiguration<MappedClass> configuration = new MappingConfiguration<MappedClass>();
        configuration.MapType(x => new {})
          .WithConcurencyControl(OptimisticConcurrencyControlStrategy.Changed)
          .ToTable("db_table");
     
        return configuration;
      }
     
      private static void PrepareMappedClassPropertyConfigurations(MappingConfiguration<MappedClass> configuration)
      {
        //Map the properties.
      }
    }

     

    #pragma warning disable 1591
    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code was generated by the FluentMappingGenerator.ttinclude code generation file.
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // </auto-generated>
    //------------------------------------------------------------------------------
    using System.Collections.Generic;
    using Telerik.OpenAccess.Metadata;
    using Telerik.OpenAccess.Metadata.Fluent;
     
    namespace Entities.BaseModel
    {
      public partial class FluentModelMetadataSource : FluentMetadataSource
      {
        protected override IList<MappingConfiguration> PrepareMapping()
        {
          List<MappingConfiguration> mappingConfigurations = new List<MappingConfiguration>();
     
          return mappingConfigurations;
        }
     
        protected override void SetContainerSettings(MetadataContainer container)
        {
          container.Name = "FluentModel";
          container.DefaultNamespace = "Entities";
          container.NameGenerator.SourceStrategy = Telerik.OpenAccess.Metadata.NamingSourceStrategy.AutoProperty;
          container.NameGenerator.RemoveCamelCase = false;
        }
      }
    }
     
    #pragma warning restore 1591

     

     

     

  3. DevCraft banner
  4. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 27 Aug 2015 in reply to George Link to this post

    Thank you for such a quick response!! Your reply absolutely answered my question (Thank you) but raised a couple of questions/thoughts ... 

    • I noticed, in your example, that there was no longer an explicit call to "configurations.add(map)" in the ​MetadData​Source​; which makes sense since ​it no longer contains references to the MappingConfigurations. I assume then, that the class scanner knows to add the MappingConfgurations from the public static calls to the List<MappingConfiguration> collection?
    • I may want to support multiple contexts, thusly would require multiple MetaDataSources (one for each context). If I understand correctly if use instance methods instead of public static methods ... would this be do-able? (Code example below )

     

    public partial class DbContextMetadataSource : FluentMetadataSource
    {
        protected override IList<MappingConfiguration> PrepareMapping()
        {
            List<MappingConfiguration> configurations = new List<MappingConfiguration>();
      
            ModelBaseMap baseMapper                              = new ModelBaseMap();
            MappingConfiguration<ModelBase> modelBaseMap         = baseMapper.Map();
             
            DeliverableTypeMap typeMapper                        = new DeliverableTypeMap();
            MappingConfiguration<DeliverableType> typeMap        = typeMapper.Map();
             
            DeliverableMap deliverableMapper                     = new DeliverableMap();
            MappingConfiguration<Deliverable> delMap             = deliverableMapper.Map();
             
            DeliverablePackageMap packageMapper                  = new DeliverablePackageMap();
            MappingConfiguration<DeliverablePackage> pkgMap      = packageMapper.Map();
             
            DeliverablePackageItemMap itemMapper                 = new DeliverablePackageItemMap();
            MappingConfiguration<DeliverablePackageItem> itemMap = itemMapper.Map();
              
            configurations.Add(modelBaseMap);
            configurations.Add(typeMap);
            configurations.Add(delMap);
            configurations.Add(pkgMap);
            configurations.Add(itemMap);
      
            return configurations;
        }
    }
     
    public partial class AnotherDbContextMetadataSource : FluentMetadataSource
    {
        protected override IList<MappingConfiguration> PrepareMapping()
        {
            List<MappingConfiguration> configurations = new List<MappingConfiguration>();
      
            ModelBaseMap baseMapper                      = new ModelBaseMap();
            MappingConfiguration<ModelBase> modelBaseMap = baseMapper.Map();
             
            AccountMap acctMapper                        = new AccountMap();
            MappingConfiguration<Account> typeMap        = acctMapper.Map();
             
            AccountTypeMap typeMapper                    = new AccountTypeMap();
            MappingConfiguration<AccountType> acctMap    = typeMapper.Map();
              
            configurations.Add(modelBaseMap);
            configurations.Add(typeMap);
            configurations.Add(acctMap);
      
            return configurations;
        }
    }
     
     
     
    public partial class DbContext : OpenAccessContext
    {
        // ... Entity defintions here
         
     
        private static string connectionStringName = @"Connection";
     
        private static BackendConfiguration backend = GetBackendConfiguration();
     
        private static MetadataSource metadataSource = new DbContextMetadataSource();
     
        public DbContext() : base(connectionStringName, backend, metadataSource) {}
     
        public static BackendConfiguration GetBackendConfiguration()
        {
            BackendConfiguration backend = new BackendConfiguration();
            backend.Backend              = "MsSql";
            backend.ProviderName         = "System.Data.SqlClient";
     
            return backend;
        }
    }
     
     
     
    public partial class AnotherDbContext : OpenAccessContext
    {
        // ... Entity defintions here
         
     
        private static string connectionStringName = @"Connection";
     
        private static BackendConfiguration backend = GetBackendConfiguration();
     
        private static MetadataSource metadataSource = new AnotherDbContextMetadataSource();
     
        public DbContext() : base(connectionStringName, backend, metadataSource) {}
     
        public static BackendConfiguration GetBackendConfiguration()
        {
            BackendConfiguration backend = new BackendConfiguration();
            backend.Backend              = "MsSql";
            backend.ProviderName         = "System.Data.SqlClient";
     
            return backend;
        }
    }

     

  5. George
    George avatar
    9 posts
    Member since:
    Mar 2015

    Posted 27 Aug 2015 in reply to Gary Link to this post

    gstenstrom said:

    I noticed, in your example, that there was no longer an explicit call to "configurations.add(map)" in the ​MetadData​Source​; which makes sense since ​it no longer contains references to the MappingConfigurations. I assume then, that the class scanner knows to add the MappingConfgurations from the public static calls to the List<MappingConfiguration> collection?

     Correct.   I do not remember having to play around to get the mappings to be picked up.

    gstenstrom said:​

    I may want to support multiple contexts, thusly would require multiple MetaDataSources (one for each context). If I understand correctly if use instance methods instead of public static methods ... would this be do-able? (Code example below )

     I have not  played around with this case, so you may need to wait for a official Telirik answer on this one.  I do have multiple contexts, but I keep them in different projects/namespaces and they do not interact with each other.  I do know that I ran into Default Namespace exceptions and Aggregate metadata exceptions when I had them mis-configured.  I do know that data access supports multiple contexts and merging metadata though.   They describe such operations in their docs:  http://docs.telerik.com/data-access/developers-guide/data-access-model/advanced-model-tasks/multiple-models/devguide-data-access-advanced-tasks-multiple-models-overview

  6. Answer
    Kaloyan Nikolov
    Admin
    Kaloyan Nikolov avatar
    118 posts

    Posted 28 Aug 2015 Link to this post

    Hi,

    The static methods providing the mapping configuration inside a the entity classes are discovered at run-time and are best to use in case of single Context/MetadataSource.

    If you plan to have multiple I would suggest you to go in more explicit way and use either the approach with the instance methods discussed below  (where the disadvantage is that you should instantiate the entity once) or you could use static methods with a bit different signature so that they not discovered automatically. It could be something like this:
    //In your entity classes
    public static void ConfigureMapping(IList<MappingConfiguration> configuration)
    {
        var productsMap = ...
         
        configuration.Add(productsMap);
    }
     
    //then in your MetadataSource your code would be:
    protected override IList<MappingConfiguration> PrepareMapping()
    {
        List<MappingConfiguration> configurations = new List<MappingConfiguration>();
     
        Product.ConfigureMapping(configurations);
     
        return configurations;
    }

    I hope this helps. 

    Regards,
    Kaloyan Nikolov
    Telerik
     
    Check out the latest announcement about Telerik Data Access vNext as a powerful framework able to solve core development problems.
Back to Top
DevCraft banner