I have a third-party library that gives me the opportunity to connect NLog via an ILoggerFactory interface.
However, I would like to add a suffix to the logger namespace.
The following ExtensionMethod is currently used to inject NLog.
var factory = LoggerFactory.Create(builder => builder.AddNLog());
Currently, a namespace looks like this:
my.custom.library.class1
my.custom.library.class2
my.custom.library.with.inner.subclass
However, I would like that behind every namespace there is e.g. foo
my.custom.library.class1.foo
my.custom.library.class2.foo
my.custom.library.with.inner.subclass.foo
Here's a quick fix without any overloads.
var factory = LoggerFactory.Create(builder => builder.AddNLogWithSuffix(_IpSuffix));
public static class LoggingBuilderExtensions
{
public static void AddNLogWithSuffix(this ILoggingBuilder builder, string loggerSuffix)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, NLogLoggerProviderWithSuffix>(_ => new NLogLoggerProviderWithSuffix(new NLogProviderOptions(), LogManager.LogFactory, loggerSuffix)));
}
}
[ProviderAlias("NLog")]
public class NLogLoggerProviderWithSuffix : ILoggerProvider
{
private readonly NLogLoggerProvider _NLogLoggerProvider;
private readonly string _LoggerSuffix;
/// <summary>New provider with options and a suffix</summary>
/// <param name="options"></param>
/// <param name="logFactory">Optional isolated NLog LogFactory</param>
/// <param name="loggerSuffix"></param>
public NLogLoggerProviderWithSuffix(NLogProviderOptions options, LogFactory logFactory, string loggerSuffix)
{
_LoggerSuffix = loggerSuffix;
_NLogLoggerProvider = new NLogLoggerProvider(options, logFactory);
}
public void Dispose()
{
_NLogLoggerProvider.Dispose();
}
public ILogger CreateLogger(string categoryName)
{
return _NLogLoggerProvider.CreateLogger($"{categoryName}.{_LoggerSuffix}");
}
}
Related
I am using asp.net core 3.1 project template to develop a web API. There are no compilation errors.
Here goes my code details:
Program.cs
public class Program
{
public static void Main(string[] args)
{
// Use the W3C Trace Context format to propagate distributed trace identifiers.
// See https://devblogs.microsoft.com/aspnet/improvements-in-net-core-3-0-for-troubleshooting-and-monitoring-distributed-apps/
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostEnvironment webHostEnvironment;
/// <summary>
/// Initializes a new instance of the <see cref = "Startup"/> class.
/// </summary>
/// <param name = "configuration">The application configuration, where key value pair settings are stored. See
/// http://docs.asp.net/en/latest/fundamentals/configuration.html</param>
/// <param name = "webHostEnvironment">The environment the application is running under. This can be Development,
/// Staging or Production by default. See http://docs.asp.net/en/latest/fundamentals/environments.html</param>
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
this.configuration = configuration;
this.webHostEnvironment = webHostEnvironment;
}
/// <summary>
/// Configures the services to add to the ASP.NET Core Injection of Control (IoC) container. This method gets
/// called by the ASP.NET runtime. See
/// http://blogs.msdn.com/b/webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx
/// </summary>
public virtual void ConfigureServices(IServiceCollection services) =>
services
.AddCosmosDBConfiguration(configuration)
.AddAutoMapperConfiguration()
.AddCustomResponseCompression(configuration)
.AddCustomCors()
.AddCustomOptions(configuration)
.AddHttpContextAccessor()
.AddCustomRouting()
.AddCustomStrictTransportSecurity()
.AddCustomHealthChecks()
.AddServerTiming()
.AddControllers()
.AddCustomJsonOptions(webHostEnvironment)
.AddCustomMvcOptions(configuration)
.Services
.AddCustomGraphQL(configuration, webHostEnvironment)
.AddGraphQLResolvers()
.AddGraphQLResponse()
.AddProjectRepositories()
.AddProjectSchemas();
/// <summary>
/// Configures the application and HTTP request pipeline. Configure is called after ConfigureServices is
/// called by the ASP.NET runtime.
/// </summary>
public virtual void Configure(IApplicationBuilder application) =>
application
.UseIf(
this.webHostEnvironment.IsDevelopment(),
x => x.UseServerTiming())
.UseForwardedHeaders()
.UseResponseCompression()
.UseFetchLocaleMiddleware()
.UseIf(
!this.webHostEnvironment.IsDevelopment(),
x => x.UseHsts())
.UseIf(
this.webHostEnvironment.IsDevelopment(),
x => x.UseDeveloperExceptionPage())
.UseRouting()
.UseCors(CorsPolicyName.AllowAny)
.UseEndpoints(
builder =>
{
builder
.MapHealthChecks("/status")
.RequireCors(CorsPolicyName.AllowAny);
builder
.MapHealthChecks("/status/self", new HealthCheckOptions() { Predicate = _ => false })
.RequireCors(CorsPolicyName.AllowAny);
})
.UseWebSockets()
// Use the GraphQL subscriptions in the specified schema and make them available at /graphql.
.UseGraphQLWebSockets<MainSchema>()
// Use the specified GraphQL schema and make them available at /graphql.
.UseGraphQL<MainSchema>()
.UseIf(
this.webHostEnvironment.IsDevelopment(),
x => x
// Add the GraphQL Playground UI to try out the GraphQL API at /.
.UseGraphQLPlayground(new GraphQLPlaygroundOptions() { Path = "/" })
// Add the GraphQL Voyager UI to let you navigate your GraphQL API as a spider graph at /voyager.
.UseGraphQLVoyager(new GraphQLVoyagerOptions() { Path = "/voyager" }));
}
Response.cs
public class Response
{
public object Data { get; set; }
public string StatusCode { get; set; }
public string ErrorMessage { get; set; }
public Response(object data)
{
StatusCode = "Success";
Data = data;
}
public Response(string statusCode, string errorMessage)
{
StatusCode = statusCode;
ErrorMessage = errorMessage;
}
}
All the dependencies mentioned in the ConfigureServices of the Startup.cs are available. While validating the APIs I am getting a run time error as mentioned below:
No constructor for type 'MyProject.Response' can be instantiated using services from the service container and default values.
Here goes the dependency setup required for the Response class as mentioned below:
ProjectServiceCollectionExtensions.cs
namespace MyProject
{
public static class ProjectServiceCollectionExtensions
{
public static IServiceCollection AddGraphQLResponse(this IServiceCollection services) => services.AddScoped<Response>();
}
}
Resolver.cs
public class Resolver
{
public Response Response(object data)
{
return new Response(data);
}
public Response Error(GraphQLError error)
{
return new Response(error.StatusCode, error.ErrorMessage);
}
public Response AccessDeniedError()
{
var error = new AccessDeniedError();
return new Response(error.StatusCode, error.ErrorMessage);
}
public Response NotFoundError(string id)
{
var error = new NotFoundError(id);
return new Response(error.StatusCode, error.ErrorMessage);
}
}
CountriesResolver.cs
using Author.Core.Framework.Utilities;
using Author.Query.New.API.GraphQL.Types;
using Author.Query.Persistence.DTO;
using Author.Query.Persistence.Interfaces;
using GraphQL.DataLoader;
using GraphQL.Types;
using Microsoft.AspNetCore.Http;
using System;
namespace MyProject.GraphQL.Resolvers
{
public class CountriesResolver : Resolver, ICountriesResolver
{
private readonly ICountryService _countryService;
private readonly IHttpContextAccessor _accessor;
private readonly IUtilityService _utilityService;
private readonly IDataLoaderContextAccessor _dataLoaderContextAccessor;
public CountriesResolver(ICountryService countryService, IHttpContextAccessor accessor, IUtilityService utilityService, IDataLoaderContextAccessor dataLoaderContextAccessor)
{
_countryService = countryService ?? throw new ArgumentNullException(nameof(countryService));
_accessor = accessor;
_utilityService = utilityService ?? throw new ArgumentNullException(nameof(utilityService));
_dataLoaderContextAccessor = dataLoaderContextAccessor;
}
public void Resolve(GraphQLQuery graphQLQuery)
{
var language = _accessor.HttpContext.Items["language"] as LanguageDTO;
graphQLQuery.FieldAsync<ResponseGraphType<CountryResultType>>("countriesresponse", resolve: async context =>
{
if (language != null)
{
var loader = _dataLoaderContextAccessor.Context.GetOrAddLoader("GetAllCountries", () => _countryService.GetAllCountriesAsync(language));
var list = await context.TryAsyncResolve(async c => await loader.LoadAsync());
return Response(list);
}
return null;
}
, description: "All Countries data");
graphQLQuery.FieldAsync<ResponseGraphType<CountryType>>("country", arguments: new QueryArguments(new QueryArgument<NonNullGraphType<IntGraphType>>{Name = "countryId", Description = "id of the country"}), resolve: async context =>
{
var countryId = context.GetArgument<int>("countryId");
if (language != null && countryId > 0)
{
var loader = _dataLoaderContextAccessor.Context.GetOrAddLoader("GetCountry", () => _countryService.GetCountryAsync(language, countryId));
var countryDetails = await context.TryAsyncResolve(async c => await loader.LoadAsync());
return Response(countryDetails);
}
return null;
}
);
}
}
}
Can anyone help me to fix this issue by providing their guidance
Based on how Response is used by Resolver I would say Response does not need to be added to the DI/IoC container, since Resolver is essentially a Response factory
If Response is not explicitly injected anywhere then the container does not need to be aware of it. No need to add it to the container begin with.
Remove
//...
.AddGraphQLResponse() //<--SHOULD BE REMOVED
//...
extension from Startup.ConfigureServices
I have a Web API controller, say EmployeeController, which we register using Autofac. Now we create another controller with the same name and route, but with different functionality. When we try to register this new EmployeeController (i.e., Plugin) using Autofac, we would get an exception like
multiple types were found that match the controller named EmployeeController.
My objective is to successfully inject the second controller and override the functionality of the first controller with it.
Project A - > Core Project
namespace Main.API
{
public class EmployeeController : ApiController
{
// Some Logic
}
}
Project B - > Plug-in Project
Later consumer want to override employee controller with same controller name
namespace Plugin.API
{
public class EmployeeController : ApiController
{
// Some Logic
}
}
Autofac
// assemblies contains Main.API.dll & Plugin.API.dll
builder.RegisterApiControllers(assemblies.ToArray()).InstancePerRequest();
In order to implement what you want I would use AOP concept which will make it easier to implement and more powerful.
Castle DynamicProxy project provides AOP concept for .net and can be used by Autofac with the Autofac.Extras.DynamicProxy2 nuget package.
You will have only 1 EmployeeController in your main project
namespace Main.API
{
public class EmployeeController : ApiController
{
public virtual String Get(Int32 id)
{
// Some Logic
}
}
}
and various IInterceptor in your plugin projects :
namespace Plugin
{
public class XEmployeeeControllerInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
if(!invocation.Method.Name == nameof(Core.APi.EmployeeController.Get))
{
return;
}
invocation.Proceed();
// alter return value
invocation.ReturnValue = invocation.ReturnValue + "-intercepted";
}
}
}
Then register things like this:
builder.RegisterApiControllers(assemblies.ToArray())
.InstancePerRequest()
.EnableClassInterceptors();
builder.RegisterAssemblyTypes(assemblies.ToArray())
.As<IInterceptor>();
See Type Interceptors for more information
Using this following code snippet you can override the same name of plugin controller.
public class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
private readonly HttpConfiguration _configuration;
private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;
/// <summary>
/// custom http controllerselector
/// </summary>
/// <param name="config"></param>
public CustomHttpControllerSelector(HttpConfiguration config) : base(config)
{
_configuration = config;
_controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
}
/// <summary>
/// GetControllerMapping
/// </summary>
/// <returns></returns>
public override IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
return _controllers.Value;
}
private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
{
var controllers = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
foreach (Type t in controllerTypes)
{
var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
//Remove Core API Controller and add the Plugin API controller.
if (controllers.Keys.Contains(controllerName) && t.Namespace.Contains("Plugin"))
{
controllers.Remove(controllerName);
}
if (!controllers.Keys.Contains(controllerName))
{
controllers[controllerName] = new HttpControllerDescriptor(_configuration, t.Nam`enter code here`e, t);
}
}
return controllers;
}
}
Problem
I am trying to test my service. when I run test it show this error and test failed. my unity config class is in my asp.net mvc project and my test is in different project I don't know where I am doing wrong.
unable to get default constructor for Class servicetest
ServiceTest
[TestClass]
public class ImportServiceTests
{
private readonly IImportService _importService;
private readonly IUnitOfWork _unitOfWork;
public ImportServiceTests(IImportService importService, IUnitOfWork unitOfWork)
{
_importService = importService;
_unitOfWork = unitOfWork;
}
[TestMethod]
public void ImportCategories()
{
string filePath = Path.GetFullPath(#"E:\categories.xlsx");
if (File.Exists(filePath))
{
Stream data = File.OpenRead(filePath);
string fileName = Path.GetFileName(filePath);
_importService.ImportCategoriesFromXlsx(data);
}
}
}
UnityConfig
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
/// <summary>Registers the type mappings with the Unity container.</summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
container.RegisterType<IImportService, ImportService>(new PerRequestLifetimeManager());
container.RegisterType<IDataContext, PriceHunterDataContext>(new PerRequestLifetimeManager());
}
}
Here is the issue at hand:
While calling my CustomerController through the URL, I get the following exception:
ExceptionMessage:
An error occurred when trying to create a controller of type
'CustomerController'. Make sure that the controller has a
parameterless public constructor.
I am using the following url's:
http://localhost:55555/api/Customer/
http://localhost:55555/api/Customer/8
Please note: The /api/Customer/ call were working before I refactored the logic into a business class and implemented dependency injection.
My research suggests that I am not registering my interface and class correctly with Ninject, but not sure what step I am missing.
Researched Links:
Make sure that the controller has a parameterless public constructor error
Make sure that the controller has a parameterless public constructor in Unity
Here is my question What is causing this exception? I am registering my interface/class within Ninject, but it doesn't seem to recognize the mapping correctly. Any thoughts?
Customer Controller
public class CustomerController : ApiController
{
private readonly ICustomerBusiness _customerBusiness;
public CustomerController(ICustomerBusiness customerBusiness)
{
_customerBusiness = customerBusiness;
}
// GET api/Customer
[HttpGet]
public IEnumerable<Customer> GetCustomers()
{
return _customerBusiness.GetCustomers();
}
// GET api/Customer/Id
[HttpGet]
public IEnumerable<Customer> GetCustomersById(int customerId)
{
return _customerBusiness.GetCustomerById(customerId);
}
}
Customer Business
public class CustomerBusiness : ICustomerBusiness
{
private readonly DatabaseContext _databaseContext = new DatabaseContext();
public IEnumerable<Customer> GetCustomers()
{
return _databaseContext.Customers;
}
public IQueryable<Customer> GetCustomerById(int customerId)
{
return _databaseContext.Customers.Where(c => c.CustomerId == customerId);
}
}
Customer Business Interface
public interface ICustomerBusiness
{
IQueryable<Customer> GetCustomerById(int customerId);
IEnumerable<Customer> GetCustomers();
}
NinjectWebCommon
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using MyReservation.API;
using MyReservation.API.Business;
using Ninject;
using Ninject.Web.Common;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(NinjectWebCommon), "Stop")]
namespace MyReservation.API
{
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICustomerBusiness>().To<CustomerBusiness>();
}
}
}
For the same problem I installed the nuget packages
ninject
ninject.web.common
ninject.web.common.webhost
ninject.web.webapi
ninject.web.webapi.webhost
and worked
I've been reading about the these two approaches to resolving dependencies and found some sample code for ninject implementations.
For service locator followed something like
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
And
public class NinjectDependencyScope : IDependencyScope
{
IResolutionRoot resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.GetAll(serviceType);
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
}
And the out of the box class
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<MembersService>().To<MembersService>();
kernel.Bind<MemberContext>().To<MemberContext>();
}
For composition root I followed - https://gist.github.com/paigecook/3860942
public class NinjectKernelActivator: IHttpControllerActivator
{
private readonly IKernel _kernel;
public NinjectKernelActivator(IKernel kernel)
{
_kernel = kernel;
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = (IHttpController) _kernel.Get(controllerType);
request.RegisterForDispose( new Release(()=> _kernel.Release(controller)));
return controller;
}
}
internal class Release : IDisposable
{
private readonly Action _release;
public Release(Action release)
{
_release = release;
}
public void Dispose()
{
_release();
}
}
And made a single change to Create(..) in NinjectWebCommon.
//GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator),
new NinjectCompositionRoot(kernel));
EDIT
Controller and service creation
public class MembersController : ApiController
{
private readonly IMembersService _membersService;
public MembersController(IMembersService membersService)
{
_membersService = embersService;
}
...
}
public class MembersService : IMembersService
{
private readonly MembersContext _context;
public MembersService(MemberContext context)
{
_context = context;
}
...
}
Have I correctly implemented the Composition Root?
I don't really see what the difference is between the two approaches?
The difference between Composition root (which is how you should do dependency injection) and Service Locator is that composition root should be in one place of the application (as close as possible to the application's entry point). This doesn't mean that it will be invoked only once. For example in case of MVC/WebAPI good place for composition root is the controllers factory which creates controllers for every HTTP request application receives. The point is that composition root implemented in controller factory should create entire object graph (controller with all of his dependencies) that is required do handle the request such that no other dependencies need to be resolved from container separately during this request.
Service Locator on the other hand is approach where you retrieve your dependencies from service locator whenever you need them. Service locator becomes ambient context in your application (usually provides static ServiceLocator.Get<T>() method). Service Locator is the opposite of Dependency Injection because instead of injecting your dependencies you are retrieving them when you need them. This means having ServiceLocator.Get<T>() method calls all over the application code and all layers of your application have dependency on service locator. This approach has several downfalls one of them is the fact that it makes code harder to unit test, since all tests need to interact with the same global service locator class to set the fake dependencies of a class under test.
Your NinjectKernelActivator implementation of composition root is correct assuming you are not exposing IKernel elsewhere in some public static property for using it later to get dependencies that you do not inject.