spring-net
spring-net copied to clipboard
Changes in Spring.Core required for Asp.Net Mvc Core
I've tried to use Spring.Net together with Asp.Net Mvc . I've found that Asp.Net MVC tries to register open generics in the container which is not supported. Few examples of such types: Microsoft.Extensions.Logging.ILogger<>, IOptions<>, IOptionsFactory<>. This is important because big part if not majority of .NET applications are Asp.Net MVC applications these days. Spring.NET is among few IoC frameworks which doesn't support open generics. Unfortunately adding this to Spring.Core is far beyond my skills.
@lahma Maybe you have an idea which should be the direction of Spring.NET for ASP.NET core. There are multiple attitudes so far - replacing built-in IoC or just extending it.
So far I've failed with writing any IServiceProvider
mostly because of typed fashion of Core IoC. Also three scopes were difficult - Transient, Scoped, Singleton.
So my current workarounds are two:
- Controllers are created by standard IoC, some parameters injected via constructor and Spring ones assigned via ServiceLocator.
- Inside
Startup.ConfigureServices()
are controllers registered with lambdas, using ServiceLocator again.
@mashbrno , would you mind sharing your prototype?
Finally I used a very simplistic approach which suffice my needs.
There is an Activator
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Spring.Context;
using Spring.Context.Support;
namespace Spring.AspNetCore
{
public class SpringControllerActivator : IControllerActivator
{
private IApplicationContext _ctx;
public object Create(ControllerContext actionContext)
{
var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();
return Context.GetObject(controllerType.Name);
}
public virtual void Release(ControllerContext context, object controller)
{
}
private IApplicationContext Context
{
get
{
if (_ctx == null)
_ctx = ContextRegistry.GetContext();
return _ctx;
}
}
}
}
registred in Startup.ConfigureServices()
like services.AddSingleton<IControllerActivator, SpringControllerActivator>();
and then two Service Locators to share object created by the built-in IoC and vice-versa. Globally stored variable IApplicationBuilder.ApplicationServices
fails probably when working with scopes, but to generate URLs and action links within Spring controllers works fine.
When a view needs to access a service defined by Spring the other ServiceLocator returning Spring context objects is used.
@mashbrno , When you say, service locator, You mean using
IServiceProvider OR Spring.Objects.Factory.IObjectFactory (http://www.springframework.net/doc-1.1-M1/sdk/2.0/html/Spring.Core~Spring.Objects.Factory.IObjectFactory~GetObject.html)
constructor(IServiceProvider services)
{
this.services = services;
}
private async Task DoWork(CancellationToken stoppingToken)
{
_logger.LogInformation(
"Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWork(stoppingToken);
}
}
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#consuming-a-scoped-service-in-a-background-task
By service locator I mean the pattern.
In MVC views, where I need a Spring defined service I use: ContextRegistry.GetContext()[<name>]
and within controllers where I need an object defined by Microsoft IoC:
public class Startup
{
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
ServiceProvider = app.ApplicationServices;
...
}
public static IServiceProvider ServiceProvider { get; private set; }
}
@mashbrno , Thanks for your response, I still cannot figure out how you build the Spring container as it only supports XML based configuration on the other hand ASP.NET DI uses code based approach.
Does this still work?
IApplicationContext ctx =return new XmlApplicationContext("application-context.xml");
Yes (I assume the return
keyword is a typo), you can create the context in the very same way as for any console app. I personally use Spring.FluentContext which has many benefits (checks the syntax during build, syntax highliting), but also drawbacks as lambas require properties to have public getters.
@mashbrno , Have you tried running this cross platform? Our main motivation moving from spring to asp.net core was cross platform.
If you mean containers then yes, I run this on both Windows and Kubernetes. As moving to ASP.NET Core is challenging itself, you should also consider to swap IoC as well. Autofac or SimpleInjector leverage tons of new features since .NET 3.5 like generics, lambdas, scopes. They're also proven to be faster.
The reason why I stick with Spring.NET is the possibility to create deffered child application contexts. Superb Spring's Expression and Validation frameworks I believe can be used independently with any IoC.
@mashbrno , What version of Spring.net are you using on Linux - source or Nuget link of spring.net please? We tried to build from source but faced compilation issues and lot of tests failing.
I use my own set which are built from the public source. Just lahma was not producing any .NET Standard packages 2 years ago. Finally he manage to access the official repo, so they exist again.
Adding my answer w.r.t. Linux usage here too.
Tests are run on both Windows and Linux. So the library should work on both platforms, but there might be some differences not spotted yet as 3.0 brought the cross-platform support.
Didn't know (or remember) there's Spring.FluentContext, ideally it would use the new 3.0 release if there's nothing blocking to do so anymore.
@mashbrno , Microsoft recommends not to capture ServiceProvider as Static property https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines Avoid static access to services. For example, avoid capturing IApplicationBuilder.ApplicationServices as a static field or property for use elsewhere.
@maulik-modi I completely agree and I mentioned my concerns in previous comments. But as my case there are very few objects and I use it to generate action links only it just works. The rest is handled by Spring.
I checked Microsoft implementation of Dependency Injection abstractions
@lahma, Can you please confirm if this is true as raised by @robsosno ? . Spring.NET is among few IoC frameworks which doesn't support open generics. Unfortunately adding this to Spring.Core is far beyond my skills.
Can you please confirm if this is true as raised by @robsosno ? . Spring.NET is among few IoC frameworks which doesn't support open generics. Unfortunately adding this to Spring.Core is far beyond my skills.
I believe this is the state of matters.