Where should I put my NHibernate SchemaExport method? - nhibernate

Where should I put my NHibernate SchemaExport method and how should I call it when I decide to recreate the database?
Should I leave this in my startup project (an asp.net mvc project)? Should I create a seperate console project just for exporting my schema?
I think these questions all originate from the fact that I don't want schema export to run every time the web app starts.
I'm using fluent nhibernate if that makes a difference.

I would factor this out into a seperate assembly; you could then use this from a variety of places (console app, integration test setup, installer, etc).

As an idea: you could place it in a ProjectInstaller that optionally takes a command line argument. So you wouldn't have to have an extra console app just for that.

Personally I use two Tests (using Nunit in this case) to create or update the database. In both cases, I only generate the script, as I want full control as to when the database gets created or updated.
[Test]
[Ignore]
public void Create_Database_Schema_From_MappingFiles()
{
Configuration cfg = new Configuration();
cfg.Configure();
var schema = new SchemaExport(cfg);
schema.Create(true, false);
}
[Test]
[Ignore]
public void Update_an_existing_database_schema()
{
Configuration cfg = new Configuration();
cfg.Configure();
var update = new SchemaUpdate(cfg);
update.Execute(true, false);
}

Related

Get service from WebApplicationFactory<T> in ASP.NET Core integration tests

I want to set up my tests using WebApplicationFactory<T> as detailed in
Integration tests in ASP.NET Core.
Before some of my tests I need to use a service configured in the real Startup class to set things up.
The problem I have is that I can't see a way to get a service from the factory.
I could get a service from factory.Server using factory.Host.Services.GetRequiredService<ITheType>(); except that factory.Server is null until factory.CreateClient(); has been called.
Is there any way that I am missing to get a service using the factory?
Thanks.
You need to create a scope from service provider to get necessary service:
using (var scope = AppFactory.Server.Host.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyDatabaseContext>();
}
Please pardon me. I know you ask for Net Core 2.1, but since v3.1+ people got here as well...
My project uses Net Core 3.1. When I use AppFactory.Server.Host.Services.CreateScope() like Alexey Starchikov's suggestion, I encounter this error.
The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.
Noted here, by designs.
So I use the below approach. I put database seeding in the constructor of the test class. Note that, I do not have to call factory.CreateClient(). I create client variables in test methods as usual.
using (var scope = this.factory.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();
// Seeding ...
dbContext.SaveChanges();
}
In APS.NET 6 I had to use _factory.Services.CreateScope():
using var scope = _factory.Services.CreateScope();
var sender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
await sender.SendAsync("Hello");

Create role using entity framework core migrations

I'm creating my 4th migrations script with EF Core (2.0.0). In there I want to add a few roles to the database.
The problem is, is that I'm not really sure how to do this. Currently I have this:
protected override void Up(MigrationBuilder migrationBuilder)
{
// todo: Pass connection string somehow..?
var opt = new DbContextOptions<ApplicationContext>();
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationContext(opt)));
//if (!roleManager.RoleExists("ROLE NAME"))
//{
// todo: create the role...
//}
}
But creating the RoleManager like that gives me the following error:
There is no argument given that corresponds to the required formal
parameter 'roleValidators' of
'RoleManager.RoleManager(IRoleStore,
IEnumerable>, ILookupNormalizer,
IdentityErrorDescriber, ILogger>)'
I'm not sure how to solve this problem. I couldn't find any info on how to do this properly in .NET Core using migrations.
I'm facing two issues in this code:
I'm trying to create an instance of the DbContext somehow. Shouldn't I be able to get the DbContext from within my migrations code?
Instantiating the RoleManager like this doesn't work and needs to be resolved.
How can I solve these problems?
The Up method is basically an instruction file that tells EF's database migrator how to generate a database upgrade script. The method is executed when the script is generated. Doing any data manipulation there is absolutely out of place. EF core doesn't support seeding yet, so you have to add missing roles when the application starts, for example by something like this.

connection string injection

I am trying to learn Ninject and started with what I thought is very simple thing. Can't make it work. Obviously, I am missing something basic.
So, I have this little console application that listens for WCF calls on a certain port and saves data that come via WCF to a database. There are 3 projects in the solution: 1. data access library, 2. WCF stuff and 3. console acting as a host. Ninject is not used yet. So the dependencies between projects are like this: 3 -> 2 -> 1
I want to start with injecting connection string that console host takes from its config into data access library. Googling for ninjection of connection string brought some examples, but they are not complete.
One of the examples suggested to bind in the host's Main() like this:
static void Main(string[] args)
{
new StandardKernel().Bind<ConnectionStringProvider>().ToConstant(
new ConnectionStringProvider { ConnectionString = Config.ConnectionString });
}
where ConnectionStrinProvider is a simple class that contains only one property ConnectionString. What I can't figure out is how do I instantiate ConnectionStrinProvider in the data access library. I tried
var csprovider = new StandardKernel().Get<ConnectionStringProvider>();
it doesn't work - meaning that it returns new instance of the provider instead of the one that was created during binding. I also tried to add .InSingletonScope() to the binding, with the same result.
You need to keep a reference to the kernel you set up. It doesn't work if you instantiate it every time.
public static IKernel Ninject {get; private set;}
static void Main(string[] args)
{
Ninject = new StandardKernel()
Ninject.Bind<ConnectionStringProvider>().ToConstant(
new ConnectionStringProvider { ConnectionString = Config.ConnectionString });
}
On the consummer side, you can call the Ninject static property on your main.
Obvious note aside: this is sample code, on production code you may want to make a better design for that global static variable.
The kernel is what keeps track of all the bindings for you. However, you are creating a new instance each time. That won't work. Instead, create the kernel and then store it off (here I'm storing it off in a local variable, but you'd probably want to store it in a field in some class):
var connectionStringProvider = new ConnectionStringProvider { ConnectionString = Config.ConnectionString };
var kernel = new StandardKernel().Bind<ConnectionStringProvider>().ToConstant(connectionStringProvider);
Now obtain instances by accessing the existing kernel.
var csprovider = kernel.Get<ConnectionStringProvider>();
That being said, using it in this fashion is the wrong way to go about it, as this pattern is known as a service locator pattern, which is the antitheses of dependency injection. Generally speaking, you have a top-level class (for example, your application class with the Main method) that is either obtained via Kernel.Get or injected via Kernel.Inject, and all other dependencies are injected normally through constructors or [Inject]'ed properties.
Also, there are usually plugins available for most situations so that you don't have to instantiate the kernel yourself. However, I'm not aware of one for console apps.

