xUnit - mocked method for the async List returns null - xunit

Here's the strange thing - when I run the Unit Test and I put the breakpoint on the line return Unit.Value;, then I see that the someResult value is null - but when I manually move the debugger's yellow arrow the the previous line again var someResult = await _myRepository.DoSomething(cancellationToken); then the someResult variable is not null and contains my object - why does that happen ?
Unit test snippet:
//Arrange
var myList = new List<MyTable> { new MyTable() };
var myRepository = Substitute.For<IMyRepository>();
myRepository.DoSomething(Arg.Any<CancellationToken>()).Returns(myList);
var command = Substitute.For<MyCommand>();
//Act
var sut = new MyCommandHandler(myRepository);
await sut.Handle(command, Arg.Any<CancellationToken>());
I also tried:
myRepository.DoSomething(Arg.Any<CancellationToken>()).Returns(Task.FromResult(myList));
The tested class:
public class MyCommandHandler : ICommandHandler<MyCommand, Unit>
{
private readonly IMyRepository _myRepository;
public MyCommandHandler(IMyRepository myRepository)
{
_myRepository = myRepository ?? throw new ArgumentNullException(nameof(myRepository));
}
public async Task<Unit> Handle(MyCommand command, CancellationToken cancellationToken)
{
var someResult = await _myRepository.DoSomething(cancellationToken);
...
return Unit.Value;
}
}
public class MyRepository : IMyRepository
{
private readonly MyDbContext _context;
public MyRepository(MyDbContext context)
{
_context = context;
}
public async Task<List<MyTable>> DoSomething(CancellationToken cancellationToken = default)
{
return await _context.MyTable
.AsNoTracking()
.Where(...)
.ToListAsync(cancellationToken);
}
}

I think the Arg.Any<CancellationToken>() matcher in your Handle invocation is the problem.
Changing
await sut.Handle(command, Arg.Any<CancellationToken>());
to
await sut.Handle(command, default);
got the test working. The matchers are for configuring the substitute and checking the received invocations, not for the actual invocation itself.
A working example can be found here. I didn't have enough to go on to replicate Unit and it's usage but was able to observe and resolve the same problem you were having without it.

Related

How to get HttpContext from Controller

