Defining Ninject's binding without scope to keep the code DRY - c#

I have a Web project and a Windows Service project in my solution. I have created 2 different binding modules for these 2 projects, but as you can see it has a lot of duplicate code... the only difference is that I am using InRequestScope() for the Web project and InTransientScope() for Windows Service project.
Is it possible combine the bindings, and add the scope depending on the project/entry point?
public class WebModule : NinjectModule
{
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().InRequestScope();
Bind<IMyRepository>().To<MyRepository>().InRequestScope();
// more types ... InRequetScope();
}
}
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().InTransientScope();
Bind<IMyRepository>().To<MyRepository>().InTransientScope();
// more types ... InTransientScope();
}
}

Update:
As explained by ninject team, we can use InRequestScope() in both scenarios... since there is no concept of Request in a Windows Service project, ninject would use the default scope, which is InTransientScope() in the service project.
Original Answer
The best solution that I have come up with is to create an extension method:
public static class NinjectExtensions
{
public static IBindingNamedWithOrOnSyntax<T> GetScopeByName<T>(this IBindingInSyntax<T> syntax, string scope)
{
if (scope.Equals("request", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InRequestScope();
}
else if (scope.Equals("thread", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InThreadScope();
}
else if (scope.Equals("singleton", StringComparison.InvariantCultureIgnoreCase))
{
return syntax.InSingletonScope();
}
return syntax.InTransientScope();
}
}
And set the scope dynamically.
public class MyModule : NinjectModule
{
private string _scope = "transient";
public MyModule()
{
if (Convert.ToBoolean(ConfigurationManager.AppSettings["IsWebProject"]))
{
_scope = "request";
}
}
public override void Load()
{
Bind<ApplicationDbContext>().ToSelf().GetScopeByName(_scope);
Bind<IMyRepository>().To<MyRepository>().GetScopeByName(_scope);
// more types ... InRequetScope();
}
}
Note: I am not sure if there is a better solution... this is just the cleanest approach that has come to my mind.

Related

Object Reference not set to an instance in ASP.Net Core 3.0

In Manage.Data project I've Admin repository and I've implemented service calls in admin repository. I've a another project Manage.API and I've a class called Authenticate.cs. I want to call GetLoginInfo() method from Authenticate.cs class file.
private IManageRepository _memRepository;
_memRepository.GetLoginInfoAsync("","",Guid.Empty);
Its not working. Could anyone please guide me.
CustomAuthenticate.cs
namespace CamManager_API
{
[AttributeUsage(AttributeTargets.Class)]
public class CustomAuthorization : Attribute, IAuthorizationFilter
{
private IManageRepository IManageRepository;
public CustomAuthorization(IManageRepository manageRepository)
{
this._manageRepository = manageRepository;
}
/// <summary>
/// This will Authorize User
/// </summary>
/// <returns></returns>
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (filterContext != null)
{
Microsoft.Extensions.Primitives.StringValues authTokens;
filterContext.HttpContext.Request.Headers.TryGetValue("Authorization", out authTokens);
var _token = authTokens.FirstOrDefault();
if (_token != null)
{
string authToken = _token;
if (authToken != null)
{
var responseModel = _manageRepository.GetLoginInfoAsync("","",Guid.Empty);
}
}
else
{
}
}
}
public bool IsValidToken(string authToken)
{
//validate Token here
return true;
}
}
}
ManageRepository.cs
using Dapper;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading.Tasks;
namespace CamManager.DataAccess
{
public class ManageRepository : SqlRepository<Member>, IManageRepository
{
public ManageRepository(string connectionString) : base(connectionString) { }
public override async Task<Login> GetLoginInfoAsync(string firstName, string password, Guid accessKey)
{
using (var conn = GetOpenConnection())
{
var parameters = new DynamicParameters();
parameters.Add("#Firstname", firstName, System.Data.DbType.String);
parameters.Add("#Password", password, System.Data.DbType.String);
parameters.Add("#AccessKey", accessKey, System.Data.DbType.Guid);
return await conn.QueryFirstOrDefaultAsync<Login>("Login", parameters, commandType: CommandType.StoredProcedure);
}
}
public override async Task<IEnumerable<Member>> GetAllAsync()
{
}
public override async Task<Member> FindAsync(long id)
{
}
public override async Task<Result> InsertAsync(Member entity)
{
}
public override async Task<Result> UpdateAsync(Member entity)
{
}
public override async Task<Result> DeleteAsync(long id)
{
}
}
}
maybe you need to init your repository in the constructor... otherwise show us all file content to detect the exact problem
public class MainClass
{
private IManageRepository _memRepository;
public MainClass(IManageRepository _memRepository)
{
this._memRepository = _memRepository;
}
}
Add this code to your startup
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IManageRepository , ManageRepository >();
}
I think you forgot to register your Interface for the ASP dependency injection. Otherwise the DI cannot do anything. More information here
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IManageRepository, ManageRepositoryImplementation>();
}
Whereby ManageRepositoryImplementation is just a placeholder for
the class which implements the interface IManageRepository and you want to create an instance of. This
information only you can know.
Then do it like Lajil Adel said:
public class MainClass
{
private IManageRepository _memRepository;
public MainClass(IManageRepository _memRepository)
{
this._memRepository = _memRepository;
}
}
I'll separate your question in half, one is base your current approach and better suitable one.
I realize that the goal you trying to achieve is to build a custom authentication challenge, and working alone or combine with the default one.
With your current approach:
The attribute wont work like that (you should give it parameter less constructor or just some constant flag to mark something special), otherwise, you won't get native DI. The compiler will yelling at you like this
Require constructor param right the moment you use it
If you want to keep your approach for the sake of simplicity, you will have to register your service first.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IManageRepository, ManageRepository>();
}
You can register the service as singleton or scope depending on how you manually create your SqlConnection.
Then take the service out of the HttpContext like this:
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
var manageRepository = filterContext.HttpContext.RequestServices
.GetRequiredService<IManageRepository>();
}
There you go... implement the rest of your logic.
A better approach:
Give authentication scheme a shot, then build a policy scheme to apply corresponding scheme for each Action or may be the whole controller.
This give you a central place to describe your authentication/authorization process in the app it's much clearer and highly adoptable with complex authentication requirement.
Combine this with redis rather than sqlConnection would be much better and scalable for production environment.

