ASP.NET 5 Controller dependency injection of concrete class with no interface in to controller - asp.net-core

Is it possible to use StructureMap to scan assemblies to be aware of concrete classes that do not implement interfaces? I am fairly new to StructureMap so not sure if this should be an obvious thing.
For context, below are the highlights of the classes I am working with. UserController depends on an instance of UserManager which depends on an instance of IUserRepository.
public interface IUserRepository { }
public class UserRepository { }
public class UserManager
{
public UserManager(IUserRepository repository) { }
}
public class UserController
{
public UserController(UserManager manager) { }
}
This is the code I have in my Startup.ConfigureServices method to do the scanning for DI:
// Setup dependencies using StructureMap
var container = new Container(x =>
{
x.Scan(s =>
{
s.AssemblyContainingType<UserRepository>();
s.WithDefaultConventions();
});
});
container.Populate(services);
The problem is I get the following error:
Unable to resolve service for type 'UserManager' while attempting to
activate 'UserController'.
If I add the following line to Startup.ConfigureServices then it works, but I am looking for a solution that doesn't require me to have a line for every manager. I have been thinking StructureMap assembly scanning could solve this but I am open to other solutions as well.
services.AddTransient<UserManager>();

Add .AddControllersAsServices() extention method to your services.AddMvc() call.
Result:
services.AddMvc().AddControllersAsServices();

Related

How to access data from API in .Net Core

I've not worked with .Net Core before but have a lot of experience with MVC and Entity Framework. My project has four distinct folders, API, DTO, Repository and WEB. The DTO folder has many model files which fits the data model. The API folder has a Controller file called ReferenceDataController and looks like this
[Route("api/[controller]")]
[ApiController]
public class ReferenceDataController : ControllerBase
{
private readonly IReferenceDataRepository _repository;
public ReferenceDataController(IReferenceDataRepository repository)
{
_repository = repository;
}
// GET api/values
[HttpGet]
public ActionResult<ReferenceData> GetReferenceData()
{
return _repository.GetReferenceData();
}
I'm told that if I call this GET method it will return a data object. How do I call this method in the API folder from my HomeController in my WEB folder?
First, in your web project, you need to do a little setup. Add a class like the following:
public class ReferenceDataService
{
private readonly HttpClient _httpClient;
public ReferenceDataService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<List<ReferenceData>> GetReferenceDataAsync(CancellationToken cancellationToken = default)
{
using (var response = await _httpClient.GetAsync("/api/referencedata", cancellationToken))
{
if (response.IsSuccessStatusCode())
{
return await response.Content.ReadAsAsync<List<ReferenceData>>();
}
return null;
}
}
}
Then, in ConfigureServices in Startup.cs:
services.AddHttpClient<ReferenceDataService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com");
// Use the actual URL for your API here. You also probably want to get this
// from `Configuration` rather than hard-coding it.
});
Finally, inject ReferenceDataService into your HomeController:
public class HomeController : Controller
{
private readonly ReferenceDataService _referenceDataService;
public HomeController(ReferenceDataService referenceDataService)
{
_referenceDataService = referenceDataService ?? throw new ArgumentNullException(nameof(referenceDataService));
}
// In your action(s):
// var data = await _referenceDataService.GetReferenceDataAsync(HttpContext.RequestAborted);
}
This is the quick and dirty code here. Things you should consider for improvement:
Use an interface for your service class(es), i.e. IReferenceDataService. That will make testing easier. In ConfigureServices:
services.AddHttpClient<IReferenceDataService, ReferenceDataService>(...);
Then, inject IReferenceDataService instead.
You can and should use the Polly extensions with AddHttpClient to support retry and exception handling polices. At the very least, you'd definitely want to add AddTransientHttpErrorPolicy:
services.AddHttpClient<ReferenceDataService>(...)
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
That will handle transient errors like temporarily being unable to connect to the API because it was restarted or something. You can find more info and more advanced configuration possibilities at the docs.
You should be using separate DTO classes. For brevity, I just used your (presumed) entity class ReferenceData. Instead, you should always use customized DTO classes that hold just the pieces of the data that you need to be available via the API. This way, you can control things like serialization as well as custom validation schemes, without conflicting with what's going on with your entity class. Additionally, the web project would only need to know about ReferenceDataDTO (or whatever), meaning you can share a library with your DTOs between the API and web projects and keep your DAL completely out of your web project.

Implement FluentSecurity 2.0.0 with Ninject MVC