I'm using ASP.NET Core (MVC)
If I call an endpoint, then this.HttpContext is not null.
Within the same class as my endpoint, if I put a break point in the controller, this.HttpContext is always null.
How do I get the value of HttpContext from the controller?
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class LoginController : ControllerBase
{
public LoginController()
{
var isNull = this.HttpContext; //always null
}
[HttpGet]
public async Task Get()
{
var isNull = this.HttpContext; //not null
}
}
The purpose for this, is on each end point, I want to access some values (which are from a cookie). In NET Framework, I'd store the cookie values in a base class (from within the constructor).
Whilst I can access HTTPContext on each each end point, doing it in the constructor means code it once per class.
The goal is very much about coding this less. I'm hoping I'm not just being lazy
No, it is not the correct way to do it. you need to use Filter or middleware to do it. HttpContext class is always null in the constructor of a controller
Sample middleware code (for logging)
you can do whatever in this like read cookies or whatnot
public class LoggingMiddleware
{
private static readonly TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.CreateDefault();
private readonly TelemetryClient telemetryClient;
private IConfiguration configuration;
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
private readonly string appName;
private readonly bool loggingEnabled;
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next, IConfiguration config)
{
_next = next;
configuration = config;
_recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
telemetryConfiguration.InstrumentationKey = configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
telemetryClient = new TelemetryClient(telemetryConfiguration);
appName = configuration.GetValue<string>("AppName");
loggingEnabled = configuration.GetValue<bool>("Logging:LogRequestResponse");
}
public async Task Invoke(HttpContext httpContext)
{
if(loggingEnabled)
{
await LogRequest(httpContext);
await LogResponse(httpContext);
}
}
private async Task LogRequest(HttpContext context)
{
context.Request.EnableBuffering();
await using var requestStream = _recyclableMemoryStreamManager.GetStream();
await context.Request.Body.CopyToAsync(requestStream);
string correlationId = context.Request.Headers.Keys.FirstOrDefault(h => h.ToLower() == "correlationid");
if (correlationId == null) correlationId = string.Empty;
if (context.Request.Path != "/")
{
telemetryClient.TrackEvent($"{appName}-RequestMiddleware", new Dictionary<string, string>
{
{ "AppName", appName },
{ "CorrelationId" , correlationId },
{ "Method" , context.Request.Method },
{ "Scheme", context.Request.Scheme},
{ "Host", context.Request.Host.Value },
{ "Path", context.Request.Path },
{ "QueryString", context.Request.QueryString.Value },
{ "Request Body", ReadStreamInChunks(requestStream) }
});
}
context.Request.Body.Position = 0;
}
private static string ReadStreamInChunks(Stream stream)
{
const int readChunkBufferLength = 4096;
stream.Seek(0, SeekOrigin.Begin);
using var textWriter = new StringWriter();
using var reader = new StreamReader(stream);
var readChunk = new char[readChunkBufferLength];
int readChunkLength;
do
{
readChunkLength = reader.ReadBlock(readChunk,
0,
readChunkBufferLength);
textWriter.Write(readChunk, 0, readChunkLength);
} while (readChunkLength > 0);
return textWriter.ToString();
}
private async Task LogResponse(HttpContext context)
{
var originalBodyStream = context.Response.Body;
await using var responseBody = _recyclableMemoryStreamManager.GetStream();
context.Response.Body = responseBody;
await _next(context);
context.Response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(context.Response.Body).ReadToEndAsync();
context.Response.Body.Seek(0, SeekOrigin.Begin);
if (context.Request.Path != "/")
{
telemetryClient.TrackEvent($"{appName}-ResponseMiddleware", new Dictionary<string, string> {
{"Scheme", context.Request.Scheme},
{ "AppName", appName },
{"Host", context.Request.Host.Value},
{"Path" , context.Request.Path},
{"QueryString", context.Request.QueryString.Value},
{"Response Body" , text}
});
}
await responseBody.CopyToAsync(originalBodyStream);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class LoggingMiddlewareExtensions
{
public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<LoggingMiddleware>();
}
}
No, you can't do it that way, controller constructors is danger zone (unless you know what you're doing) and should be used for DI only.
Instead, you should look at custom middleware:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-3.1
More info on asp.net Core life-cycles:
https://www.c-sharpcorner.com/article/asp-net-core-mvc-request-life-cycle/

RoutingSlipActivityCompleted not trigger