Provide user information from signalr request in business logic layer using autofac

I have an ASP.NET MVC 5 Application with a SignalR 2 hub and using autofac for the DI.
The entire business logic is encapsulated in manager classes in their own layer. Some manager methods need informations about the current logged in user (UserId, TenantId, ..).
I solved this problem by injecting an AuthorizationProvider into each manager class that needs the user information.
public interface IAuthorizationProvider
{
long? GetUserId();
long? GteTenantId();
}
public class MyManager : IMyManager
{
private IAuthorizationProvider _authorizationProvider;
public MyManager(IAuthorizationProvider authorizationProvider)
{
_authorizationProvider = authorizationProvider;
}
public void MyMethod()
{
// Getting the User information here is pretty simple
long userId = _authorizationProvider.GetUserId();
}
}
Normally I can get the user information from the HttpContext and from the session. So I wrote a SessionAuthorizationProvider:
public class SessionAuthorizationProvider{
public long? GetUserId()
{
HttpContext.Current?.Session?[SessionKeys.User]?.Id;
}
public long? GteTenantId() { ... }
}
But now I have a new method in the SignalR hub that use the same mechanism.
[HubName("myHub")]
public class MyHub : Hub
{
private IMyManager _myManager;
public MyHub(IMyManager myManager)
{
_myManager = myManager;
}
[HubMethodName("myHubMethod")]
public void MyHubMethod(long userId, long tenantId)
{
_myManager.MyMethod();
}
}
The problem is that a SignalR request doesn't have a session. Therefore I have also set the required user information in the hub method as parameters postet from the client.
So I thought it is the best solution for this problem to write a new AuthorizationProvider for SignalR and adapt the depdendency resolver. But I can't get the current user in the new SignalrAuthorizationProvider.
public class SignalrAuthorizationProvider{
public long? GetUserId()
{
// How to get the user information here???
}
public long? GteTenantId() { /* and here??? */ }
}
Is there a recommended solution to this problem?
Of course, I can extend MyMethod to accept the user information as a parameter. But MyMethod calls another method from another manager and that manager also calls another method. The user information is only needed for the last method call. So I had to change at least 3 methods and many more in the future.
Here is a sketch of the problem
This is a potential solution. But it's very bad
Session is not supported by SignalR by default and you should avoid using it. See No access to the Session information through SignalR Hub. Is my design is wrong?. But you still can use cookie or querystring to get the desired value.
In both case you need to have access to the HubCallerContext of the underlying hub, the one that is accessible through the Context property of the Hub.
In a ideal word you should just have to had the dependency to the SignalAuthorizationProvider
ie :
public class SignalrAuthorizationProvider {
public SignalrAuthorizationProvider(HubCallerContext context){
this._context = context;
}
private readonly HubCallerContext _context;
public long? GetUserId() {
return this._context.Request.QueryString["UserId"]
}
}
But due to SignalR design it is not possible. Context property is assigned after construction of the Hub and AFAIK there is no way to change it.
Source code here : HubDispatcher.cs
One possible solution would be to inject a mutable dependency inside the Hub and alter the object in the OnConnected, OnReconnected methods.
public class SignalrAuthorizationProvider : IAuthorizationProvider
{
private Boolean _isInitialized;
private String _userId;
public String UserId
{
get
{
if (!_isInitialized)
{
throw new Exception("SignalR hack not initialized");
}
return this._userId;
}
}
public void OnConnected(HubCallerContext context)
{
this.Initialize(context);
}
public void OnReconnected(HubCallerContext context)
{
this.Initialize(context);
}
private void Initialize(HubCallerContext context) {
this._userId = context.QueryString["UserId"];
this._isInitialized = true;
}
}
and the Hub
public abstract class CustomHub : Hub
{
public CustomHub(IAuthorizationProvider authorizationProvider)
{
this._authorizationProvider = authorizationProvider;
}
private readonly IAuthorizationProvider _authorizationProvider;
public override Task OnConnected()
{
this._authorizationProvider.OnConnected(this.Context);
return base.OnConnected();
}
public override Task OnReconnected()
{
this._authorizationProvider.OnReconnected(this.Context);
return base.OnReconnected();
}
}
Having a mutable dependency is not the best design but I can't see any other way to have access to IRequest or HubCallerContext.
Instead of having an abstract Hub class which is not a perfect solution. You can change the RegisterHubs autofac method to use AOP with Castle.Core and let the interceptor calls the methods for you.

