Base solution for your next web application
Open Closed

Problems with Insert @ NHibernate IRepository #6954


User avatar
0
ipservant created

Hi, I am forced to use a very special DBMS and we are happy that it at least works with NHibernate. Thus I am using ABP-NHibernate stuff.

So far it works quite well (after some crunches in the beginning). I get my data off the DB and I can use sync LINQ at least. But when it comes to writing to the DB it seeminly works out NOOPs. At least the Debugging measures of NHibernate do not print any SQL insert statements.

What I am trying to do: some controller calls this Manager class' SaveAsync:

    private readonly IObjekteRepository _objRepo;

    public async Task SaveAsync(OBJEKTE entity)
    {
        try
        {
            OBJEKTE res = await _objRepo.InsertAsync(entity);
        }
        catch (Exception h)
        {
            //works well actually
        }
    }

The IObjekteRepository is just an empty derivation of yours in a 2 step process (all needed for DI):

 public class ObjekteRepository : AdsRepositoryBase<OBJEKTE, string>, IObjekteRepository
 {
    public ObjekteRepository(INhIpsSessionProvider sessionProvider) : base(sessionProvider)
    {
    }
}

and finally :

/// <summary>
/// Base class for all repositories based on ADS via NHibernate in this application.
/// </summary>
public abstract class AdsRepositoryBase<TEntity, TPrimaryKey> : NhRepositoryBase<TEntity, TPrimaryKey>
    where TEntity : class, IEntity<TPrimaryKey>
{
    protected AdsRepositoryBase(ISessionProvider sessionProvider)
        : base(sessionProvider)
    {
    }

    //add common methods for all repositories
}

So because I do not directly use NH but via your NhRepository, let me understand what's going wrong with it? is it because of the string Template as ID? Writing not supported?

I have also implemented a generator for NH and if this is commented in, it will also be invoked. Just the INSERT never happens. I boiled everything down to a very simple insertion but it is not even shown up in the log. Still I am getting a success result. What is still going wrong? File OBJEKTE.hbm.xml:

<class name = "OBJEKTE" > <id name="Id" column="INT_ID"> <!--<generator class="IPSweb.AdsNHibernate.Gen.IpsHiLoGenerator, IPSweb.AdsNHibernate" > </generator>--> </id>

EDIT: Well actually after overriding the Insert method of the Repository using my own implementation of Save() NHibernate still won't do an Insert to the DBMS. Seems like a NHbiernate with ADS problem. But any hints are welcome