I added a consumer to observe routing slip events, but doesn't work as expected. RoutingSlipCompleted consumer is always triggered, RoutingSlipActivityCompleted and RoutingSlipActivityFaulted consumer are never triggered. This is my consumer code.
public abstract class RoutingSlipExecuteActivityResponseProxy<TRequest, TResponse, TFaultResponse> :
IConsumer<RoutingSlipActivityCompleted>,
IConsumer<RoutingSlipActivityFaulted>,
IConsumer<RoutingSlipCompleted>
where TRequest : class
where TResponse : class
where TFaultResponse : class
{
public abstract string ActivityName { get; }
public async Task Consume(ConsumeContext<RoutingSlipActivityCompleted> context)
{
if(context.Message.ActivityName!= ActivityName)
{
return;
}
var request = context.Message.GetVariable<TRequest>("Request");
var requestId = context.Message.GetVariable<Guid>("RequestId");
Uri responseAddress = null;
if (context.Message.Variables.ContainsKey("ResponseAddress"))
responseAddress = context.Message.GetVariable<Uri>("ResponseAddress");
if (responseAddress == null)
throw new ArgumentException($"The response address could not be found for the faulted routing slip: {context.Message.TrackingNumber}");
var endpoint = await context.GetResponseEndpoint<TResponse>(responseAddress, requestId).ConfigureAwait(false);
var response = await CreateResponseMessage(context, request);
await endpoint.Send(response).ConfigureAwait(false);
}
public async Task Consume(ConsumeContext<RoutingSlipActivityFaulted> context)
{
if (context.Message.ActivityName != ActivityName)
{
return;
}
var request = context.Message.GetVariable<TRequest>("Request");
var requestId = context.Message.GetVariable<Guid>("RequestId");
Uri faultAddress = null;
if (context.Message.Variables.ContainsKey("FaultAddress"))
faultAddress = context.Message.GetVariable<Uri>("FaultAddress");
if (faultAddress == null && context.Message.Variables.ContainsKey("ResponseAddress"))
faultAddress = context.Message.GetVariable<Uri>("ResponseAddress");
if (faultAddress == null)
throw new ArgumentException($"The fault/response address could not be found for the faulted routing slip: {context.Message.TrackingNumber}");
var endpoint = await context.GetFaultEndpoint<TResponse>(faultAddress, requestId).ConfigureAwait(false);
var response = await CreateFaultedResponseMessage(context, request, requestId);
await endpoint.Send(response).ConfigureAwait(false);
}
protected abstract Task<TResponse> CreateResponseMessage(ConsumeContext<RoutingSlipActivityCompleted> context, TRequest request);
protected abstract Task<TFaultResponse> CreateFaultedResponseMessage(ConsumeContext<RoutingSlipActivityFaulted> context, TRequest request, Guid requestId);
public Task Consume(ConsumeContext<RoutingSlipCompleted> context)
{
throw new NotImplementedException();
}
}
My activity has no additional configuration, basically it is written according to the documentation.
You might want to check out this sample, which uses the RequestResponseProxy to handle a request via routing slip, and then generates the response based upon the RoutingSlipCompleted/RoutingSlipFaulted events.

Do I need to impliment UnitOfWork for EF Core and if so is there a standard pattern somewhere?