Error activating ISecurityContext using binding from ISecurityContext to SecurityContext.
I'm getting the above error with FluentSecurity 2.0.0 when I'm trying to configure it with Ninject.Web.Mvc3 in an ASP.NET MVC 4 web application.
I think the internal IoC of FluentSecurity and the Ninject IoC may be clashing. Or I may be incorrectly setting up the DependencyResolver in the SecurityConfigurator.
I need to set it up with IoC as I need to get the UserRoles through an injected class.
public static class SecurityConfig
{
public static ISecurityConfiguration Configure()
{
SecurityConfigurator.Configure(
configuration =>
{
configuration.ResolveServicesUsing(
DependencyResolver.Current.GetServices,
DependencyResolver.Current.GetService);
configuration.DefaultPolicyViolationHandlerIs(() => new DefaultPolicyViolationHandler());
configuration.GetAuthenticationStatusFrom(
() => HttpContext.Current.User.Identity.IsAuthenticated);
configuration.GetRolesFrom(
() =>
((IPersonManager)DependencyResolver
.Current
.GetService(typeof(IPersonManager)))
.GetCurrentUserRoles());
configuration.ForAllControllers().DenyAnonymousAccess();
configuration.For<AdminController>().RequireAnyRole(Role.Administrator);
});
return SecurityConfiguration.Current;
}
}
Where am I going wrong? Is there another way I could achieve this?
I faced the same situation. It happened because Ninject throws an exception when cannot resolve a dependency. I solved it implementing my own ISecurityServiceLocator
public class FluentSecurityServiceLocator : ISecurityServiceLocator
{
public static IKernel Kernel { get; set; }
public object Resolve(Type typeToResolve)
{
return Kernel.TryGet(typeToResolve);
}
public IEnumerable<object> ResolveAll(Type typeToResolve)
{
if (!Kernel.GetBindings(typeToResolve).Any())
{
return new List<object>();
}
return Kernel.GetAll(typeToResolve);
}
}
I passed the kernel instance in my ninject configuration class
FluentSecurityServiceLocator.Kernel = kernel;
Hope this helps!
I'm not really familiar with Ninject but are you sure that DependencyResolver.Current.GetServices and DependencyResolver.Current.GetService won't throw an exception when FluentSecurity asks for something (like ISecurityContext) that is not registered with Ninject?
In structuremap there is a method called TryGetInstance that won't throw an exception when asking for something that is not registered in the container. You can read more on how FluentSecurity and IoC works here:
https://github.com/kristofferahl/FluentSecurity/wiki/IoC-container-integration

FluentSecurity and Ninject

