New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSessionContext in ASP.NET Core 2 #1632

Open
leandro86 opened this Issue Mar 30, 2018 · 14 comments

Comments

Projects
None yet
8 participants
@leandro86

leandro86 commented Mar 30, 2018

Hello,

I'm trying to use the WebSessionContext in a ASP.NET Core 2 application, but I get the following error when CurrentSessionContext is accesed:

Could not load file or assembly 'System.Web, Version=4.0.30319.42000, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

The problem seems to be in ReflectiveHttpContext.cs:

private static System.Type HttpContextType
{
	get
	{
		return
			System.Type.GetType(
				string.Format(
					"System.Web.HttpContext, System.Web, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
					Environment.Version));
	}
}

HttpContext is now in the assembly Microsoft.AspNetCore.Http.Abstractions. However, I don't believe it would be possible to load the current HttpContext by reflection, since now it doesn't have a "Current" property. ReflectiveHttpContext access the "Current" property of HttpContext here:

private static void CreateCurrentHttpContextGetter()
{
	PropertyInfo currentProperty = HttpContextType.GetProperty("Current", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
	Expression propertyExpression = Expression.Property(null, currentProperty);
	Expression convertedExpression = Expression.Convert(propertyExpression, typeof (object));
	HttpContextCurrentGetter = (Func<object>) Expression.Lambda(convertedExpression).Compile();		
}

The current HttpContext is now available through IHttpContextAccesor. See: https://stackoverflow.com/questions/31243068/access-the-current-httpcontext-in-asp-net-core

@fredericDelaporte

This comment has been minimized.

Member

fredericDelaporte commented Mar 31, 2018

Feature parity between full Framework/.Net Core about the HttpContext based session context seems hard to achieve, since the Core team is banning static accessors and tries hard to prevent/dissuade people of putting workarounds in place.

The main difficulties lie in the fact that NHibernate has been ported to .Net Core and not rewritten/re-architectured for it. It especially has not been rewritten as an injectable .Net Core service, meaning it has no mechanism out of the box for easily getting injected with other .Net Core services, like an IHttpContextAccessor. (Moreover this service is not guaranteed to be present. And many NHibernate setup may not require it either, so putting it as a mandatory dependency of NHibernate does not look as an option to me.)

I think this case would require a new kind of session context, which would also be a Core service to add in the pipeline like any service, allowing it to mandate and get any .Net Core dependencies it requires.

@leandro86

This comment has been minimized.

leandro86 commented Mar 31, 2018

Thanks for your answer. As a workaround, right now I'm doing this:

I created a new session context:

[Serializable]
public class AspNetCoreWebSessionContext : MapBasedSessionContext
{
	private const string SessionFactoryMapKey = "NHibernate.Context.AspNetCoreWebSessionContext.SessionFactoryMapKey";

	public static IHttpContextAccessor HttpContextAccessor { get; set; }

	public AspNetCoreWebSessionContext(ISessionFactoryImplementor factory) : base(factory)
	{
	}

	protected override IDictionary GetMap()
	{
		return HttpContextAccessor.HttpContext.Items[SessionFactoryMapKey] as IDictionary;
	}

	protected override void SetMap(IDictionary value)
	{
		HttpContextAccessor.HttpContext.Items[SessionFactoryMapKey] = value;
	}
}

And then, after building the session factory, I ask the container to resolve an instance of IHttpContextAccesor:

var sessionFactory = config.BuildSessionFactory();
AspNetCoreWebSessionContext.HttpContextAccessor = container.GetInstance<IHttpContextAccessor>();

It's also possible to create an instance of HttpContextAccessor directly inside AspNetCoreWebSessionContext, but that would require a dependency on Microsoft.AspNetCore.Http, instead of Microsoft.AspNetCore.Http.Abstractions.

What do you have in mind to do this the proper way?

@fredericDelaporte

This comment has been minimized.

Member

fredericDelaporte commented Mar 31, 2018

As a workaround you may use instead the AsyncLocalSessionContext, it should be suitable.

About providing a .Net Core compatible web session context, I would need to investigate the subject more.

@jpenniman

This comment has been minimized.

jpenniman commented Apr 4, 2018

In our ASP.Net Core apps, we don't use the WebSessionContext at all. We manage the ISessionFactory and ISession life cycle with the dependency injection built into ASP.Net Core...

services.AddSingleton<ISessionFactory>((provider)=>{ 
    var cfg = new NHibernate.Cfg.Configuration();
    // your config stuff here
    return cfg.BuildSessionFactory();
});

services.AddScoped<ISession>((provider)=>{
    var factory = provider.GetService<ISessionFactory>();
    return factory.OpenSession();
});

Then just inject ISession into what ever needs it. For example (over simplified for brevity):

public class CustomerController : Controller
{
    ISession session;

    public CustomerController(ISession session)
    {
        this.session = session;
    }

    public IEnumerable<Customer> Get()
    {
        return session.QueryOver<Customer>().List();
    }
}

We simplified it even further and created an extension method on IServiceProvider to handle all the boilerplate, so the Startup.cs in our projects just contains...

services.AddNHibernate();

We have been using this approach for the last couple years (since ASP.Net Core 1.0) and it has worked well for us.

@hazzik

This comment has been minimized.

Member

hazzik commented Apr 4, 2018

I think the solution would be to throw PlatformNotSupportedException on access to WebSessionContext. Thoughts?

@fredericDelaporte

This comment has been minimized.

Member

fredericDelaporte commented Apr 4, 2018

Well, that was somewhat my first thought. But since we have avoided this in the call context case for feature parity sake, it would have been better to find a solution for web context too.

But it will be still better to throw that error than failing as it does currently.

@hazzik

This comment has been minimized.

Member

hazzik commented Apr 4, 2018

I don't think we would be able to find a viable solution at the moment.

@fredericDelaporte

This comment has been minimized.

Member

fredericDelaporte commented Apr 5, 2018

I think the solution would be to throw PlatformNotSupportedException on access to WebSessionContext.

Thinking about it again, that may not be straightforward to do. For netcoreapp target, this is easy. But for netstandard, it is another story, since we do not really know what will be the executing runtime at compile time. It could even be the full framework after all.

Currently, there is no standardized way of detecting this. This is a work in progress in dotnet/corefx#17452.
Some workarounds are provided in dotnet/corefx#22293, like using this.

But maybe (even if unlikely) some other runtime than the full .Net Framework may support the HttpContext. So overall we may have better keeping this context working or failing as it does currently.

@hazzik

This comment has been minimized.

Member

hazzik commented Apr 11, 2018

I think the simplest fix would be to pass throwOnError: true into System.Type.GetType method in ReflectiveHttpContext.

@fredericDelaporte

This comment has been minimized.

Member

fredericDelaporte commented Apr 11, 2018

I think the simplest fix would be to pass throwOnError: true into System.Type.GetType method in ReflectiveHttpContext.

While the code would be more correct this way (avoiding a potential null reference exception), it will not change anything to the case reported here, because that is a case which already throws even if throwOnError is false.

@danilobreda

This comment has been minimized.

danilobreda commented Jun 4, 2018

A "deprecated" or something like that would be nice, because even on .net full, the use of a DI is suggestive for a right way that the plataform is supporting right now.

@shatl

This comment has been minimized.

shatl commented Jun 4, 2018

@leandro86 Using DI instead would be a better approach.
I had a lot of issues with HttpContext based approach when decided to move some code to run in background thread.

@leandroaraujo

This comment has been minimized.

leandroaraujo commented Aug 3, 2018

@jpenniman, @shatl

In our ASP.Net Core apps, we don't use the WebSessionContext at all. We manage the ISessionFactory and ISession life cycle with the dependency injection built into ASP.Net Core...

And about the CloseSession?
.Net Core DI disposes/close Session automatically or I need to close by my own?

@maca88

This comment has been minimized.

Contributor

maca88 commented Aug 3, 2018

.Net Core DI disposes/close Session automatically or I need to close by my own?

The session will be closed and disposed automatically when the service provider scope is disposed. .Net Core DI disposes all services that implements IDisposable interface when the scope that contains them is disposed. As NHibernate session implements IDisposable, it will be disposed when the scope ends and also closed if it not already closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment