Autofac logging module & Resolved Parameter in ASP.Net Web Forms - c#

I'm trying to use an Autofac module to inject NLog logger to asp.net Web Forms application (code behind) but i'm getting an error when trying to use a ResolvedParameter:
Global asax & Logging module:
public class Global : HttpApplication, IContainerProviderAccessor
{
static IContainerProvider _containerProvider;
public IContainerProvider ContainerProvider
{
get { return _containerProvider; }
}
void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();
builder.RegisterModule<LoggingModule>();
_containerProvider = new ContainerProvider(builder.Build());
}
}
public class LoggingModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(RegsiterFunc).AsImplementedInterfaces();
}
private NLogger RegsiterFunc(IComponentContext arg, IEnumerable<Parameter> parameters)
{
return new NLogger(parameters.TypedAs<Type>());
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing +=
(sender, args) =>
{
var forType = args.Component.Activator.LimitType;
var logParameter = new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILogger),
(p, c) => c.Resolve<ILogger>(TypedParameter.From(forType)));
args.Parameters = args.Parameters.Union(new[] { logParameter });
};
}
}
NLogger class:
public class NLogger : ILogger
{
private readonly Logger m_Logger;
public NLogger(Type type)
{
m_Logger = LogManager.GetLogger(type.FullName);
}
public NLogger(string typeName)
{
m_Logger = LogManager.GetLogger(typeName);
}
.......
}
Aspx page code behind:
public partial class _Default : Page
{
public ILogger m_Logger { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
m_Logger.Error("test");
}
}
To initiate the log based on the requesting type I'm using a ResolvedParameter.
The line parameters.TypedAs() in RegsiterFunc throws an exception: "Sequence contains no elements".
Same code works in asp.net MVC application but fails in the web forms project.
System.InvalidOperationException occurred HResult=0x80131509
Message=Sequence contains no elements Source=Autofac
StackTrace: at Autofac.ParameterExtensions.ConstantValue[TParameter,TValue](IEnumerable`1 parameters, Func`2 predicate)
at Autofac.ParameterExtensions.TypedAs[T](IEnumerable`1 parameters)
at WebApplication1.LoggingModule.RegsiterFunc(IComponentContext arg, IEnumerable`1 parameters) in Global.asax.cs:line 55
at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.b__0(IComponentContext c, IEnumerable`1 p)
at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)

As far as I understand, in RegsiterFunc you are attempting to retrieve type of component the logger is being injected into.
But the parameter that carries component type is not a TypedParameter, but rather it's a NamedParameter. That's why you get the exception.
Though less elegant, but working version of RegsiterFunc:
private NLogger RegsiterFunc(IComponentContext arg, IEnumerable<Parameter> parameters)
{
var type = (Type)((NamedParameter)parameters.First()).Value;
return new NLogger(type);
}
Then for Default.aspx page, the type passed to NLogger will be ASP.default_aspx.
The second parameter you create:
new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILogger),
(p, c) => c.Resolve<ILogger>(TypedParameter.From(forType)));
is not in use, so that overriding AttachToComponentRegistration is unnecessary.

Related

Could not find an IRouter associated with the ActionContext Error

In my app, I want to use Redirect when any method throws a RedirectException.
For example...
Note: ActivePeriodException inherits from RedirectException
if (Period.ActivePeriod(Db))
throw new ActivePeriodException();
My ActivePeriodException class is
public class ActivePeriodException : RedirectException
{
public ActivePeriodException ()
{
PathRedirect = Constantes.PathMantenimiento;
}
protected ActivePeriodException (SerializationInfo serializationInfo, StreamingContext streamingContext) :
base(serializationInfo, streamingContext)
{
PathRedirect = Constantes.PathMantenimiento;
}
public override string Message =>
"Hello World :)";
}
And this example throws me:
Could not find an IRouter associated with the ActionContext.
If your application is using endpoint routing then you can get a IUrlHelperFactory with dependency injection
and use it to create a UrlHelper, or use Microsoft.AspNetCore.Routing.LinkGenerator.
public override void OnException(ExceptionContext context)
{
_logger = ApplicationLogging.LoggerFactory.CreateLogger(context.RouteData.Values["controller"].ToString());
HandleCustomException(context);
base.OnException(context);
}
private void HandleCustomException(ExceptionContext context)
{
if (context.Exception is RedirectException exception)
{
var urlHelper = new UrlHelper(context);
var path = exception.PathRedirect;
path = IsHTTP(path) ? path : urlHelper.RouteUrl(path); //HERE IS THE PROBLEM
_logger.LogInformation(ApplicationLogging.ExceptionMessage(context.Exception));
Session.MsjErrorView = exception.Message;
context.HttpContext.Response.Redirect(path);
FuncionesUtiles.ArmarRespuestaErrorRedirect(context, path);
if (exception.NotificarViaMail)
MailHelper.MandarMail(MailHelper.GetMensajeMailDeError(context), new List<string> { Configuracion.GetValueFromCache(Configuraciones.MailEnvioErrores) }, Constantes.MailSubject);
}
else if (context.Exception is CustomException)
{
_logger.LogInformation(ApplicationLogging.ExceptionMessage(context.Exception));
FuncionesUtiles.ArmarMsgRespuestaError(context, null);
}
}
How can I fix that problem?
Try building UrlHelper with IUrlHelperFactory resolved from the context services:
public void OnException(ExceptionContext context)
{
// ...
var urlHelperFactory = context.HttpContext.RequestServices.GetRequiredService<IUrlHelperFactory>();
var urlHelper = urlHelperFactory.GetUrlHelper(context);
// ...
}

Could not load type Castle.Proxies.IReadinessProxy when running xUnit integration tests in parallel with Autofac

I am having an issue that I've been many days unable to resolve.
I use xUnit with a given-then-when abstraction to make tests more readable.
I am using a wrapper over EventStore and running some integration tests. They all go well.. except one that fails when running all in parallel (and xUnit runs in parallel), but if I run them all sequentially they all succeed.
I cannot understand why this would be an issue because every single fact is supposed to run the constructor (the given) and the functionality to test (the when). And in each fact I instantiate an Autofac ContainerBuilder, build the container and resolve its IComponentContext, so in theory every test should be isoalted and idempotent as intended.
This is the exception I keep receiving:
Autofac.Core.DependencyResolutionException : An exception was thrown while activating SalesOrder.EventStore.Infra.EventStore.EventStore -> SalesOrder.EventStore.Infra.EventStore.DomainEventsRetriever -> SalesOrder.EventStore.Infra.EventStore.Factories.DomainEventFactory -> λ:SalesOrder.EventStore.Infra.EventStore.EventTypeResolver.
---- System.Reflection.ReflectionTypeLoadException : Unable to load one or more of the requested types.
Could not load type 'Castle.Proxies.IReadinessProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget) in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 136
at Autofac.Core.Resolving.InstanceLookup.Execute() in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 85
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Resolving\ResolveOperation.cs:line 130
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Resolving\ResolveOperation.cs:line 83
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 1041
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 871
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\ResolutionExtensions.cs:line 300
at SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests.EventStoreExtensionsTests.ResolveTests.Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore.When() in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests\EventStoreExtensionsTests\ResolveTests.cs:line 53
at SalesOrder.EventStore.Infra.EventStore.Autofac.IntegrationTests.EventStoreExtensionsTests.ResolveTests.Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore..ctor()
----- Inner Stack Trace -----
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at System.Reflection.Assembly.GetTypes()
at SalesOrder.EventStore.Infra.EventStore.Autofac.EventStoreExtensions.<>c.<RegisterResolvers>b__6_2(Assembly s) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac\EventStoreExtensions.cs:line 174
at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
at SalesOrder.EventStore.Infra.EventStore.Factories.EventTypeResolverFactory.Create(IEnumerable`1 existingTypes, IReadOnlyDictionary`2 domainEventSerializerDeserializers) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore\Factories\EventTypeResolverFactory.cs:line 16
at SalesOrder.EventStore.Infra.EventStore.Autofac.EventStoreExtensions.<>c__DisplayClass6_0.<RegisterResolvers>b__1(IComponentContext context) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac\EventStoreExtensions.cs:line 180
at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass0_0`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p) in C:\projects\autofac\src\Autofac\Builder\RegistrationBuilder.cs:line 62
at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) in C:\projects\autofac\src\Autofac\Core\Activators\Delegate\DelegateActivator.cs:line 71
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget) in C:\projects\autofac\src\Autofac\Core\Resolving\InstanceLookup.cs:line 118
This is the test with only one Fact that fails when running in parallel with others:
public class Given_A_Container_With_Event_Store_Registered_When_Resolving_An_IEventStore
: Given_When_Then_Test
{
private IEventStore _sut;
private IComponentContext _componentContext;
protected override void Given()
{
var builder = new ContainerBuilder();
builder
.RegisterEventStore(
ctx =>
{
var eventStoreOptions =
new EventStoreOptions
{
Url = EventStoreTestConstants.TestUrl,
Port = EventStoreTestConstants.TestPort
};
return eventStoreOptions;
},
ctx =>
{
var readinessOptions =
new ReadinessOptions
{
Timeout = EventStoreTestConstants.TestTimeout
};
return readinessOptions;
});
var container = builder.Build();
_componentContext = container.Resolve<IComponentContext>();
}
protected override void When()
{
_sut = _componentContext.Resolve<IEventStore>();
}
[Fact]
public void Then_It_Should_Not_Be_Null()
{
_sut.Should().NotBeNull();
}
}
Any clue what may be happening here?
I've had a look at Autofac's guidelines on concurrency: https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html but I think I am already using properly the component context.
UPDATE 1: FYI this is the GivenThenWhen template I use. But nothing very special here (I think!)
namespace ToolBelt.TestSupport
{
public abstract class Given_When_Then_Test
: IDisposable
{
protected Given_When_Then_Test()
{
Setup();
}
private void Setup()
{
Given();
When();
}
protected abstract void Given();
protected abstract void When();
public void Dispose()
{
Cleanup();
}
protected virtual void Cleanup()
{
}
}
public abstract class Given_WhenAsync_Then_Test
: IDisposable
{
protected Given_WhenAsync_Then_Test()
{
Task.Run(async () => { await SetupAsync(); }).GetAwaiter().GetResult();
}
private async Task SetupAsync()
{
Given();
await WhenAsync();
}
protected abstract void Given();
protected abstract Task WhenAsync();
public void Dispose()
{
Cleanup();
}
protected virtual void Cleanup()
{
}
}
}
UPDATE 2: And this is the IoCC Autofac registration's method I am using for my implementation and all the tests. A bit complex because I use reflection to keep the EventStore wrapper fully generic, but just in case somebody sees something funky there that might affect the tests..
public static class EventStoreExtensions
{
public static void RegisterEventStore(
this ContainerBuilder builder,
Func<IComponentContext, EventStoreOptions> optionsRetriever,
Func<IComponentContext, ReadinessOptions> readinessOptionsRetriever,
Func<IComponentContext, CustomDomainEventMappersOptions> customDomainEventMappersOptionsRetriever = null)
{
RegisterEventStoreConnection(builder, optionsRetriever);
RegisterFactories(builder);
RegisterEventStoreManager(builder);
RegisterConverters(builder);
RegisterResolvers(builder, customDomainEventMappersOptionsRetriever);
RegisterEventStoreServices(builder);
RegisterEventStoreReadinessCheck(builder, readinessOptionsRetriever);
}
private static void RegisterEventStoreReadinessCheck(
ContainerBuilder builder,
Func<IComponentContext, ReadinessOptions> readinessOptionsRetriever)
{
builder
.Register(context =>
{
var ctx = context.Resolve<IComponentContext>();
var readinessOptions = readinessOptionsRetriever.Invoke(ctx);
var timeout = readinessOptions.Timeout;
var eventStoreConnection = context.Resolve<IEventStoreConnection>();
var eventStoreReadiness =
new EventStoreReadiness(
eventStoreConnection,
timeout);
return eventStoreReadiness;
})
.As<IEventStoreReadiness>()
.As<IReadiness>()
.SingleInstance();
}
private static void RegisterEventStoreConnection(
ContainerBuilder builder,
Func<IComponentContext, EventStoreOptions> optionsRetriever)
{
builder
.Register(context =>
{
var ctx = context.Resolve<IComponentContext>();
var eventStoreOptions = optionsRetriever.Invoke(ctx);
ConnectionSettings connectionSetting =
ConnectionSettings
.Create()
.KeepReconnecting();
var urlString = eventStoreOptions.Url;
var port = eventStoreOptions.Port;
var ipAddress = IPAddress.Parse(urlString);
var tcpEndPoint = new IPEndPoint(ipAddress, port);
var eventStoreConnection = EventStoreConnection.Create(connectionSetting, tcpEndPoint);
return eventStoreConnection;
})
.As<IEventStoreConnection>()
.SingleInstance();
}
private static void RegisterFactories(
ContainerBuilder builder)
{
builder
.RegisterType<DomainEventFactory>()
.As<IDomainEventFactory>()
.SingleInstance();
builder
.RegisterType<EventDataFactory>()
.As<IEventDataFactory>()
.SingleInstance();
builder
.RegisterType<ExpectedVersionFactory>()
.As<IExpectedVersionFactory>()
.SingleInstance();
builder
.RegisterType<IdFactory>()
.As<IIdFactory>()
.SingleInstance();
builder
.RegisterType<StreamNameFactory>()
.As<IStreamNameFactory>()
.SingleInstance();
builder
.RegisterType<RetrievedEventFactory>()
.As<IRetrievedEventFactory>()
.SingleInstance();
}
private static void RegisterEventStoreManager(ContainerBuilder builder)
{
builder
.RegisterType<EventStoreManager>()
.As<IEventStoreManager>()
.SingleInstance();
}
private static void RegisterConverters(ContainerBuilder builder)
{
builder
.Register(context =>
{
var utf8Encoding = new BytesConverter(Encoding.UTF8);
return utf8Encoding;
})
.As<IBytesConverter>()
.SingleInstance();
builder
.RegisterType<NewtonsoftConverter>()
.As<IJsonConverter>()
.SingleInstance();
}
private static void RegisterResolvers(
ContainerBuilder builder,
Func<IComponentContext, CustomDomainEventMappersOptions> customDomainEventMappersOptionsRetriever)
{
builder
.Register(context =>
{
var ctx = context.Resolve<IComponentContext>();
var customDomainEventMappersOptions = customDomainEventMappersOptionsRetriever?.Invoke(ctx);
var domainEventSerializerDeserializers =
customDomainEventMappersOptions?.DomainEventSerializerDeserializers;
var mapperResolver = MapperResolverFactory.Create(domainEventSerializerDeserializers);
return mapperResolver;
})
.As<IMapperResolver>()
.SingleInstance();
builder
.Register(context =>
{
var ctx = context.Resolve<IComponentContext>();
var assembliesLoaded = AppDomain.CurrentDomain.GetAssemblies();
var domainEventTypes =
assembliesLoaded
.SelectMany(s => s.GetTypes())
.Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
&& x.IsClass);
var customDomainEventMappersOptions = customDomainEventMappersOptionsRetriever?.Invoke(ctx);
var domainEventSerializerDeserializers =
customDomainEventMappersOptions?.DomainEventSerializerDeserializers;
var typeResolver =
EventTypeResolverFactory.Create(
domainEventTypes,
domainEventSerializerDeserializers);
return typeResolver;
})
.As<IEventTypeResolver>()
.SingleInstance();
}
private static void RegisterEventStoreServices(ContainerBuilder builder)
{
builder
.RegisterType<EventStoreRepository>()
.As<IEventStoreRepository>();
builder
.RegisterType<DomainEventsPersister>()
.As<IDomainEventsPersister>();
builder
.RegisterType<DomainEventsRetriever>()
.As<IDomainEventsRetriever>();
builder
.RegisterType<EventStore>()
.As<IEventStore>();
}
}
UPDATE 3: This is the whole repository in case somebody is bored and wants to try to reproduce it. It is a generic event store wrapper used for event sourcing implemented for Greg Young's Event Store product using the official C# driver.
https://gitlab.com/DiegoDrivenDesign/DiDrDe.EventStore
Funnily enough, this issue seems to disappear every once in a while. In fact, often, after restarting the PC all the tests pass properly. Other times it doesn't so I suspect it has something to do with the fact that I'm loading assemblies at runtime and something gets out of whack :(
UPDATE 9 Sep 2021
The async execution I was doing is not entirely correct. There is a way to embrace async execution with xUnit that may have been playing a role in this race condition. This is a better implementation of the given-when-then pattern all my tests inherit from. Thanks to IAsyncLifetime I am able to asynchronously (and await) the preconditions and the action itself.
using System.Threading.Tasks;
using Xunit;
namespace Rubiko.TestSupport.XUnit
{
public abstract class Given_When_Then_Test_Async
: IAsyncLifetime
{
public async Task InitializeAsync()
{
await Given();
await When();
}
public async Task DisposeAsync()
{
await Cleanup();
}
protected virtual Task Cleanup()
{
return Task.CompletedTask;
}
protected abstract Task Given();
protected abstract Task When();
}
}
DynamicProxyGenAssembly2 is a temporary assembly built by mocking systems that use CastleProxy. There are some similar opened issues for NSubstitute and Moq that indicate that the problem is a race condition in Castle.Core or even in the .Net Framework (see: TypeLoadException or BadImageFormatException under heavy multi-threaded proxy generation for more details)
The inner exeption indicates that a type could not be loaded
Could not load type 'Castle.Proxies.IReadinessProxy' from assembly 'DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
and it came from this line :
at System.Reflection.Assembly.GetTypes()
at SalesOrder.EventStore.Infra.EventStore.Autofac.EventStoreExtensions.<>c <RegisterResolvers>b__6_2(Assembly s) in C:\src\SalesOrder.EventStore\SalesOrder.EventStore.Infra.EventStore.Autofac\EventStoreExtensions.cs:line 174
at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
which corresponds to this line in your code
var domainEventTypes = assembliesLoaded
.SelectMany(s => s.GetTypes())
.Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
&& x.IsClass);
It means that one of the assembly contains a type that contains a reference to another assembly which is not loaded. Based on the name of the type in error it seems to be related with castle auto generated type.
You can subscribe to the static AppDomain.CurrentDomain.AssemblyResolve and AppDomain.CurrentDomain.TypeResolve events to better understand why the assembly is not loaded and maybe load it manually. See Assembly.GetTypes() - ReflectionTypeLoadException for more information.
In some case the exception is "normal" and could be ignored with a code like this :
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
// TODO: Argument validation
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
Code from How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes()
And you can use this here :
var domainEventTypes = assembliesLoaded
.SelectMany(s => s.GetLoadableTypes())
.Where(x => typeof(IDomainEvent).IsAssignableFrom(x)
&& x.IsClass);
If you have COM dependencies, make sure you set Embed Interop Types to No in your unit tests projects for these COM dependencies. By default, this property is set to Yes. When you mockup objects in your unit tests from these COM objects, they should not be embedded.
your table of database id DatabaseGeneratedOption.Identity and you try to save id of code you get error