Error activating IntPtr
I'm trying to configure FluentSecurity (v.1.4) with Ninject (v.3) in an ASP.NET MVC 4 application.
I can't set up the ResolveServicesUsing() configuration expression without throwing the above error.
SecurityConfigurator.Configure(
configuration =>
{
configuration.ResolveServicesUsing(
DependencyResolver.Current.GetServices,
DependencyResolver.Current.GetService);
...
I've also tried using another overload for ResolveServicesUsing()
configuration.ResolveServicesUsing(
type => DependencyResolver.Current.GetServices(type));
FluentSecurity needs to be configured with Ninject to inject the method for finding my users' roles and also for the PolicyViolationHandler implementations.
UPDATE
I've found I can leave out the offending lines and still have my GetRolesFrom() implementation called (hurrah):
configuration.GetRolesFrom(
() =>
((IPersonManager)DependencyResolver
.Current
.GetService(typeof(IPersonManager)))
.GetCurrentUserRoles());
I still can't get my PolicyViolationHandler to work, however:
public class RequireRolePolicyViolationHandler : IPolicyViolationHandler
{
public ActionResult Handle(PolicyViolationException exception)
{
return new RedirectToRouteResult(
new RouteValueDictionary(
new
{
action = "AccessDenied",
controller = "Home"
}));
}
}
I'm doing the binding in a NinjectModule like this:
public class SecurityModule : NinjectModule
{
public override void Load()
{
this.Kernel.Bind<IPolicyViolationHandler>()
.To<RequireRolePolicyViolationHandler>();
}
}
Error activating IntPtr
Unfortunately you havn't posted the complete StackTrace. But usually you will get this exception when injecting a Func to some class without having a binding or using the Factory extension.
I use Fluent Security with Ninject as IOC container.
In your Fluent Security configuration, you need to set the service locator to the NinjectServiceLocator.
public static void Configure(IKernel kernel)
{
var locator = new NinjectServiceLocator(kernel);
ServiceLocator.SetLocatorProvider(() => locator);
SecurityConfigurator.Configure(
configuration =>
{
configuration.GetAuthenticationStatusFrom(() => HttpContext.Current.User.Identity.IsAuthenticated);
....
}
You can get the locator here.
Hope this helps

Singleton with StructureMap custom convention in ASP.NET MVC 4

I am having an issue trying to get the singleton lifecycle to work with a custom convention in StructureMap.
Basically I have a custom registry type class that contains a dictionary that I would like to be a singleton so that it is created once at startup of the application.
I created a custom convention that will look at an attribute of a class and determine whether or not the class should be HttpContextScoped or Singleton.
The problem is that when I run the application with the Visual Studio debugger the constructor of the object that should be a singleton gets called every time the web page is loaded instead of happening once as I expected. It looks like the object is behaving as a HttpContextScoped instead of a Singleton.
Here are some details:
StructuremapMvc class in app_start folder
public static class StructuremapMvc
{
public static void Start()
{
IContainer container = IoC.Initialize();
DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver(container);
}
}
Ioc class
public static IContainer Initialize()
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.AssemblyContainingType<IConfigManager>();
scan.WithDefaultConventions();
scan.Convention<CustomConvention>();
});
CustomConvention : IRegistrationConvention
public void Process(Type type, Registry registry) public void Process(Type type, Registry registry)
{
var attributes = type.GetCustomAttributes(false);
if (attributes.Length > 0)
{
if (attributes[0] is SingletonAttribute)
{
registry.For(type).Singleton();
}
else if (attributes[0] is HttpContextScopedAttribute)
{
registry.For(type).HttpContextScoped();
}
}
}
[Singleton]
public class MyRegistry : IMyRegistry
This questions seems to be quite old but I'll trie to answer it anyway because there could be others which are experiencing the same problem with Structure map. In some cases singleton insances are created "per instance" referring to the instance where they are injected in. This means that you could have different instances of "singleton" when they are injected somewhere else. I've personally seen this behavior with WEBAPI inside MVC app.
The only way I could make it work as "true" global singleton is by using generic interface with specific type parameters to distinguish different types to be used:
public interface ITest<T>
{
}
public class Test1 : ITest<int>
{
}
public class Test2 : ITest<string>
{
}
Scan(x =>
{
x.TheCallingAssembly();
x.IncludeNamespace("MvcApplication1");
x.ConnectImplementationsToTypesClosing(typeof(ITest<>))
.OnAddedPluginTypes(a => a.LifecycleIs(InstanceScope.Singleton));
});
I know that this isn't as ellegant nor usable as approach described above but at least it works as expected. Other approach which works is to do standard mapping one-on-one like:
For<ISingleton>().Singleton().Use<Singleton>();

Ninject property binding, how to do correctly

I have installed Ninject (v4.0.30319) package in test project to test. Create test code below, unfortunately ValidateAbuse.Instance.Repository is always Null. Why Ninject do not bind repository to ValidateAbuse.Repository property?
Some of you may suggest to use constructor binding but I can't use it due to code structure. The below code is just example and I need to find a way to bind to property.
Test method which always fail
[TestMethod]
public void PropertyInjection()
{
using (IKernel kernel = new StandardKernel())
{
kernel.Bind<ISettingsRepository>().To<SettingsRepository>();
Assert.IsNotNull(ValidateAbuse.Instance.Repository);
}
}
The repository interface
public interface ISettingsRepository
{
List<string> GetIpAbuseList();
List<string> GetSourceAbuseList();
}
The repository implementation
public class SettingsRepository : ISettingsRepository
{
public List<string> GetIpAbuseList()
{
return DataAccess.Instance.Abuses.Where(p => p.TypeId == 1).Select(p => p.Source).ToList();
}
public List<string> GetSourceAbuseList()
{
return DataAccess.Instance.Abuses.Where(p => p.TypeId == 2).Select(p => p.Source).ToList();
}
}
The class to which I am trying to bind repository
public class ValidateAbuse
{
[Inject]
public ISettingsRepository Repository { get; set; }
public static ValidateAbuse Instance = new ValidateAbuse();
}
Ninject will only bind properties on an object when it creates an instance of that object. Since you are creating the instance of ValidateAbuse rather than Ninject creating it, it won't know anything about it and therefore be unable to set the property values upon creation.
EDIT:
You should remove the static singleton from ValidateAbuse and allow Ninject to manage it as a singleton.
kernel.Bind<ValidateAbuse>().ToSelf().InSingletonScope();
Then when you ask Ninject to create any class that needs an instance of ValidateAbuse, it will always get the same instance.
It seems like you don't fully understand how Ninject works or how to implement it so I would suggest you read the wiki https://github.com/ninject/ninject/wiki/How-Injection-Works and follow some more basic examples before trying to wire it into an existing application.