Adding services in asp.net core project - asp.net-core

Is there a way to map all my repository objects to its interfaces in a single line. I donĀ“t want to repeat my self in declarations like these:
services.AddScoped<Repository.Interfaces.IModeloRepository, Repository.ModeloRepository>();
services.AddScoped<Repository.Interfaces.IMunicipioRepository, Repository.MunicipioRepository>();
services.AddScoped<Repository.Interfaces.IPeriodoRepository, Repository.PeriodoRepository>();
services.AddScoped<Repository.Interfaces.IPlanRepository, Repository.PlanRepository>();
Here is a declaration of one of these repositories:
public interface IChatRepository : IRepository<Models.Chat>
I already tried something like this:
services.AddScoped(typeof(Repository.Common.IRepository<>), typeof(Repository.Common.BaseRepository<>));
But gets the following error:
Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
An unhandled exception has occurred: Unable to resolve service for type 'SqlExpress.Repository.Interfaces.IChatRepository' while attempting to activate 'SqlExpress.Helpers.LessonTagHelper'.
System.InvalidOperationException: Unable to resolve service for type 'SqlExpress.Repository.Interfaces.IChatRepository' while attempting to activate '
SqlExpress.Helpers.LessonTagHelper'.
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )

Unfortunately, the built in DI container in ASP.NET Core is relatively simplistic. If you would like to use more advanced features like these, then you will need to use a different container.
The example below uses StructureMap as that's what I'm familiar with, but it is probably also possible with Autofac, Ninject etc.
Add the StructureMap library to project.json
"StructureMap.Dnx": "0.5.1-rc2-final"
Configure the DI container to use StructureMap, with naming conventions:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddControllersAsServices();
// other service configuration
// Create a new StructureMap container
var container = new Container();
container.Configure(config =>
{
//add all the services that are already configured
config.Populate(services);
config.Scan(_ =>
{
_.WithDefaultConventions();
_.AssemblyContainingType<Startup>();
_.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
});
});
//set ASP.NET Core to use the StructureMap container to build types
return container.GetInstance<IServiceProvider>();
}
It is worth checking our the documentation to see exactly how this works, but the default convention is to automatically register interface types such as IMyInterestingType with their implementation called MyInterestingType.
By using ConnectImplementationsToTypesClosing, each IRepository<> should also be registered to it's implementation.

Related

.Net Core : Class Library to connect to DB, DAL, User Secret and Asp.Net Core's Configuration

I have the following :
a class library with connection classes such as connection, command, parameter
a DAL with entities, mapper, interface, services as well as a static class that holds hard coded connectionString and InvariantName.
an Asp.Net Core project
References :
DAL has a reference to the class library to make use of its connection class to which it provides connectionString and InvariantName thanks to its static class etc..
Asp.Net has a reference to the DAL.
What I want :
I now want to use the User Secrets to store hard coded sensitive data connections and get rid off the static class.
I know I can use the the Asp.Net Core startup.cs to read the settings from Configuration and make use of binding to store them into a class and use DI.
My guess :
DI seems "easy" when used inside an Asp controller. But I need the settings values (connectionString and InvariantName) outside the Asp.Net Core to be injected into a constructor of a class somewhere in my DAL.
I guess I would then need to have to reference the Asp.Net Core project to my DAL. But then I would end up with a circular reference (DAL to Asp.Net Core and the opposite).
So what's the solution?
Have an intermediate library class into which I would retreive the settings values from Asp.Net Core and then pass them to my DAL (to prevent circular reference)?
Manually recreate the "Configuration process" inside the DAL and get settings there directly
Or something else that I don't know?
Ps : I am new in development and only have a few projects'experience in Asp.Net Framework so far..and it's my first Asp.Net Core project
I know I can use the the Asp.Net Core startup.cs to read the settings from Configuration and make use of binding to store them into a class and use DI
You already answered your own question with this. This is the correct and recommended behavior to setup DI for 3rd party libs and configurations. If you want to avoid clutter in Startup class, create an extension method:
namespace Microsoft.Extensions.DependencyInjetion
{
public static MyLibraryCollectionExtensions
{
public static IServiceCollection AddMyLibrary(this IServiceCollection services)
{
services.AddDbContext<MyDbContext>(...);
}
}
}
to register your classes. Alternatively, extend the method to accept a parameter delegate to configure it
namespace Microsoft.Extensions.DependencyInjetion
{
public static MyLibraryCollectionExtensions
{
public static IServiceCollection AddMyLibrary(this IServiceCollection services, Action<MyOptions> setup)
{
var defaultOptions = ... // i.e. new MyOptions();
// pass default options to be modified by the delegate
setup?.Invoke(defaultOptions);
// your registrations
services.AddDbContext<MyDbContext>(...);
}
}
}
And all the user has to do in your library is add
services.AddMyLibrary();
// or with setup
services.AddMyLibrary(config =>
{
config.MyConnectionString = Configuration.GetConnectionString("MyContext");
});
and store the connection string in the appsettings.json.
{
"ConnectionStrings":
{
"MyContext" : "MyConnectionString here"
}
}
I finally used the ConfigurationBuilder to get values from the appsettings.json file.
It's probably not the right way to do it but it is working with my DAL and Connection dlls.
In case it helps anyone else :

Shared service provider NServiceBus and ASPNET Core