FluentValidation with Mediatr and Unity

I'm trying to use FluentValidation in a WebApi project (not asp.net Core).
I have the following code:
public static class UnityConfig
{
public static void RegisterComponents(UnityContainer container)
{
// Register validators
RegisterValidators(container);
// Mediatr
container.RegisterType<IMediator, Mediator>();
container.RegisterTypes(AllClasses.FromAssemblies(true, Assembly.GetExecutingAssembly()), WithMappings.FromAllInterfaces, GetName, GetLifetimeManager);
container.RegisterInstance<SingleInstanceFactory>(t => container.Resolve(t));
container.RegisterInstance<MultiInstanceFactory>(t => container.ResolveAll(t));
// Automapper profiles
var profileTypes = typeof(BaseProfile).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(BaseProfile)));
var config = new MapperConfiguration(cfg => new MapperConfiguration(x =>
{
foreach (var type in profileTypes)
{
var profile = (BaseProfile)Activator.CreateInstance(type);
cfg.AddProfile(profile);
}
}));
container.RegisterInstance<IConfigurationProvider>(config);
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
static LifetimeManager GetLifetimeManager(Type type)
{
return IsNotificationHandler(type) ? new ContainerControlledLifetimeManager() : null;
}
static string GetName(Type type)
{
return IsNotificationHandler(type) ? string.Format("HandlerFor" + type.Name) : string.Empty;
}
private static void RegisterValidators(IUnityContainer container)
{
var validators = AssemblyScanner.FindValidatorsInAssembly(Assembly.GetExecutingAssembly());
validators.ForEach(validator => container.RegisterType(validator.InterfaceType, validator.ValidatorType));
}
}
I'm scanning the assemblies and registrering the validators, of which there's only one right now, it sits here: (don't mind the weird validations, I'm trying to have it fail)
public class Query : IRequest<Result>
{
public Guid? Id { get; set; }
}
public class QueryValidator : AbstractValidator<Query>
{
public QueryValidator()
{
RuleFor(q => q.Id).Empty();
RuleFor(q => q.Id).Equal(Guid.NewGuid());
}
}
My Application_start looks like this:
protected void Application_Start()
{
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
GlobalConfiguration.Configure(WebApiConfig.Register);
var factory = new UnityValidatorFactory2(GlobalConfiguration.Configuration);
FluentValidationModelValidatorProvider.Configure(GlobalConfiguration.Configuration, x => x.ValidatorFactory = factory);
}
And I have the following validatorFactory:
public class UnityValidatorFactory2 : ValidatorFactoryBase
{
private readonly HttpConfiguration _configuration;
public UnityValidatorFactory2(HttpConfiguration configuration)
{
_configuration = configuration;
}
public override IValidator CreateInstance(Type validatorType)
{
var validator = _configuration.DependencyResolver.GetService(validatorType) as IValidator;
return validator;
}
}
Now; when I call the action on the controller, 'CreateInstance' tries to resolve a validatorType of the type:
IValidator<Guid>
instead of:
IValidator<Query>
and of course finds nothing, this means that my validations does not run.
Does anyone have an ideas as to why this is? it seems faily straight forward, so I have trouble seeing what goes wrong.
After having slept on it, I found the answer myself.
I was posting a Guid to my controller instead of the model I was trying to validate (which only contains a guid)
After posting the right model, it now validates correctly.