8 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    Can you try to use NHibernate without using ABP to see if it works?

  • User Avatar
    0
    ipservant created

    Sorry I have to reopen this. I desperately need a success.

    I created a test project using bare NHibernate. I even used the same Entities and the same table(file) ... it worked. So why would it not work with ABP in addition? The test project is able to read the table and insert into it (just after calling Flush). The Entity class looks nearly the same so does the hbm.xml. So far it uses a different ID generator but that generator has never really been the problem. I even replaced it with the same uuid.hex but it makes no differnce. It is actually the only piece that produces SQL towards the DB.

    Still my problem is: I can select data out of ABP using NHibernate and my funky DBMS. BUT I cannot Insert any data. (seemingly rolled back?) NO Exceptions happen if the supposed code is running.

    Now I am fiddling in the object trees of the UnitOfWork stuff and transactions. Because my suspicion is that somehow a rolback happens. When I use a UnitOfWork-Manager and/or an [UnitOfWorkAttribute], I can see three filters in Abp.Runtime.Session.ClaimsAbpSession

    1. SoftDelete (enabled)
    2. MustHaveTenant (diabled)
    3. MayHaveTenant (enabled)

    More things to considder are:

    • The result of me 'calling' my controller is : {"result":null,"targetUrl":null,"success":true,"error":null,"unAuthorizedRequest":false,"__abp":true}
    • In order to be faster in testing, I created an unauthenticated simple Controller that I can 'directly call' from the web browser. Thus I am not logged in, which may be a problem.
    • When I do both, BeginWork with a UnitOfWorkManager plus have the Attribute, I end up getting this:

    Abp.AbpException: "Did not call Complete method of a unit of work."

    In order to make it work in my test project I had to call Session.Flush()

                OBJEKTE o = new OBJEKTE()
                {
                    ID_AKTE = "0000001235",
                   ...
                    ONLDATUM = System.DateTime.UtcNow,
                    SAFETYLVL = "",
                    ONLSTATUS = "",
                    DATFTS = null,
                };
                List<OBJEKTE> all = sess.Query<OBJEKTE>().ToList();
    
                string generatedId = (string)sess.Save(o);
                sess.Flush();
    
    
  • User Avatar
    0
    maliming created
    Support Team

    @ipservant
    Can you provide a project to reproduce the problem?

  • User Avatar
    0
    ipservant created

    Yes. i am happy to do so. As it will be the stripped down project of the ongoing development, can I send it to you as a PM?

  • User Avatar
    0
    maliming created
    Support Team

    You can send mail to me. [email protected]

  • User Avatar
    0
    maliming created
    Support Team

    The problem is that each method call of the ObjekteRepository will open a new Session.

    Because you are not using the unit of work of ABP NH. For details, you can refer to the source code of ABP NH.

    And you use ABP NH way I think there is a problem. You are not dependent on AbpNHibernateModule.

    The current solution to your problem temporarily is:

  • User Avatar
    0
    ipservant created

    First of all thanks. In the test program it works. Even though I don't understand yet, why. And only the Async version. Trying the same on the non-async will not lead to a success. But let's digg deeper into the root causes. Your** Abp.NHibernate asm** and there the AbpNHibernateModule does some more magic, which I could not replicate yet. The thing is: I certainly tried to depend on the NHModule already a while ago:

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

    but it turns out that once enabled, the module registers all standard facilties on it's. So all the other IRepository<T>s or similar end up becoming an NHibrernate Repo. So the whole existing EF-Based application does not work anymore. Example-Exception below. Sorry but I have to have two OR-Mappers in my application for two very differend DBMSs.

    So what do I need to register differently or do different in my SessionProvider? I suppose, I have to register a shim in Kestrel to create the (NH-based) wrapping transactions before any call to a NH-Reposoitory....

    Hmm. So this happens if the Module dependency is declared: Also the User repository is realized as NH as the facility is now on NH

    HibernateException: Unable to locate persister for the entity named 'IPSweb.Authorization.Users.User'.
    The persister define the persistence strategy for an entity.
    Possible causes:
    - The mapping for 'IPSweb.Authorization.Users.User' was not added to the NHibernate configuration.
    NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType)
    Stack Query Cookies Headers 
    HibernateException: Unable to locate persister for the entity named 'IPSweb.Authorization.Users.User'. The persister define the persistence strategy for an entity. Possible causes: - The mapping for 'IPSweb.Authorization.Users.User' was not added to the NHibernate configuration. 
    NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType)
    NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType)
    NHibernate.Impl.SessionImpl.Get(string entityName, object id)
    NHibernate.Impl.SessionImpl.Get<T>(object id)
    Abp.Domain.Repositories.AbpRepositoryBase<TEntity, TPrimaryKey>.FirstOrDefaultAsync(TPrimaryKey id) in AbpRepositoryBase.cs
    Castle.Proxies.Invocations.IRepository`2_FirstOrDefaultAsync_8.InvokeMethodOnTarget()
    Castle.DynamicProxy.AbstractInvocation.Proceed()
    Abp.Domain.Uow.UnitOfWorkInterceptor.PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options) in UnitOfWorkInterceptor.cs
    Castle.DynamicProxy.AbstractInvocation.Proceed()
    Abp.Domain.Uow.UnitOfWorkInterceptor.PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options) in UnitOfWorkInterceptor.cs
    Castle.DynamicProxy.AbstractInvocation.Proceed()
    Castle.Proxies.IRepository`2Proxy_2.FirstOrDefaultAsync(long id)
    Abp.Authorization.Users.AbpUserStore<TRole, TUser>.FindByIdAsync(string userId, CancellationToken cancellationToken) in AbpUserStore.cs
    
  • User Avatar
    0
    maliming created
    Support Team

    hi

    The problem is that each method call of the ObjekteRepository will open a new Session.

    see: https://github.com/aspnetboilerplate/aspnetboilerplate/blob/dev/src/Abp.NHibernate/NHibernate/Repositories/NhRepositoryBaseOfTEntityAndTPrimaryKey.cs#L24

    I mean if your module doesn't depend on AbpNHibernateModule, then some of the features in the AbpNHibernateModule module won't work.

    However, I think AbpNHibernateModule and EFCore seem to be difficult to coexist.

    You can still use the solution I mentioned above,