I have read in some places that EF already implements it's own UnitOfWork and transactions.
I am looking at the following solutions:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
I don't really like this because I don't want to have to manually add in every type of repo I have as that goes against the reason for the GenericRepository which I've already put in a lot of work to making generic already.
Also looking at the UnitOfWork Attribute solution described here but backing away because of the reasons discussed by the author himself:
Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter
But let me try to lay out my question in the discussion below.
I have a Generic Repo and a Generic Service. They are registered in my Startup like this:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
My Generic Repo looks like this (leaving out Interfaces for brevity):
public enum FilteredSource
{
All,
GetAllIncluding,
}
public class GenericRepository<T> : IGenericRepository<T>
where T: BaseEntity
{
protected readonly ApplicationDbContext _context;
protected DbSet<T> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
// no eager loading
private IQueryable<T> All => _dbSet.Cast<T>();
// eager loading
private IQueryable<T> GetAllIncluding(
params Expression<Func<T, object>>[] includeProperties) =>
includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));
// eager loading
public async Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> entities = GetAllIncluding(includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return await entities.SingleOrDefaultAsync(e => e.Id == id);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
}
/// <summary>
/// Takes in a lambda selector and let's you filter results from GetAllIncluding or All.
/// </summary>
/// <param name="selector">labmda expression to filter results by.</param>
/// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
/// <param name="includeProperties">array of eager load lamda expressions.</param>
/// <returns></returns>
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All.Where(selector);
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties).Where(selector);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All;
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<T> InsertAsync(T entity)
{
if (entity == null)
{
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Insert");
}
await _dbSet.AddAsync(entity);
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
T entityToUpdate = await
_dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
if (entityToUpdate == null)
{
//return null;
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Update");
}
_dbSet.Update(entity);
return entity;
}
public async Task<T> DeleteAsync(T entity)
{
_dbSet.Remove(entity);
return await Task.FromResult(entity);
}
public Task SaveAsync() => _context.SaveChangesAsync();
}
Service Layer looks like this:
public class GenericService<T> : IGenericService<T>
where T : BaseEntity
{
private IGenericRepository<T> _genericRepo;
public GenericService(IGenericRepository<T> genericRepo)
{
_genericRepo = genericRepo;
}
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetFiltered(selector, filteredSource,
includeProperties);
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
return await _genericRepo.GetUnFiltered(filteredSource,
includeProperties);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _genericRepo.GetSingleIncludingAsync(id);
}
// eager loading
public async Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties)
{
T entity = await _genericRepo.GetSingleIncludingAsync(id, includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return entity;
}
public async Task<T> InsertAsync(T entity)
{
var result = await _genericRepo.InsertAsync(entity);
await _genericRepo.SaveAsync();
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
var result = await _genericRepo.UpdateAsync(entity);
if (result != null)
{
await _genericRepo.SaveAsync();
}
return result;
}
public async Task<T> DeleteAsync(T entity)
{
throw new NotImplementedException();
}
}
An example of a MVC Core Web API controller that uses the service looks like this:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericService<Employee> _genericService;
public EmployeesController(IGenericService<Employee> genericService)
{
_genericService = genericService;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
return await _genericService.GetFiltered(
e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
FilteredSource.GetAllIncluding,
new Expression<Func<Employee, object>>[] { a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit }
);
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
var employee = await _genericService.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _genericService.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}
}
So here are my questions:
The way I understand it, UnitOfWork is used in case you bring in two repositories into a service or controller. If you manipulate data in repo 1 and it passes, and then manipulate data in repo 2 and it fails (or visa versa) you want to roll everything back.
So far I am not using two repos. But if this situation occurs, it will be because I am bringing in two GenericServices to a controller, which will in turn bring in two GenericRepos.
Now lets talk about scope.
I have to bring in my GenericRepo as Scoped. Not Singleton. Because if I query an object on one request, and then try to update that object on the next request, I will get an error that the object cannot be updated because it is already being tracked by the Singleton Repo from the previous request.
So I bring it in Scoped as shown in the StartUp segment:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
I also bring in the service as scoped. Pretty much guessing I should bring it in as scoped also.
Now, if I bring in a GenericService of Type Employee -> which gets a GenericRepository of Type Employee into a controller and I also bring in GenericService of Type Case -> which gets a GenericRepository of Type Case, are these two different GenericRepos? Or are they the same Repo?
Will they be treated as the same transaction and all pass or fail together?
Or do I need to manually implement UnitOfWork?
Another factor I think that goes into this is whether the following baked into Core DI line is Singleton or Scoped:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConn")));
You are right to use Scoped but see below: it's your ApplicationDbContext that must be scoped, the services and repos can be transient.
Since IGenericRepo<Employee> is a different type to IGenericRepo<Case> yes you will get two different GenericRepo<T>'s (and the same logic applies to the services).
But about the UnitOfWork pattern in the article you link, I don't get your comment that "I don't really like this because I don't want to have to manually add in every type of repo ..."
You could change the UoW code in the article to a GenericUnitOfWork<T1,T2> ?
Or you could take the view that for each controller which needs to write to 2 repos, you write a UoW class specifically for it. Note that you can strip out the lazy getter code that the article uses in its UoW class, because your repos are already created for you anyway by the services container, so the UoW reduces to only a few lines of mostly-boiler code anyway.
public class UnitOfWork<T1,T2> : IDisposable
{
ApplicationDbContext context;
GenericRepo<T1> Repo1 {get; private set;}
GenericRepo<T2> Repo2 {get; private set;}
public UnitOfWork(ApplicationDbContext context)
{
this.context=context;
Repo1 =new GenericRepo<T1>(context)
Repo2 =new GenericRepo<T2>(context)
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
But here's the important thing: the UoW pattern as presented in the article depends crucially on both repos using the same DbContext (otherwise it would be a distributed transaction and requires more code)
So it's important that your ApplicationDbContext is scoped:
services.AddScoped(typeof(ApplicationDbContext), typeof(ApplicationDbContext));
However, you do have to be sure that every controller in your application can happily accept this constraint that it should only need one underlying ApplicationDbContext. For must cases, it should be fine.
Finally, you might as well be explicit that the real dependency is the DbContext:
public class EmployeesController : Controller
{
UnitofWork<T1,T2> uow;
public EmployeesController(ApplicationDbContext dbContext)
{
this.uow= new UnitofWork<Employee,Case>(dbContext)
}
//...

Can CRM OrgService and Context be static?

This is design question/query so I hope it's Ok to post...
Pretty much every CRM OrgService and Context example you see is not static. Normally uses a using block. Which works of course, but could this be a static class rather than creating/destroying this over and over and over with using blocks everywhere? Is there any issue doing this?
Thanks for your advice :-)
Edit: Here's the code. Do you see any issue with this?...
CrmServiceContext is created by the early-bound entities generator.
static public class CRMOrgService
{
static private IOrganizationService _orgService = null;
static private CrmServiceContext _context = null;
static public IOrganizationService OrgService {
get
{
if (_orgService == null)
{
var reader = new AppSettingsReader();
Uri crmUri = new Uri(reader.GetValue("CRMOrgSvc", typeof(string)).ToString());
string crmUser = reader.GetValue("CRMUser", typeof(string)).ToString();
string crmPass = reader.GetValue("CRMPass", typeof(string)).ToString();
// Your client credentials
ClientCredentials clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = crmUser;
clientCredentials.UserName.Password = crmPass;
// Create your Organization Service Proxy
var proxy = new OrganizationServiceProxy(crmUri, null, clientCredentials, null);
proxy.EnableProxyTypes();
_orgService = (IOrganizationService)proxy;
}
return _orgService;
}
}
static public CrmServiceContext Context
{
get
{
if (_context == null)
{
_context = new CrmServiceContext(OrgService);
}
return _context;
}
}
static public void CloseCleanUp()
{
_context.ClearChanges();
_context.Dispose();
_context = null;
_orgService = null;
}
} // end class
Yes, it can be static. However, you have to keep in mind this instance will not be thread safe. This means, the connection instance can not be used simultaneous by multiple threads.

Simple Injector inject dependency into custom global authentication filters and OWIN middle ware OAuthAuthorizationServerProvider

I used Simple Injector as our Ioc container; we have two problems.
We want to inject into our custom authentication filter; we read the post of converting attribute to a passive attribute: Convert Attribute into a passive. But we can't convert custom authentication filter attribute into a passive.
public class BearerAuthentication : Attribute, IAuthenticationFilter
{
public async Task AuthenticateAsync(
HttpAuthenticationContext context, CancellationToken cancellationToken)
{
}
public Task ChallengeAsync(
HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
}
}
We want to inject dependency into OWin middleware OAuthAuthorizationServerProvider; we know we can use begin execution context scope, but we want an elegant solution.
using (Ioc.Container.BeginExecutionContextScope())
{
}
Updated
public interface IAuthenticationFilter<TAttribute> where TAttribute : Attribute
{
Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken);
Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken);
}
public class BearerAuthenticationFilter : Attribute, IAuthenticationFilter<BearerAuthenticationFilter>
{
private readonly IAuthenticationBusinessEngine _authenticationBusinessEngine;
private readonly IHttpContextAccessor _httpContextAccessor;
public BearerAuthenticationFilter(IAuthenticationBusinessEngine authenticationBusinessEngine, IHttpContextAccessor httpContextAccessor)
{
_authenticationBusinessEngine = authenticationBusinessEngine;
_httpContextAccessor = httpContextAccessor;
}
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
public class AuthenticationFilterDispatcher : IAuthenticationFilter
{
private readonly Func<Type, IEnumerable> _container;
public AuthenticationFilterDispatcher(Func<Type, IEnumerable> container)
{
_container = container;
}
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var descriptor = context.ActionContext.ActionDescriptor;
var attributes = descriptor.ControllerDescriptor.GetCustomAttributes<Attribute>(true)
.Concat(descriptor.GetCustomAttributes<Attribute>(true));
foreach (var attribute in attributes)
{
var filterType = typeof(IAuthenticationFilter<>).MakeGenericType(attribute.GetType());
var filters = _container.Invoke(filterType);
foreach (dynamic actionFilter in filters)
{
await actionFilter.AuthenticateAsync(context, cancellationToken);
}
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public bool AllowMultiple
{
get
{
return true;
}
}
}
The equivalent code for working with IAuthenticationFilter is:
public interface IAuthenticationFilter<TAttribute> where TAttribute : Attribute
{
Task AuthenticateAsync(TAttribute attribute, HttpAuthenticationContext context);
}
public class AuthenticationFilterDispatcher : IAuthenticationFilter
{
private readonly Func<Type, IEnumerable> container;
public AuthenticationFilterDispatcher(Func<Type, IEnumerable> container) {
this.container = container;
}
public async Task AuthenticateAsync(HttpAuthenticationContext context,
CancellationToken token) {
var descriptor = context.ActionContext.ActionDescriptor;
var attributes = descriptor.ControllerDescriptor
.GetCustomAttributes<Attribute>(true)
.Concat(descriptor.GetCustomAttributes<Attribute>(true));
foreach (var attribute in attributes) {
Type filterType = typeof(IAuthenticationFilter<>)
.MakeGenericType(attribute.GetType());
IEnumerable filters = this.container.Invoke(filterType);
foreach (dynamic actionFilter in filters) {
await actionFilter.AuthenticateAsync((dynamic)attribute, context);
}
}
}
public async Task ChallengeAsync(HttpAuthenticationChallengeContext context,
CancellationToken token) { }
public bool AllowMultiple { get { return true; } }
}
Registration is done as follows:
GlobalConfiguration.Configuration.Filters.Add(
new AuthenticationFilterDispatcher(container.GetAllInstances));
// For Simple Injector 2.x:
container.RegisterManyForOpenGeneric(typeof(IAuthenticationFilter<>),
container.RegisterAll,
new[] { typeof(IAuthenticationFilter<>).Assembly });
// For Simple Injector 3.x:
container.RegisterCollection(typeof(IAuthenticationFilter<>),
new[] { typeof(IAuthenticationFilter<>).Assembly });
Now instead of making your attributes active, you can make the attribute passive and implement the required logic inside an IAuthenticationFilter<MyPassiveAttribute> implementation.
Your attribute and new component might look like this:
// NOTE: This attribute does not derive from anything Web API specific,
// just from Attribute
public class RequiresBearerAuthenticationAttribute : Attribute
{
// put here properties if required
}
public class BearerAuthenticationFilter
: IAuthenticationFilter<RequiresBearerAuthenticationAttribute>
{
private readonly IAuthenticationBusinessEngine _authenticationBusinessEngine;
private readonly IHttpContextAccessor _httpContextAccessor;
public BearerAuthenticationFilter(
IAuthenticationBusinessEngine authenticationBusinessEngine,
IHttpContextAccessor httpContextAccessor)
{
_authenticationBusinessEngine = authenticationBusinessEngine;
_httpContextAccessor = httpContextAccessor;
}
public async Task AuthenticateAsync(RequiresBearerAuthenticationAttribute attribute,
HttpAuthenticationContext context)
{
// TODO: Behavior here
}
}

Resources