Passing Web Api Nhibernate Session to Controller

I have added a new WebApi project to my solution and Controllers Get Method calls a function which returns an Xml . Am calling an function which uses a Nhibernate ISession to instantiate . Am using MySql for my Db I get the following error.
Here is a trace of the error
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Object reference not set to an instance of an object.
</ExceptionMessage>
<ExceptionType>System.NullReferenceException</ExceptionType>
<StackTrace>
at comp.rest.RestApi.Controllers.RestApiController.Get() in C:\GitHub\rea-rest\src\comp.rest.RestApi\Controllers\RestApiController.cs:line 23 at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.<>c__DisplayClass5.<ExecuteAsync>b__4() at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)
</StackTrace>
</Error>
I do have a dependency resolver which i call from the global.asax file on app start
public class ApiDependencyResolver : IDependencyResolver
{
private static ISession _session;
private static ISettings _settings;
private static IDiscountService _discountService;
private static IAuctionService _auctionService;
private static IAuditService _auditService;
private NhProductAdminService productAdminService = new NhProductAdminService(_session, _settings,
_discountService,
_auctionService,
_auditService);
public IDependencyScope BeginScope()
{
// This example does not support child scopes, so we simply return 'this'.
return this;
}
public object GetService(Type serviceType)
{
if (serviceType == typeof(RestApiController))
{
return new RestApiController(productAdminService);
}
else
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
return new List<object>();
}
public void Dispose()
{
// When BeginScope returns 'this', the Dispose method must be a no-op.
}
}
Usually has the Nhibernate session instantiated once the controller is hit this is because we have already opened a session in the global.asax Application_Start, I am stuck at this for a few days now , Would be great for anyone to help me out , Am sure am doing something silly . Am new to WebApi .
In our Web Application we Open the Nhibernate Session using the global.asax .
builder.RegisterModule(new WebNHibernateModule());
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
The WebHibernate Class looks like this
public class WebNHibernateModule : NHibernateModule
{
protected override IPersistenceConfigurer DatabaseConfig
{
get
{
return
MySQLConfiguration.Standard
.ConnectionString(s => s.FromConnectionStringWithKey("ApplicationServices"))
.Driver<ProfiledSqlClientDriver>();
//.ShowSql();
}
}
protected override Action<MappingConfiguration> MappingConfig
{
get { return AutoConfig.Mappings; }
}
}
public class ProfiledSqlClientDriver : MySqlDataDriver
{
public override IDbCommand CreateCommand()
{
var command = base.CreateCommand();
if (MiniProfiler.Current != null)
{
command = DbCommandProxy.CreateProxy(command);
}
return command;
}
}
The NHibernateModule Class looks like this
public abstract class NHibernateModule : Module
{
protected abstract IPersistenceConfigurer DatabaseConfig { get; }
protected abstract Action<MappingConfiguration> MappingConfig { get; }
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(NhSessionQueryable<>)).As(typeof(IQueryable<>));
builder.RegisterType<NhQueryContext>().As<IQueryContext>();
builder.RegisterType<WebSessionTracker>().As<ISessionTracker>()
.InstancePerHttpRequest();
builder.Register(c => c.Resolve<ISessionFactory>().OpenSession())
.InstancePerHttpRequest()
.OnActivated(e =>
{
e.Context.Resolve<ISessionTracker>().CurrentSession = e.Instance;
e.Instance.BeginTransaction();
});
builder.Register(c =>
Fluently.Configure().Database(DatabaseConfig)
.Mappings(MappingConfig)
.BuildConfiguration())
.SingleInstance()
.OnActivated(e =>
{
e.Instance.Initialize(e.Context.Resolve<ValidatorEngine>());
new SchemaValidator(e.Instance).Validate(); // Validate the schema when we create the session factory
});
builder.Register(c => c.Resolve<Configuration>().BuildSessionFactory())
.SingleInstance();
}
}
Usually in our Web Application similar Autofac is used to prepopulate the session ,
Am doing the same for the WebApi as well , but the Nhibernate Session is still null .
It sounds like you have some confusion over the term "session"...
MVC usually has the session instantiated once the controller is hit
An NHibernate session is not the same thing as ASP.NET (MVC) session. You create an NHibernate session with ISessionFactory.OpenSession(). See chapter 3 of the documentation for a more detailed explanation of how to get an NHibernate session.