DbContext is Disposed When Using Autofac Dependency Injection on WebApi project

I have a WebApi project using Entity Framework 6.0, Autfac for DI and CQRS architecture. The problem I have that DbContext isn't disposing how it supposed to. The action I take:
I run two quick requests, e.g. send request from Postman to one endpoint, runtime stops on breakpoint in controller method, I send second request to another endpoint in different controller.
Resume Runtime
if the second request finished before the first one is done, the first one throws and error that dbcontext was disposed and it cannot run whatever it was supposed to do
Originally problem appeared when I posted and patched from frontend one after another.
It seems like lifetime scope is not really per-request. It seems like all dbcontexts are disposed on one of the request's end. The other one does not have anything to work with.
How is it configured?
Starting from the highest layer - controller:
public class UsersController : BaseController, IUsersApi
{
private readonly IUserService _userService;
public UsersController(IUserService userService, ILogging logging) : base(logging)
{
_userService = userService;
}
[HttpGet]
[Route("api/users")]
public IList<UserDto> GetUsers()
{
try
{
return _userService.GetAllUsers();
}
catch (Exception e)
{
_logger.Error(e);
_logger.Trace(e);
throw;
}
}
[HttpPatch]
[Route("api/users/")]
public IHttpActionResult EditUsers(ICollection<UserEditDto> model)
{
try
{
_userService.EditUsers(model);
return Ok();
}
catch (Exception e)
{
_logger.Error(e);
_logger.Trace(e);
return BadRequest("Error");
}
}
}
Service layer:
public class UserService : IUserService
{
private readonly IServiceTools _serviceTools;
private readonly IUserQuerier _userQuerier;
public UserService(IServiceTools serviceTools, IUserQuerier userQuerier)
{
_serviceTools = serviceTools;
_userQuerier = userQuerier;
}
public void EditUsers(ICollection<UserEditDto> model)
{
var mapper = _serviceTools.AutoMapperConfiguration.Configure().CreateMapper();
var userEditCommands = mapper.Map<ICollection<UserEditDto>, ICollection<EditUserCommand>>(model);
foreach (var command in userSaveCommands)
{
_serviceTools.CommandBus.SendCommand(command);
CacheHelper.Clear(command.Id.ToString());
}
}
public IList<UserDto> GetAllUsers()
{
var allUsers = _userQuerier.GetAllUsers();
var result = allUsers.Select(x => new UserDto()
{
...
}).ToList();
return result;
}
}
Service Tools interface where command bus sits:
public interface IServiceTools
{
ICommandBus CommandBus { get; }
IAutoMapperConfiguration AutoMapperConfiguration { get; }
IIdentityProvider IdentityProvider { get; }
}
public class ServiceTools : IServiceTools
{
public ServiceTools(ICommandBus commandBus, IAutoMapperConfiguration autoMapperConfiguration, IIdentityProvider identityProvider)
{
CommandBus = commandBus;
AutoMapperConfiguration = autoMapperConfiguration;
IdentityProvider = identityProvider;
}
public ICommandBus CommandBus { get; }
public IAutoMapperConfiguration AutoMapperConfiguration { get; }
public IIdentityProvider IdentityProvider { get; }
}
And whatever handler for command:
public class EditUserHandler : IHandleCommand<EditUserCommand>
{
private readonly ICommandsContext _commandsContext;
public SaveUserHandler(ICommandsContext commandsContext)
{
_commandsContext = commandsContext;
}
public void Handle(EditUserCommand command)
{
... using dbcontext here...
}
}
}
For DI I use Autofac, all resources are set to per-request lifetime, split into modules, e.g. module for data access
public class DataModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AppNameDbContext>().As<ICommandsContext>().InstancePerRequest();
builder.RegisterType<AppNameDbContext>().As<IQueryContext>().InstancePerRequest();
base.Load(builder);
}
}
The difference between both interfaces is that IQueryContext cannot change entity states and use SaveChagnes() method. IQueryContext have all DbSets in it, while ICommandsContext inherits from it and adds SettingState methods (added, modified, deleted) and SaveChanges() method.
IQueryContext is injected into queries and ICommandsContext into commands as seend in example aboove.
Now the Autofac config for command bus looks like that:
public class InfrastractureModule : Module
{
private ICommandsContext _commandsContext;
private ITranslationsCommandsContext _translationsCommandsContext;
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AutoMapperConfiguration>().
As<IAutoMapperConfiguration>().InstancePerRequest();
builder.RegisterType<ServiceTools>().As<IServiceTools>().InstancePerRequest();
builder.Register(c =>
{
_commandsContext = c.Resolve<ICommandsContext>();
_translationsCommandsContext = c.Resolve<ITranslationsCommandsContext>();
return new CommandBus(CreateHandlersFactory);
})
.As<ICommandBus>().InstancePerRequest();
base.Load(builder);
}
private IHandleCommand CreateHandlersFactory(Type type)
{
if (type == typeof(XXXCommand))
{
return new XXXHandler(_commandsContext);
}
}
While the command bus looks like that
public class CommandBus : ICommandBus
{
private readonly Func<Type, IHandleCommand> _handlersFactory;
public CommandBus(Func<Type, IHandleCommand> handlersFactory)
{
_handlersFactory = handlersFactory;
}
public void SendCommand<T>(T command) where T : ICommand
{
var handler = (IHandleCommand<T>) _handlersFactory(typeof(T));
handler.Handle(command);
}
}
There is completely separate context used for translations for the app, but I do not thing that is important here.
I did not find any posts with similar problem. It only occurs when where two requests processed at the same time. I do not know if the configuration is wrong or Autofac messes things up, because it should not technically dispose dbcontext which was allocated for another request.
Sorry for the wall of text ;) I hope someone can help with that.
Obiously changing dbcontext's lifetime to SingleInstance fixed the problem, but we do not want that :)
SOLUTION EDIT:
As #ZeljkoVujaklija noticed CommandsDbContext declarations in InfrastractureModule seemed strange. I removed whole CommandBus registration from InfrastractureModule. Instead I created CommandsModule in the assembly where all the commands sit. It looks like that:
public class CommandsModule : Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(x => x.IsAssignableTo<IHandleCommand>())
.AsImplementedInterfaces();
builder.Register<Func<Type, IHandleCommand>>(c =>
{
var ctx = c.Resolve<IComponentContext>();
return t =>
{
var handlerType = typeof(IHandleCommand<>).MakeGenericType(t);
return (IHandleCommand)ctx.Resolve(handlerType);
};
});
builder.RegisterType<CommandBus>()
.AsImplementedInterfaces();
}
}
Not only it fixes the problem but also gets rid of huge factory.
If you are running within ASP.NET Core you should run InstancePerLifetimeScope instead of InstancePerRequest
Use InstancePerLifetimeScope instead of InstancePerRequest. In previous ASP.NET integration you could register a dependency as InstancePerRequest which would ensure only one instance of the dependency would be created per HTTP request. This worked because Autofac was in charge of setting up the per-request lifetime scope. With the introduction of Microsoft.Extensions.DependencyInjection, the creation of per-request and other child lifetime scopes is now part of the conforming container provided by the framework, so all child lifetime scopes are treated equally - there’s no special “request level scope” anymore. Instead of registering your dependencies InstancePerRequest, use InstancePerLifetimeScope and you should get the same behavior. Note if you are creating your own lifetime scopes during web requests, you will get a new instance in these child scopes.
http://autofaccn.readthedocs.io/en/latest/integration/aspnetcore.html#differences-from-asp-net-classic

