2

We are trying to implement a cloud database solution using nhibernate. We are using Azure SQL Database and we are in troubles at the time of set the connection. As the Authentication keyword is not allowed on the connection string, the only way to create a connection is to provide an Access Token. This property is not available on nhibernate. We were advised to create our own connection provider in order to achieve this. Our connection provides is:

using Project.dataaccessobjects;
using Project.security;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using NHibernate.Connection;
using System;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace Project.connection
{
    public partial class AzureSQLDatabase : DriverConnectionProvider
    {
        private string strSQLServer{ get; set; }
        private string strDatabase { get; set; }
        private string strTenantId { get; set; }
        private string strClientId { get; set; }
        private string strPassword { get; set; }
        public AzureSQLDatabase() : base()
        {
            strSQLServer = AzureKeyVaultDAO.getInstance().get("SQLServer");
            strDatabase = AzureKeyVaultDAO.getInstance().get("Database");
            strTenantId = AzureKeyVaultDAO.getInstance().get("TenantId");
            strClientId = AzureKeyVaultDAO.getInstance().get("ClientId");
            strPassword = AzureKeyVaultDAO.getInstance().get("Password");
        }
        private string accessToken()
        {
            const string ResourceUrl = "https://database.windows.net/";
            string AuthorityUrl = $"https://login.microsoftonline.com/{strTenantId}";
            AuthenticationContext objAuthenticationContext;
            Task<AuthenticationResult> objAuthenticationResult;
            ClientCredential objCredentials;
            objCredentials = new ClientCredential(strClientId, SecureText.getInstance().decrypt(strPassword));
            objAuthenticationContext = new AuthenticationContext(AuthorityUrl);
            objAuthenticationResult = objAuthenticationContext.AcquireTokenAsync(ResourceUrl, objCredentials);
            return objAuthenticationResult.Result.AccessToken;
        }
        public override DbConnection GetConnection(string connectionString)
        {
            DbConnection objConnection = new SqlConnection();
            try
            {
                objConnection.ConnectionString = connectionString;
                ((SqlConnection) objConnection).AccessToken = accessToken();
                objConnection.Open();
            }
            catch (Exception)
            {
                objConnection.Dispose();
                throw;
            }
            return objConnection;
        }
    }
}

Next is our HibernateUtil class

using Project.dataaccessobjects;
using Project.entities;
using NHibernate;
using NHibernate.Cfg;
using System.Collections.Generic;
namespace Project.hibernate
{
    public class HibernateUtil
    {
        private static ISessionFactory _sessionFactory;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    Configuration objConfiguration = new Configuration();
                    objConfiguration.SetProperties(properties());
                    objConfiguration.AddAssembly(typeof(Entity/Beam/Domain).Assembly);
                    _sessionFactory = objConfiguration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }
        private static Dictionary<string, string> properties()
        {
            Dictionary<string, string> obj = new Dictionary<string, string>();
            obj.Add("connection.provider", "Project.connection.AzureSQLDatabase");
            obj.Add("connection.connection_string", $"Server = tcp:{AzureKeyVaultDAO.getInstance().get("SQLServer")},1433; Initial Catalog = {AzureKeyVaultDAO.getInstance().get("Database")}; Persist Security Info = False; MultipleActiveResultSets = False; Encrypt = True; TrustServerCertificate = False");
            obj.Add("dialect", "NHibernate.Dialect.MsSqlAzure2008Dialect");
            obj.Add("show_sql", "true");
            return obj;
        }
        public static ISession OpenSession()
        {
            return SessionFactory.OpenSession();
        }
    }
}

At objConfiguration.BuildSessionFactory() we get the following exception:

NHibernate.HibernateException: 'Could not instantiate connection provider: Project.connection.AzureSQLDatabase' TypeLoadException: Could not load type Project.connection.AzureSQLDatabase. Possible cause: no assembly name specified.

Do you know what are we missing/skipping?

3 Answers 3

1

I found a better solution. Setting the property Environment.Hbm2ddlKeyWords, "none" allowed me to execute the BuildSessionFactory and open set the connection at SessionFactory.OpenSession().