Single website multiple connection strings using asp mvc 2 and nhibernate

In my website i use ASP MVC 2 + Fluent NHibernate as orm, StructureMap for IoC container.
There are several databases with identical metadata(and so entities and mappings are the same). On LogOn page user fiils in login, password, rememberme and chooses his server from dropdownlist (in fact he chooses database).
Web.config contains all connstrings and we can assume that they won't be changed in run-time.
I suppose that it is required to have one session factory per database.
Before using multiple databases, i loaded classes to my StructureMap ObjectFactory in Application_Start
ObjectFactory.Initialize(init => init.AddRegistry<ObjectRegistry>());
ObjectFactory.Configure(conf => conf.AddRegistry<NhibernateRegistry>());
NhibernateRegistry class:
public class NhibernateRegistry : Registry
{
public NhibernateRegistry()
{
var sessionFactory = NhibernateConfiguration.Configuration.BuildSessionFactory();
For<Configuration>().Singleton().Use(
NhibernateConfiguration.Configuration);
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped().Use(
ctx => ctx.GetInstance<ISessionFactory>().GetCurrentSession());
}
}
In Application_BeginRequest i bind opened nhibernate session to asp session(nhibernate session per request) and in EndRequest i unbind them:
protected void Application_BeginRequest(
object sender, EventArgs e)
{
CurrentSessionContext.Bind(ObjectFactory.GetInstance<ISessionFactory>().OpenSession());
}
Q1: How can i realize what SessionFactory should i use according to authenticated user?
is it something like UserData filled with database name (i use simple FormsAuthentication)
For logging i use log4net, namely AdoNetAppender which contains connectionString(in xml, of course).
Q2: How can i manage multiple connection strings for this database appender, so logs would be written to current database? I have no idea how to do that except changing xml all the time and reseting xml configuration, but its really bad solution.
I suppose that it is required to have one session factory per database.
No; you can do just fine with one session factory for both databases.
You just supply an opened IDbConnection as a param to the OpenSession() method of ISessionFactory.
By doing so, you'll lose the possibility for a second level cache, but that might not be a problem.
If you want the second level cache, you need to implement you're own DriverConnectionProvider and supply it via fluent nh's Provider<TYourDriverConnectionProvider>() method.

How should I be configuring spring and hibernate so that my Integration Tests replicate properly the behaviour of the Web application?

We have a web application based on NSpring 1.2 and NHibernate 2 and use HibernateTemplate. The web application is configured to use OpenSessionInViewModule.
In our integration tests however the hibernate session is marked as InitDeferredClose and for the most part this works fine for our tests. There are some service functions that work fine in the context of the web application but fail when called from a test harness.
For example : I have one function that creates and updates a number of objects within a transaction. The function is marked with the Transaction attribute but the test fails with the message :
Illegal attempt to associate a collection with two open sessions
When called from the web application the transaction completes successfully
How should I be configuring spring and hibernate so that my Integration Tests replicate properly the functionnality of the Web application ?
You should create a SessionScope instance in the SetUp part of test and then close (dispose) it at the end (TearDown). This should mimic OSIV model quite well. Here's a simplified sample:
using System;
using NHibernate;
using NUnit.Framework;
using Spring.Data.NHibernate.Support;
[TestFixture]
public class OsivKindOfTest
{
private SessionScope scope;
// get LocalSessionFactoryObject from somewhere
// see Spring.Testing.NUnit and auto-injection
private ISessionFactory sessionFactory;
[SetUp]
public void OnSetUp()
{
scope = new SessionScope(sessionFactory, null, true, FlushMode.Never, true);
}
public void TestSomething()
{
// just a dummy demo
Console.WriteLine(sessionFactory.GetCurrentSession().Statistics.EntityCount);
}
[TearDown]
public void TearDown()
{
if (scope != null)
{
scope.Dispose();
}
}
}
This sample expects that Spring's LocalSessionFactoryObject has ExposeTransactionAwareSessionFactory set to true
I have rewritten my integration test fixtures to inherit from the AbstractTransactionalDbProviderSpringContextTests class provided by the Spring testing framework.
This indeed runs each test in its own transaction with a (by default) rollback at the end of the test. Apart from solving the problem of two open sessions, my tests run a lot quicker (no need to clear the database each time).
An important point to keep in mind when using the spring testing framework is that it is necessary to ensure the spring application context of the application code and that of the test framework are the one and the same otherwise the outer transaction and the inner transaction would be created by two different Hibernate Session Factories