Base solution for your next web application
Open Closed

Solution to using EF an NHibernate together? #6192


User avatar
0
ipservant created

Hi, in our situation, we want to use two DBs for our application. Because it is the web port of an existing App, we still need access to the old DB data. And that DBMS is barely supported by NHibernate, not talking about EF core.

So my approach is to keep the MyApp.EntityFrameworkCore assembly as is AND create a new Assembly MyApp.NHibernateAccess Then all the User, Role, Log, ... access will be made using EF core (as in the downloaded template) and my access to the existing data would go via repositories to the old DBMS via NHibernate.

Is this a known or valid way to have several IRepository<> of different ORMs? I anticipate some problems when IoC-resolving just a IRepository<EntityX>. Well, I could just restrict myselt to creating custom IBlaBlaRepository repositories. And they should be resolved by Windsor Castle just fine.

Here are some problems that I had when trying this approach:

I followed basically the steps off ASP.Net boilerplate to using NHibernate. (https://aspnetboilerplate.com/Pages/Documents/NHibernate-Integration)

So I crated a module (as abp supposes) and depended on AbpNHibernateModule.

    [DependsOn(typeof(AbpNHibernateModule))]
    [DependsOn(typeof(IPSwebCoreModule))]
    public class MyAppdsDataModule : AbpModule
    {

This causes AbpNHibernateModule to IoC-register it's verions of UnitOfWork implementations. Which gives me an exception in MyApp.EntityFrameworkCore.DatabaseCheckHelper :

Exception : "Unable to cast object of type 'Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork' to type 'Abp.NHibernate.Uow.NhUnitOfWork'."

At:

            try
            {
                using (var uow =_unitOfWorkManager.Begin())  <<----
                {

So the question rather goes like: Is there a place to further configure the IoC-Container to return the right type depending on a string or requesting type (feature of WC)? or is there a need to include [DependsOn(typeof(AbpNHibernateModule))]? Because if I leave this commented out, it does not create conflicting IoC-registrations.... So far... just being confused.


4 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @ipservant,

    In your case it is better to use custom repositories I guess.

    You need to create a second DbContext for your entities (for the ones accessed via NHibernate) and create a custom repository derived from NhRepositoryBase for each entity.

  • User Avatar
    0
    ipservant created

    I managed to create a separate NHibernate assembly for the DB access and the custom repositories (ones that inherit IRepository) plus a MyAppNhRepositoryBase abstract base implementation. I still have my Module a such:

    [DependsOn(typeof(AbpNHibernateModule))]
    [DependsOn(typeof(IPSwebCoreModule))]
    public class MyAppdsDataModule : AbpModule
    {
    

    Now what happens depends on the order on my main module.

    if the order of the attributes is like so:

        [DependsOn(
        typeof(MyAppEntityFrameworkCoreModule),
        typeof(MyAppNHibDataModule),
        ...
        typeof(AbpHangfireAspNetCoreModule) 
    )]
    public class MyAppWebCoreModule : AbpModule
    {
    

    I get this exception at startup in UnitOfWorkBase.cs: System.InvalidCastException: "Unable to cast object of type 'Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork' to type 'Abp.NHibernate.Uow.NhUnitOfWork'." .. and similar ones

    however if the oder is reverse like so:

        [DependsOn(
        typeof(MyAppNHibDataModule),
        typeof(MyAppEntityFrameworkCoreModule),
        ...
        typeof(AbpHangfireAspNetCoreModule) 
    )]
    public class MyAppWebCoreModule : AbpModule
    {
    

    The result is that IRepository<User> gets resolved with an NH-Implementation of IRepository.

    I understand, why that happens and I know that castle windsor can be highly customized. But I rather want to ask especially what or how I should customize the resolution/registration of types.

    1. Is it at all possible to use NH and the AbpNHibernateModulein conjunction with EF?
      1. Or will it clash central components?
    2. If yes, how can it be accomplished and is there a forseen way?
    3. Can I claim land if I omit dependency of AbpNHibernateModule and register the AbpNHibernate classes myself with WC?
  • User Avatar
    1
    ipservant created

    OK, I came up with something and I share this with you here: Maybe there is also some inspiration for the future framework.

    Essentially I went without initializing the Abp.NHibernate module plus creating my own ISessionFactory singleton which is initialized in PreInitialize().

    //[DependsOn(typeof(AbpNHibernateModule))]
    [DependsOn(typeof(IPSwebCoreModule))]
    public class MyAppAdsDataModule : AbpModule
    {
            private ISessionFactory _sessionFactory;
        public override void PreInitialize()
        {
            var connStr = _appConfiguration["ConnectionStrings:AdsConnString"];
    
            var cfg = new NHibernate.Cfg.Configuration();
            _sessionFactory = Fluently.Configure(cfg)
                .Database(AdsIntegration.ConnectionString(connStr))
                .Mappings(m => m.HbmMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
                .BuildSessionFactory();
        }
    

    Problems arose with satisfying my Repoisitoy class - winsor-castle did not know how to resolve my custom repo.

    public class KarteiRepository : AdsRepositoryBase&lt;KARTEIKARTE, int&gt;, IKarteiRepository
    {
        public KarteiRepository(INhMySessionProvider sessionProvider) : base(sessionProvider)
        {
        }
    

    In order to avoid clashes (my code used to use ISessionProvider as ctor-Param. So I created my own INhMySessionProvider and programmed it like this:

    public interface INhMySessionProvider : ISessionProvider
    { }
    
    public class NhMySessionProvider : INhMySessionProvider
    {
        private readonly ISessionFactory f;
    
        public NhMySessionProvider(ISessionFactory f)
        {
            this.f = f;
        }
    
        public ISession Session
        {
            get
            {
                return f.OpenSession();
            }
        }
    }
    

    Then, the only thing needed to to (to let it be resolved) was to add some lines to my Data module's initialize method:

        public override void Initialize()
        {
            IocManager.IocContainer.Register(
                Component.For&lt;ISessionFactory&gt;().Instance(_sessionFactory).LifeStyle.Singleton,
                Component.For&lt;INhMySessionProvider&gt;().ImplementedBy&lt;NhMySessionProvider&gt;().LifeStyle.Transient);
    
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
        }
    

    PS. AdsIntegration.ConnectionString(connStr) is my customization for SAP Advanced DB. but this is a different topic.

    So eventually I did not need Apb.NHibernate. Still my reference is there... let's see

  • User Avatar
    0
    ismcagdas created
    Support Team

    Thanks a lot @ipservant :).