Getting Attribute Value on Member Interception

I have this CacheAttribute that accepts Duration Value like such
public class MyTestQuery : IMyTestQuery
{
private readonly ISomeRepository _someRepository;
public TestQuery(ISomeRepository someRepository)
{
_someRepository = someRepository;
}
[Cache(Duration = 10)]
public MyViewModel GetForeignKeysViewModelCache()
{
...code here...
return viewModel;
}
}
The Attribute looks like this
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
public int Duration { get; set; }
}
When Intercepted using Castle.Proxy.IInterceptor it works but when I perform an Attribute.GetCustomAttribute either by IInvocation.MethodInvocationTarget or IInvocation.Method both returns a null value
Here it is in code
public class CacheResultInterceptor : IInterceptor
{
public CacheAttribute GetCacheResultAttribute(IInvocation invocation)
{
var methodInfo = invocation.MethodInvocationTarget;
if (methodInfo == null)
{
methodInfo = invocation.Method;
}
return Attribute.GetCustomAttribute(
methodInfo,
typeof(CacheAttribute),
true
)
as CacheAttribute;
}
public void Intercept(IInvocation invocation)
{
var cacheAttribute = GetCacheResultAttribute(invocation);
//cacheAttribute is null always
...more code here...
}
}
And this is how I register them
public class Bootstrapper
{
public static ContainerBuilder Builder;
public static void Initialise()
{
Builder = new ContainerBuilder();
...other codes in here...
CacheInstaller.Install();
var container = Builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class CacheInstaller
{
public static void Install()
{
Bootstrapper.Builder.RegisterType<CacheResultInterceptor>()
.SingleInstance();
Bootstrapper.Builder.RegisterAssemblyTypes(Assembly.Load("MyApplication.Web"))
.Where(t => t.Name.EndsWith("Query"))
.AsImplementedInterfaces()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(CacheResultInterceptor))
.SingleInstance();
}
}
My Expensive Method Class Ends with Query
Now the question is why invocation.MethodInvocationTarget and/or invocation.Method returns null?
What am I doing wrong?
Any other strategies so I can pass a parameter value without creating a Method for each value I can think of?
BTW I am using
Autofac 4.3.0.0
Autofac.Extras.DynamicProxy 4.2.1.0
Autofac.Integration.Mvc 4.0.0.0
Castle.Core 4.0.0.0
UPDATE 1
Here is what it returns when it runs for clarity
Here's what I found.
invocation.Method returns the method declaration on the interface, in your case IMyTestQuery.
On the other hand, invocation.MethodInvocationProxy returns the method that is going to be called when invoking invocation.Proceed(). This means it can be:
the next interceptor if you have several
a decorator if you have decorators over your interface
the final implementation of your interface
As you can see, MethodInvocationProxy is less deterministic than Method, which is why I would recommend you avoid using it, at least for what you're trying to achieve.
When you think about it, an interceptor should not be tied to an implementation as it proxies an interface, so why don't you put the [Cache] attribute at the interface level?
Using your code, I could successfully retrieve it when put on the interface.
Edit:
OK, I've put together a repository on GitHub that uses the specific versions of the NuGet packages you mentioned and shows how to retrieve an attribute on intercepted methods.
As a reminder, here are the used NuGet packages:
Microsoft.AspNet.Mvc v5.2.3
Autofac v4.3.0
Autofac.Mvc5 4.0.0
Autofac.Extras.DynamicProxy v4.2.1
Castle.Core v4.0.0
I created 2 query interfaces, IMyQuery and IMySecondQuery. Please note that as mentioned in my original answer, the [Cache] attributes are placed on the interfaces methods, not on the implementing classes.
public interface IMyQuery
{
[Cache(60000)]
string GetName();
}
public interface IMySecondQuery
{
[Cache(1000)]
string GetSecondName();
}
Then we have 2 very basic implementations of these classes. Not relevant at all, but for the sake of completeness:
public class DefaultMyQuery : IMyQuery
{
public string GetName()
{
return "Raymund";
}
}
public class DefaultMySecondQuery : IMySecondQuery
{
public string GetSecondName()
{
return "Mickaël Derriey";
}
}
And then the interceptor:
public class CacheResultInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var cacheAttribute = invocation.Method.GetCustomAttribute<CacheAttribute>();
if (cacheAttribute != null)
{
Trace.WriteLine($"Found a [Cache] attribute on the {invocation.Method.Name} method with a duration of {cacheAttribute.Duration}.");
}
invocation.Proceed();
}
}
Note that the GetCustomAttribute<T> method is an extension method over MemberInfo present in the System.Reflection namespace.
Let's move on to the registration in the Autofac container. I tried to follow you registration style as much as I could:
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder
.RegisterType<CacheResultInterceptor>()
.SingleInstance();
builder
.RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
.Where(x => x.Name.EndsWith("Query"))
.AsImplementedInterfaces()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(CacheResultInterceptor));
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
The queries are then used in the HomeController:
public class HomeController : Controller
{
private readonly IMyQuery _myQuery;
private readonly IMySecondQuery _mySecondQuery;
public HomeController(IMyQuery myQuery, IMySecondQuery mySecondQuery)
{
_myQuery = myQuery;
_mySecondQuery = mySecondQuery;
}
public ActionResult MyQuery()
{
return Json(_myQuery.GetName(), JsonRequestBehavior.AllowGet);
}
public ActionResult MySecondQuery()
{
return Json(_mySecondQuery.GetSecondName(), JsonRequestBehavior.AllowGet);
}
}
What I did to test this is just put a breakpoint in the interceptor, F5 the application, open a browser and navigate to both http://localhost:62440/home/myquery and http://localhost:62440/home/myquery.
It did hit the interceptor and find the [Cache] attribute. In the Visual Studio Output window, it did show:
Found a [Cache] attribute on the GetName method with a duration of 60000.
Found a [Cache] attribute on the GetSecondName method with a duration of 1000.
Hopefully that helps you pinpoint what's going on in your project.
I pushed changes to the repository so that the first query calls the second one.
It still works. You should really make an effort and put some code on the question.