namespace ExceptionsDB.hibernate
{
    public class HibernateUtil
    {
        private static ISessionFactory _sessionFactory;
        private static ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    Configuration objConfiguration = new Configuration();
                    objConfiguration.SetProperties(properties());
                    //Add all Entities
                    objConfiguration.AddAssembly(typeof(Entity/Bean/Domain).Assembly);
                    _sessionFactory = objConfiguration.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }
        private static Dictionary<string, string> properties()
        {
            Dictionary<string, string> obj = new Dictionary<string, string>();
            obj.Add("dialect", "NHibernate.Dialect.MsSqlAzure2008Dialect");
            obj.Add(Environment.Hbm2ddlKeyWords, "none");
            obj.Add("show_sql", "true");
            return obj;
        }
        public static ISession OpenSession()
        {
            //return SessionFactory.OpenSession(ADO.NET Connection); obsolete
            return SessionFactory.WithOptions().Connection(ADO.NET Connection).OpenSession();
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

The error message is pretty clear. You've specified a type name without an assembly name.

 obj.Add("connection.provider", "Project.connection.AzureSQLDatabase");

Which is not enough for NHibernate to find the type. To load a type you must know what Assembly it's in. From the docs:

The type of a custom IConnectionProvider implementation. eg. full.classname.of.ConnectionProvider if the Provider is built into NHibernate, or full.classname.of.ConnectionProvider, assembly if using an implementation of IConnectionProvider not included in NHibernate. The default is NHibernate.Connection.DriverConnectionProvider.

https://nhibernate.info/doc/nhibernate-reference/session-configuration.html

Comments

0

Here is the complete example:

using System;
using System.Data.SqlClient;
using Azure.Identity;
using Azure.Core;
using NHibernate;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Helpers;
using FluentNHibernate.Conventions.Helpers.Prebuilt;
using AzureManagedIdentityTest.Models;

namespace AzureManagedIdentityTest.Repository
{
    public class AzureDbRepository
    {
    
        const string _connectionString = "Server=tcp:your.database.windows.net,1433;Database=your_database_name";
        
        public TestModel GetTestModel(int id)
        {
            var testModel = new TestModel();
            
            var connection = new SqlConnection(_connectionString);
            var credential = new DefaultAzureCredential();
            var token = credential.GetToken(new TokenRequestContext(new[] { "https://database.windows.net/.default" }));
            connection.AccessToken = token.Token;

            var session = GetSession(connection);

            var result = session.Query<TestModel>().Where(x => x.Id == id).ToList().FirstOrDefault();
            testModel.FirstName = result.FirstName;
            testModel.LastName = result.LastName;
            
            connection.Close();
            connection.Dispose();

            return testModel 
        }
    
        private ISession GetSession(SqlConnection connection)
        {
            const string context = "web";
            
            ISessionFactory _sessionFactory;
            var objConfiguration = Fluently.Configure()
                .Mappings(x =>
                {
                    x.AutoMappings.Add(AutoMap.AssemblyOf<TestModel>()
                        .UseOverridesFromAssemblyOf<TestModel>());
                })
                .ExposeConfiguration(x => x.SetProperty("current_session_context_class", context))
                .ExposeConfiguration(x => x.SetProperty("adonet.batch_size", "100"))
                .BuildConfiguration();
                
            objConfiguration.SetProperties(properties());
            
            objConfiguration.AddAssembly(typeof(TestModel).Assembly);
            _sessionFactory = objConfiguration.BuildSessionFactory();

            var session = GetSessionFactory().WithOptions().Connection(connection).OpenSession();
            
            return session;
        }
        
        private Dictionary<string, string> properties()
        {
            var properties = new Dictionary<string, string>();
            properties.Add("dialect", 
           "NHibernate.Dialect.MsSqlAzure2008Dialect");
            properties.Add("hbm2ddl.keywords", "none");
            properties.Add("show_sql", "true");
            return properties;
        }   
        
    }   
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.