Using Unity to inject dependencies into a custom ActionFilter

At the moment, I have a custom ControllerFactory into which I inject my Unity container:
in global.asax Application_Start():
var container = InitContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
var factory = new UnityControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(factory);
In the controller factory I set my controllers to use a custom ActionInvoker like so:
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
var controller = base.GetControllerInstance(requestContext, controllerType) as Controller;
if (controller != null)
controller.ActionInvoker = new UnityActionInvoker(_container);
return controller;
}
Finally in my custom ActionInvoker, I attempt to buildup actions being invoked using the ActionInvokers container:
protected override ActionExecutedContext InvokeActionMethodWithFilters(
ControllerContext controllerContext,
IList<IActionFilter> filters,
ActionDescriptor actionDescriptor,
IDictionary<string, object> parameters)
{
var builtUpFilters = new List<IActionFilter>();
foreach (IActionFilter actionFilter in filters)
{
builtUpFilters.Add(_container.BuildUp<IActionFilter>(actionFilter));
}
return base.InvokeActionMethodWithFilters(controllerContext, builtUpFilters, actionDescriptor, parameters);
}
Here is an example of one of the ActionFilters that is being built up:
public class PopulatRolesAttribute : ActionFilterAttribute, IActionFilter
{
private const string RolesKey = "roles";
[Dependency]
public Func<IMetadataService> Service { get; set; }
public PopulatRolesAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.Controller.ViewData[RolesKey] == null)
{
filterContext.Controller.ViewData[RolesKey] = Service().GetRoles();
}
}
}
The problem is that the public property on my custom ActionFilterAttribute is never injected with anything, it remains null on execution! I cannot see why my filter is not being correctly builtup by the container. The type being injected is registered properly, like so:
container.RegisterInstance(new ChannelFactory<IMetadataService>(
new BasicHttpBinding(),
new EndpointAddress("http://example.com/ABSApplication/MetadataService.svc")));
container.RegisterInstance<Func<IMetadataService>>(
() => container.Resolve<ChannelFactory<IMetadataService>>().CreateChannel());
And is also being injected elsewhere in the application (Although not via .Buildup). This is pretty much the same process followed by this blog post. What piece of the puzzle am I missing?
I would do this slightly differently. I would:
install the unity.mvc3 nuget package
call the bootstrapped.initialise() as mentioned in the txt doc the package adds to the project
define your IMetadataService mapping in the initialize to your Concrete type
add IMetadataService to your constructor
The difference between your implementation and the article you references is you use Func, which Im not sure if that adds a different problem to the mix here. I have to imagine it does as the above method (without Func) works fine for me.
Edit:
Brad Wilson's code worked just fine for me here:
http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html
Applicable parts (please see the link above)
Global.asax.cs
protected void Application_Start() {
// ...
var oldProvider = FilterProviders.Providers.Single(
f => f is FilterAttributeFilterProvider
);
FilterProviders.Providers.Remove(oldProvider);
var container = new UnityContainer();
var provider = new UnityFilterAttributeFilterProvider(container);
FilterProviders.Providers.Add(provider);
// ...
}
The filter itself:
using System;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
public class InjectedFilterAttribute : ActionFilterAttribute {
[Dependency]
public IMathService MathService { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext) {
filterContext.HttpContext.Response.Write(
String.Format("The filter says 2 + 3 is {0}.",
MathService.Add(2, 3))
);
}
}
and UnityFilterAttributeFilterProvider.cs
using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider {
private IUnityContainer _container;
public UnityFilterAttributeFilterProvider(IUnityContainer container) {
_container = container;
}
protected override IEnumerable GetControllerAttributes(
ControllerContext controllerContext,
ActionDescriptor actionDescriptor) {
var attributes = base.GetControllerAttributes(controllerContext,
actionDescriptor);
foreach (var attribute in attributes) {
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
protected override IEnumerable GetActionAttributes(
ControllerContext controllerContext,
ActionDescriptor actionDescriptor) {
var attributes = base.GetActionAttributes(controllerContext,
actionDescriptor);
foreach (var attribute in attributes) {
_container.BuildUp(attribute.GetType(), attribute);
}
return attributes;
}
}

Categories

Resources