I'm creating a way to publish integration events via NServiceBus that are published from within an operation executed in a handler. The path I've chosen is bridge the IIntegrationEventProvider with IEventCollectionPublisher to get the published events from domain layer.
public sealed class Bridge : IIntegrationEventProvider /* Infrastructure */,
IEventCollectionPublisher /* Domain */
{
private readonly List<object> _events = new List<object>();
void IEventCollectionPublisher.Publish(object domainEvent) { _events.Add(domainEvent): }
IReadOnlyCollection IIntegrationEventProvider.GetEvents() => _events;
}
Since NServiceBus has its own service provider (IBuilder) I need to resolve the class doing the application operation from the IServiceProvider that is made available to pipeline in ServiceScopedBehavior. Doing this I can get the bridge instance that contains the events published from domain layer and publish them as integration events using NServiceBus.
I published a Gist with (hopefully) the code pieces needed to grasp what I'm trying to achieve.
The question is: can I instruct NServiceBus to just delegate calls to the application service provider instead of building it and copy all instructions in endpoint.UserContainer<ServiceBuilder>()? Below is an example
internal sealed class Handler : IHandleMessages<Command>
{
public async Task Handle(Command message, IMessageHandlerContext context)
{
// Resolved from ASPNET DI
var useCase = context.GetService<CommandUseCase>();
// _useCase is resolved NSB DI since injected from constructor
Debug.Assert(ReferenceEquals(useCase, _useCase), "");
await useCase.Execute().ConfigureAwait(false);
}
}
This way I could inject to correct scoped application class in the handler constructor instead of resolving it from the scope provided by IServiceProvider that is made available from context.Extensions.Get<IServiceScope>().ServiceProvider.
Thanks for help
Regards
I think ASP.NET Core integration sample could be useful. Starting from version 7.2 sharing of the DI infrastructure between ASP.NET and NServiceBus is much simpler. There is also a specialized NServiceBus.Extensions.Hosting adapter package that adds UseNServiceBus API.

Instantiate DBContext and get its metadata from a Type (another project)

I have Project 1 which is an ASP.NET Core application and Project 2 which is a .NET Standard library.
Is there a way to instantiate a DbContext found in Project 1 from my library using only the Type? I only want to get the metadata about it.
How does Scaffolding do it?
Example
var context = InstatiateFromType(typeof(MyContext));
// So that I get access to the metadata
var x = context.Model.FindEntityType(...);
I'm not sure I understood correctly.
But instead of instantiating it yourself, you could use get an instance via DI in your constructor.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<MyContext>(options => options.UseSqlServer("connectionString"));
}
And then in your Class you just get it via the constructor.

Autofac PerRequest WebApi 2 + Owin

Trying to implement autofac with my WebApi ... but having some issues with lifetime for my objects...
My startup webapi class:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
container.RegisterType<MyConcreteClass>().As<IMyInterface>().InstancePerRequest();
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
But not works
Unable to resolve the type 'IMyInterface' because the lifetime scope it belongs in can't be located. The following services are exposed by this registration:
- IMyInterface
Details ---> No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself. (See inner exception for details.)
Removing this part .InstancePerRequest(); , then works, but the object is not disposing.
What am i doing wrong ?
Thanks!
I strongly suspect the problem lies with the following code:
var csl = new AutofacServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => csl);
The error message indicates you must resolve dependencies using the dependency resolver, but this is bypassing that and using the container itself.
On a side note, using a service locator is anti-pattern. You should be injecting dependencies into your controllers and other MVC extension points rather than using this approach.

Migration from WCF WebApi to MVC4 Api - Registering global error handler

I am migrating a project that was developed using WebApi Preview 5 (when it was part of WCF) to the final release of WebApi (part of MVC4). There is a document describing the process but it is extremely simplistic and doesn't cover most of the issues.
Now one of the issues I am facing is that a GlobalErrorHandler was created by inheriting from HttpErrorHandler and then overriding OnTryProvideResponse and that was used to hook error handling with Elmah. Now that was registered on AppStart with a line like this:
var configuration = new WebApiConfiguration();
//some other configuration for security and CreateInstance
configuration.ErrorHandlers =
(handlers, endpoint, description) => handlers.Add(new GlobalErrorHandler())
};
//then some registration
RouteTable.Routes.MapServiceRoute<SomeObject>("routeName", configuration);
and then mapping different route to this configuration. All this code doesn't work in the new world of MVC4 WebApi, it seems like there is a conflict between HttpErrorHandler and it can't even implement its members properly.
Now I've seen general posts about how to register Elmah with WebApi but I am trying to stick to the original code as much as possible and I am assuming - may be I am wrong - that there is a direct equivalent to what Microsoft had in the Preview version and what they released in the final one. So my questions:
What is the equivalent of this Global Error handling registation in ASP.NET MVC4 WebApi?
Do I need to do the configuration the same way it is done here (default webapi samples project doesn't seem to have similar code)
What is the equivalent of that route registration line of code: RouteTable.Routes.MapServiceRoute("routeName", configuration);
If you create a quick one-off WebApi MVC project in Visual Studio you will see an App_Start folder which contains some classes which have static methods for handling the registration, specifically:
FilterConfig.cs
WebApiConfig.cs
WebApi Config is where you need to register routes etc...
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Filter config is what you need to handle your global errors... Filter config has a default error handler attribute added which you can swap out or out
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
Global.asax calls these static registration scripts like so:
protected void Application_Start()
{
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
}
In regard to Elmah it appears simplying including the Nuget package will register it...
Look for the package Elmah.Mvc
PM> Install-Package Elmah.MVC
it used to be like this How to get ELMAH to work with ASP.NET MVC [HandleError] attribute? but now according to this blog it has changed:
HandleErrorAttribute inside If you tried to use ELMAH in ASP.NET MVC,
you are probably implemented your own HandleErrorAttribute, as it's
shown in this example. You no longer need to apply this custom code
with Elmah.MVC. As soon you installed package, so can safely remove
your HandleError attribute, since it's already included into package.
This now appears to register itself in the Web.Config as a managedHandler so your code does not need to reference Elmah directly.