Automapper Custom Resolver - Inject Repository into constructor

I am trying to create a custom resolver for automapper which needs to access one of my data repositories to retreive the logged in users account.
Here is my code so far...
public class FollowingResolver : ValueResolver<Audio, bool>
{
readonly IIdentityTasks identityTasks;
public FollowingResolver(IIdentityTasks identitTasks)
{
this.identityTasks = identitTasks;
}
protected override bool ResolveCore(Audio source)
{
var user = identityTasks.GetCurrentIdentity();
if (user != null)
return user.IsFollowingUser(source.DJAccount);
return false;
}
}
However I am getting this error:
FollowingResolver' does not have a default constructor
I have tried adding a default contrstructor but my repository never gets initialised then.
This is my autoampper initialisation code:
public static void Configure(IWindsorContainer container)
{
Mapper.Reset();
Mapper.Initialize(x =>
{
x.AddProfile<AccountProfile>();
x.AddProfile<AudioProfile>();
x.ConstructServicesUsing(container.Resolve);
});
Mapper.AssertConfigurationIsValid();
}
Am I missing something, is it even possible to do it like this or am I missing the boat here?
Found the solution shorlty after...i was forgetting to add my resolvers as an IoC container.
Works great now!
I was getting the same error using Castle Windsor while trying to inject a service.
I had to add:
Mapper.Initialize(map =>
{
map.ConstructServicesUsing(_container.Resolve);
});
before Mapper.CreateMap calls.
Created a ValueResolverInstaller like this:
public class ValueResolverInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IValueResolver>()
.LifestyleTransient());
}
}
and the ValueResolver itself:
public class DivergencesResolver : ValueResolver<MyClass, int>
{
private AssessmentService assessmentService;
public DivergencesResolver(AssessmentService assessmentService)
{
this.assessmentService = assessmentService;
}
protected override int ResolveCore(MyClass c)
{
return assessmentService.GetAssessmentDivergences(c.AssessmentId).Count();
}
}

Categories