• Using ConfORM with modular application


    Using ConfORM with modular application

    I’m getting some questions about how use ConfORM in modular applications. The first commercial application mapped with ConfORM is a pretty complex modular application and if you have a look to the first ConfOrm example you can find the IModuleMapping.
    Here I’ll try to explain how we have used it in that application writing a new example.

    The IModuleMapper

    /// <summary>
    /// A template to perform a mapping using ConfOrm
    /// </summary>
    public interface IModuleMapper
    {
        /// <summary>
        /// Register domain classes, persistent-strategies, relations and so on of a spefic module
        /// </summary>
        void DefineDomain();

        /// <summary>
        /// Register patterns of a module
        /// </summary>
        void RegisterPatterns();

        /// <summary>
        /// Customize persistence representations
        /// </summary>
        void Customize();

        /// <summary>
        /// Get all domain entities of the module.
        /// </summary>
        /// <returns>domain entities.</returns>
        IEnumerable<Type> GetEntities();
    }
    As you can see it is a simple template just to suggest how organize your mapping… nothing complicated.

    The Domain

    In the assembly Acme.ModuleA
    using System;
    using Acme.Domain.Common;

    namespace Acme.ModuleA
    {
        public class UserEntity
        {
            public string UserName { getset; }
            public string Text { getset; }
            public DateTime Birthday { getset; }
        }
    }
    In the assembly Acme.ModuleB
    using System;
    using Acme.Domain.Common;
    using Acme.ModuleA;

    namespace Acme.ModuleB
    {
        public class ArticleEntity
        {
            public DateTime PublishedAt { getset; }
            public string Title { getset; }
            public string Text { getset; }
            public User Author { getset; }
        }
    }

     

    Some extensions

    Just to not forget it later… 
    public static class ModuleMappingUtil
    {
        public static IEnumerable<Type> MapModule(this IModuleMapper mapper)
        {
            mapper.DefineDomain();
            mapper.RegisterPatterns();
            mapper.Customize();
            return mapper.GetEntities();
        }

        public static bool IsOfModuleContaining<T>(this Type source)
        {
            return source.Assembly.Equals(typeof(T).Assembly);
        }
    }

     

    The mapping

    You can organize the mapping of your modules in a single assembly, in general the same where you are generating the session factory, or in more than one assembly. In general you need just a little class to map a module so please don’t be exaggerated separating your application. 
    For this example I will show three implementations of ModuleMapper all implemented in the same assembly named Acme.Persistence.Wiring.
    public class GeneralsPatternsModuleMapper : IModuleMapper
    {
        private readonly Mapper mapper;
        private readonly ObjectRelationalMapper orm;

        public GeneralsPatternsModuleMapper(ObjectRelationalMapper orm, Mapper mapper)
        {
            this.orm = orm;
            this.mapper = mapper;
        }

        #region IModuleMapper Members

        public void DefineDomain()
        {
            // map .NET4 ISet<T> as a NHibernate's set
            orm.Patterns.Sets.Add(mi => mi.GetPropertyOrFieldType().GetGenericIntercafesTypeDefinitions().Contains(typeof (ISet<>)));
        }

        public void RegisterPatterns()
        {
            // all strings are length 50 characters
            mapper.PatternsAppliers.Property.Add(mi => typeof (string).Equals(mi.GetPropertyOrFieldType()), (mi, map) => map.Length(50));
          
            // when a DateTime seems to be a simple date apply NH's Date type
            mapper.PatternsAppliers.Property.Add(mi => typeof (DateTime).Equals(mi.GetPropertyOrFieldType()) && IsDate(mi.Name), (mi, map) => map.Type(NHibernateUtil.Date));

            // when a DateTime seems to not be a simple date apply NH's UtcDateTime type
            mapper.PatternsAppliers.Property.Add(mi => typeof (DateTime).Equals(mi.GetPropertyOrFieldType()) && !IsDate(mi.Name), (mi, map) => map.Type(NHibernateUtil.UtcDateTime));
        }

        public void Customize()
        {
            // Nothing to do
        }

        public IEnumerable<Type> GetEntities()
        {
            yield break;
        }

        #endregion

        public static bool IsDate(string name)
        {
            return name.EndsWith("Date") || name.StartsWith("Date") || name.EndsWith("Day") || name.EndsWith("day") || name.StartsWith("Day");
        }
    }
    The GeneralsPatternsModuleMapper register just part of my common convention for the current application. Then the mapping of my Acme.ModuleA:
    public class ModuleAMapper : IModuleMapper
    {
        private readonly Mapper mapper;
        private readonly ObjectRelationalMapper orm;

        public ModuleAMapper(ObjectRelationalMapper orm, Mapper mapper)
        {
            this.orm = orm;
            this.mapper = mapper;
        }

        public void DefineDomain()
        {
            // register all classes of my module (ConfORM will use it to discover polymorphic associations)
            orm.AddToDomain(typeof(User).Assembly.GetExportedTypes());

            // defines the persistence strategy for each root of each hierarchy
            orm.TablePerClass<User>();
        }

        public void RegisterPatterns()
        {
            // no specific patterns for this module
        }

        public void Customize()
        {
            // map a specific size (20) for a specific property of a specific class
            mapper.Class<User>(x => x.Property(user => user.UserName, map => map.Length(20)));
        }

        public IEnumerable<Type> GetEntities()
        {
            // all entities of this modules
            return typeof(User).Assembly.GetExportedTypes().Where(t=> typeof(Entity).IsAssignableFrom(t));
        }
    }
    The mapping of Acme.ModuleB:
    public class ModuleBMapper : IModuleMapper
    {
        private readonly Mapper mapper;
        private readonly ObjectRelationalMapper orm;

        public ModuleBMapper(ObjectRelationalMapper orm, Mapper mapper)
        {
            this.orm = orm;
            this.mapper = mapper;
        }

        public void DefineDomain()
        {
            // register all classes of my module (ConfORM will use it to discover polymorphic associations)
            orm.AddToDomain(typeof(Article).Assembly.GetExportedTypes());

            // defines the persistence strategy for each root of each hierarchy
            orm.TablePerClassHierarchy<Article>();
        }

        public void RegisterPatterns()
        {
            // patterns for this module

            // when a string property is named "Text" then apply StringClob type (note just for Acme.ModuleB)
            mapper.PatternsAppliers.Property.Add(
                mi => "text".Equals(mi.Name.ToLowerInvariant()) && typeof (string).Equals(mi.GetPropertyOrFieldType()) && mi.DeclaringType.IsOfModuleContaining<Article>(),
                (mi, map) => { map.Type(NHibernateUtil.StringClob); map.Length(int.MaxValue); });
        }

        public void Customize()
        {
            // nothing to do
        }

        public IEnumerable<Type> GetEntities()
        {
            // all entities of this modules
            return typeof(Article).Assembly.GetExportedTypes().Where(t => typeof(Entity).IsAssignableFrom(t));
        }
    }

     

    The NHibernate’s initialization

    First you need a method to get all modules of your application, for simplicity here is a simple implementation:
    private static IEnumerable<IModuleMapper> GetAllModulesMappers(ObjectRelationalMapper orm, Mapper mapper)
    {
        yield return new GeneralsPatternsModuleMapper(orm, mapper);
        yield return new ModuleAMapper(orm, mapper);
        yield return new ModuleBMapper(orm, mapper);
    }
    and now you have all is needed to have the session factory:
    1. var orm = new ObjectRelationalMapper();
    2. var patternSet = new CoolPatternsAppliersHolder(orm);
    3. var mapper = new Mapper(orm, patternSet);
    4.  
    5. HbmMapping mappings = mapper.CompileMappingFor(GetAllModulesMappers(orm, mapper).SelectMany(x => x.MapModule()));
    6.  
    7. var configure = new Configuration();
    8. configure.SessionFactoryName("Demo");
    9. configure.DataBaseIntegration(db =>
    10. {
    11.     db.Dialect<MsSql2008Dialect>();
    12.     db.ConnectionStringName = "ToAcmeDb";
    13. });
    14.  
    15. configure.AddDeserializedMapping(mappings, "AcmeApplicationDomain");
    16.  
    17. ISessionFactory factory = configure.BuildSessionFactory();
    In the line 5 I’m getting just one mapping document for the whole domain. You can use the methodmapper.CompileMappingForEach without problems. The main matter here is the fact that I’m using just one instance of ObjectRelationalMapper and Mapper to give the whole picture of the domain of the application to ConfORM.
  • 相关阅读:
    Jmeter接口测试时传递json格式的数据
    selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element 定位frame中的元素
    selenium报错“selenium.common.exceptions.WebDriverException: Message: 'geckodriver' executable needs to be in PATH.”的解决方案
    python+selenium如何定位页面的元素,有几种定位元素的方法?
    c#中的表达式
    占位符的使用
    数据类型(变量的声明与赋值)
    Hello World 老调重谈
    易语言转C#小试牛刀
    开博了
  • 原文地址:https://www.cnblogs.com/sunjie9606/p/2169735.html
Copyright © 2020-2023  润新知