Inject some service in custom DisplayFormatAttribute - c#

Hopefully this is not a duplicate!
I am using Autofac.
I am struggling to inject some services with in my custom attribute called XNameDisplayFormatAttribute which inherits from DisplayFormatAttribute. The idea is that I want to get some data from database relating to current culture info for logged in user. For that I created a SettingsService but the problem is that I can not inject it in XNameDisplayFormatAttribute. I read there is not way how to inject services in attributes(not filters) but there are workarounds for that(Not quite sure how!). My custom attribute will look like this:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class XNameDisplayFormatAttribute : DisplayFormatAttribute
{
public ISettingsService SettingsService {get;set;}
public XNameDisplayFormatAttribute (bool applyFormatInEditMode = true)
{
DataFormatString = SettingsService.GetDataFromDb();"*My format retrieved from db*";
ApplyFormatInEditMode = applyFormatInEditMode;
}
}
This is not working normally but I need something similar to this logic in order to perform get action from database.
Does anybody have any idea how this can be achievable ?
A good idea is not messing around with filters if possible, I want to keep the code clear with only appropriate attributes.
EDIT
I have used the code below as well but I get null value only in attributes:
ISettingsService settings = DependencyResolver.Current.GetService<ISettingsService>();
Here is my configuration section:
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
RegisterStartUp.RegisterDependecies(builder, app);
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//Registering model binders
builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();
//builder.RegisterSource(new ViewRegistrationSource());
builder.RegisterFilterProvider();
// BUILD THE CONTAINER
var container = builder.Build();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// REGISTER WITH OWIN
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
ConfigureAuth(app);
app.MapSignalR();
}

I think your design has some flaws.
You are using an attribute to implement business logic, but an attribute should normally contain only metadata, and for a good reason: the attribute is embedded into the assembly in a peculiar way, and processed differently from the rest of the classes.
The root problem of your idea is that an attribute is really meant to store (and eventually operate) on data known at compile time. An attribute is not (and, AFAICT, will never be) instantiated like the rest of the classes, so deendency injection does not apply.
What I would try: implement a class DisplayFormatManager and register it via Autofac as a singleton. This class is responsible for calling on the SettingsService and retrieving what you need.
On application startup, right after Autofac setup, you inject the DisplayFormatManager onto a static property of the XNameDisplayFormatAttribute class. Then hope for the best: I don't know WHEN the attribute value is going to be used.
I would search for a different path, however: if the configuration is dynamic, it should be handled via code, not via metadata, IMO. Your mileage may vary, a lot.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class XNameDisplayFormatAttribute : DisplayFormatAttribute
{
public static DisplayFormatManager FormatsManager { get; set; }
public XNameDisplayFormatAttribute(bool applyFormatInEditMode = true)
{
DataFormatString = FormatsManager.GetDataFromDb(_dataFormatString);
ApplyFormatInEditMode = applyFormatInEditMode;
}
private string _dataFormatString;
public new string DataFormatString
{
get => FormatsManager.GetDataFromDb(_dataFormatString);
set => _dataFormatString = value;
}
}
public class DisplayFormatManager
{
public DisplayFormatManager(Func<ISettingsService> settingsServiceFactory)
{
SettingsServiceFactory = settingsServiceFactory;
}
public Func<ISettingsService> SettingsServiceFactory { get; }
public string GetDataFromDb(string format)
{
var service = SettingsServiceFactory();
return service.GetDataFromDb(format);
}
}
public interface ISettingsService
{
string GetDataFromDb(string format);
}

You could inject service into an action filter
http://docs.autofac.org/en/latest/integration/mvc.html#enable-property-injection-for-action-filters

Related

Dependency Injection in Model classes (entities)

I am building an ASP.NET Core MVC application with Entity Framework Code-First.
I implemented a simple repository pattern, providing basic CRUD operations for all the model classes I have created.
I chose to follow all the recommendations provided in docs and DI is one of these.
In ~~.NET 5~~ (6 years later update: .net 5 was the alpha name of .net core 1.0) dependency injection works very well for any class that we do not directly instantiate (e.g.: controllers, data repositories, ...).
We simply inject them via the constructor, and register the mappings in the Startup class of the application :
// Some repository class
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
}
// In startup.cs :
services.AddScoped<IMyDependency, MyDependency>();
services.AddScoped<IMyRepository, MyRepository>();
The problem is that in some of my model classes, I would like to inject some of the dependencies I have declared.
But I think that I cannot use the constructor injection pattern because model classes are often explicitly instantiated. Therefore, I would need to provide myself with the dependencies, which I can't.
So my question is: is there another way than constructor injection to inject dependencies, and how? I was for example thinking of an attribute pattern or something like that.
As I already explained in a comment, when creating an object using new, there is nothing from the dependency injection framework that is involved in the process. As such, it’s impossible for the DI framework to magically inject things into that object, it simply doesn’t know about it.
Since it does not make any sense to let the DI framework create your model instances (models are not a dependency), you will have to pass in your dependencies explicitly if you want the model to have them. How you do that depends a bit on what your models are used for, and what those dependencies are.
The simple and clear case would be to just have your model expect the dependencies on the constructor. That way, it is a compile time error if you do not provide them, and the model has access to them right away. As such, whatever is above, creating the models, is required to have the dependencies the model type needs. But at that level, it’s likely that this is a service or a controller which has access to DI and can request the dependency itself.
Of course, depending on the number of dependencies, this might become a bit complicated as you need to pass them all to the constructor. So one alternative would be to have some “model factory” that takes care of creating the model object. Another alternative would also be to use the service locator pattern, passing the IServiceCollection to the model which can then request whatever dependencies it needs. Note that is generally a bad practice and not really inversion of control anymore.
Both these ideas have the issue that they modify the way the object is created. And some models, especially those handled by Entity Framework, need an empty constructor in order for EF to be able to create the object. So at that point you will probably end up with some cases where the dependencies of your model are not resolved (and you have no easy way of telling).
A generally better way, which is also a lot more explicit, would be to pass in the dependency where you need it, e.g. if you have some method on the model that calculates some stuff but requires some configuration, let the method require that configuration. This also makes the methods easier to test.
Another solution would be to move the logic out of the model. For example the ASP.NET Identity models are really dumb. They don’t do anything. All the logic is done in the UserStore which is a service and as such can have service dependencies.
The pattern often used in domain driven design (rich domain model to be specific) is to pass the required services into the method you are calling.
For example if you want to calculate the vat, you'd pass the vat service into the CalculateVat method.
In your model
public void CalculateVat(IVatCalculator vatCalc)
{
if(vatCalc == null)
throw new ArgumentNullException(nameof(vatCalc));
decimal vatAmount = vatcalc.Calculate(this.TotalNetPrice, this.Country);
this.VatAmount = new Currency(vatAmount, this.CurrencySymbol);
}
Your service class
// where vatCalculator is an implementation IVatCalculator
order.CalculateVat(vatCalculator);
Finally your service can inject another services, like a repository which will fetch the tax rate for a certain country
public class VatCalculator : IVatCalculator
{
private readonly IVatRepository vatRepository;
public VatCalculator(IVatRepository vatRepository)
{
if(vatRepository == null)
throw new ArgumentNullException(nameof(vatRepository));
this.vatRepository = vatRepository;
}
public decimal Calculate(decimal value, Country country)
{
decimal vatRate = vatRepository.GetVatRateForCountry(country);
return vatAmount = value * vatRate;
}
}
I know my answer is late and may not exactly what you're asking for, but I wanted to share how I do it.
First of all: If you want to have a static class that resolves your dependencies this is a ServiceLocator and it's Antipattern so try not to use it as you can.
In my case I needed it to call MediatR inside of my DomainModel to implement the DomainEvents logic.
Anyway, I had to find a way to call a static class in my DomainModel to get an instance of some registered service from DI.
So I've decided to use the HttpContext to access the IServiceProvider but I needed to access it from a static method without mention it in my domain model.
Let's do it:
1- I've created an interface to wrap the IServiceProvider
public interface IServiceProviderProxy
{
T GetService<T>();
IEnumerable<T> GetServices<T>();
object GetService(Type type);
IEnumerable<object> GetServices(Type type);
}
2- Then I've created a static class to be my ServiceLocator access point
public static class ServiceLocator
{
private static IServiceProviderProxy diProxy;
public static IServiceProviderProxy ServiceProvider => diProxy ?? throw new Exception("You should Initialize the ServiceProvider before using it.");
public static void Initialize(IServiceProviderProxy proxy)
{
diProxy = proxy;
}
}
3- I've created an implementation for the IServiceProviderProxy which use internally the IHttpContextAccessor
public class HttpContextServiceProviderProxy : IServiceProviderProxy
{
private readonly IHttpContextAccessor contextAccessor;
public HttpContextServiceProviderProxy(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public T GetService<T>()
{
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
public IEnumerable<T> GetServices<T>()
{
return contextAccessor.HttpContext.RequestServices.GetServices<T>();
}
public object GetService(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetService(type);
}
public IEnumerable<object> GetServices(Type type)
{
return contextAccessor.HttpContext.RequestServices.GetServices(type);
}
}
4- I should register the IServiceProviderProxy in the DI like this
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<IServiceProviderProxy, HttpContextServiceProviderProxy>();
.......
}
5- Final step is to initialize the ServiceLocator with an instance of IServiceProviderProxy at the Application startup
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IServiceProvider sp)
{
ServiceLocator.Initialize(sp.GetService<IServiceProviderProxy>());
}
As a result now you can call the ServiceLocator in your DomainModel classes "Or and needed place" and resolve the dependencies that you need.
public class FakeModel
{
public FakeModel(Guid id, string value)
{
Id = id;
Value = value;
}
public Guid Id { get; }
public string Value { get; private set; }
public async Task UpdateAsync(string value)
{
Value = value;
var mediator = ServiceLocator.ServiceProvider.GetService<IMediator>();
await mediator.Send(new FakeModelUpdated(this));
}
}
The built-in model binders complain that they cannot find a default ctor. Therefore you need a custom one.
You may find a solution to a similar problem here, which inspects the registered services in order to create the model.
It is important to note that the snippets below provide slightly different functionality which, hopefully, satisfies your particular needs. The code below expects models with ctor injections. Of course, these models have the usual properties you might have defined. These properties are filled in exactly as expected, so the bonus is the correct behavior when binding models with ctor injections.
public class DiModelBinder : ComplexTypeModelBinder
{
public DiModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders)
{
}
/// <summary>
/// Creates the model with one (or more) injected service(s).
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
protected override object CreateModel(ModelBindingContext bindingContext)
{
var services = bindingContext.HttpContext.RequestServices;
var modelType = bindingContext.ModelType;
var ctors = modelType.GetConstructors();
foreach (var ctor in ctors)
{
var paramTypes = ctor.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = paramTypes.Select(p => services.GetService(p)).ToArray();
if (parameters.All(p => p != null))
{
var model = ctor.Invoke(parameters);
return model;
}
}
return null;
}
}
This binder will be provided by:
public class DiModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType)
{
var propertyBinders = context.Metadata.Properties.ToDictionary(property => property, context.CreateBinder);
return new DiModelBinder(propertyBinders);
}
return null;
}
}
Here's how the binder would be registered:
services.AddMvc().AddMvcOptions(options =>
{
// replace ComplexTypeModelBinderProvider with its descendent - IoCModelBinderProvider
var provider = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
var binderIndex = options.ModelBinderProviders.IndexOf(provider);
options.ModelBinderProviders.Remove(provider);
options.ModelBinderProviders.Insert(binderIndex, new DiModelBinderProvider());
});
I'm not quite sure if the new binder must be registered exactly at the same index, you can experiment with this.
And, at the end, this is how you can use it:
public class MyModel
{
private readonly IMyRepository repo;
public MyModel(IMyRepository repo)
{
this.repo = repo;
}
... do whatever you want with your repo
public string AProperty { get; set; }
... other properties here
}
Model class is created by the binder which supplies the (already registered) service, and the rest of the model binders provide the property values from their usual sources.
HTH
Is there another way than constructor injection to inject dependencies, and how?
The answer is "no", this cannot be done with "dependency injection". But, "yes" you can use the "service locator pattern" to achieve your end-goal.
You can use the code below to resolve a dependency without the use of constructor injection or the FromServices attribute. Additionally you can new up an instance of the class as you see fit and it will still work -- assuming that you have added the dependency in the Startup.cs.
public class MyRepository : IMyRepository
{
public IMyDependency { get; } =
CallContextServiceLocator.Locator
.ServiceProvider
.GetRequiredService<IMyDependency>();
}
The CallContextServiceLocator.Locator.ServiceProvider is the global service provider, where everything lives. It is not really advised to use this. But if you have no other choice you can. It would be recommended to instead use DI all the way and never manually instantiate an object, i.e.; avoid new.
I'm simply adding some supplemental information here to the answers provided that can help.
IServiceProvider was provided in the accepted answer, but not the important IServiceProvider.CreateScope() method. You can use it to create scopes as necessary that you added through ConfigureServices.
I'm not sure if IServiceProvider is actually a Service Locator pattern behind the scenes or not, but it's how you create scopes as far as I know. At least in the case if it is a Service Locator pattern, it's the official one for today in .NET, and so it's not compounded by the problems of writing your own Service Locator, which I also agree is anti-pattern.
Example, Startup.cs/ConfigureServices and Configure:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("Databases").GetSection("SomeDb")["ConnectionString"]);
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}, ServiceLifetime.Scoped);
services.AddMvcCore().AddNewtonsoftJson();
services.AddControllersWithViews();
}
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)
{
...
IServiceScope scope = provider.CreateScope();
SomeDbContext context = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
SomeModelProxyClass example = new SomeModelProxyClass(context);
await example.BuildDefaults(
Configuration.GetSection("ProfileDefaults").GetSection("Something"),
Configuration.GetSection("ProfileDefaults").GetSection("SomethingSomething"));
scope.Dispose();
}
The above is for doing some default interactions on Startup, maybe if you need to build some default records in your database on a first usage, just as an example.
Ok so let's get to your repository and dependency though, will they work?
Yep!
Here's a test in my own CRUD project, I made a simple minimalist implementation of your IMyDependency and IMyRepository like so, then added them scoped as you did to Startup/ConfigureServices:
public interface IMyRepository
{
string WriteMessage(string input);
}
public interface IMyDependency
{
string GetTimeStamp();
}
public class MyDependency : IMyDependency
{
public MyDependency()
{
}
public string GetTimeStamp()
{
return DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
}
}
public class MyRepository : IMyRepository
{
private readonly IMyDependency _myDependency;
public MyRepository(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public string WriteMessage(string input)
{
return input + " - " + _myDependency.GetTimeStamp();
}
}
Here ContextCRUD is a Model class from my own project not derived from Scaffold-DbContext tooling like my other database classes, it's a container of logic from those scaffold Model classes, and so I put it in the namespace Models.ProxyModels to hold its own business logic for doing CRUD operations so that the Controllers are not gummed up with logic that should be in the Model:
public ContextCRUD(DbContext context, IServiceProvider provider)
{
Context = context;
Provider = provider;
var scope = provider.CreateScope();
var dep1 = scope.ServiceProvider.GetService<IMyRepository>();
string msg = dep1.WriteMessage("Current Time:");
scope.Dispose();
}
Debugging I get back the expected results in msg, so it all checks out.
The calling code from the Controller for reference, just so you can see how IServiceProvider is passed from upstream by constructor injection in the Controller:
[Route("api/[controller]")]
public class GenericController<T> : Controller where T: DbContext
{
T Context { get; set; }
ContextCRUD CRUD { get; set; }
IConfiguration Configuration { get; set; }
public GenericController(T context, IConfiguration configuration, IServiceProvider provider)
{
Context = context;
CRUD = new ContextCRUD(context, provider);
Configuration = configuration;
}
...
You can do it, check out [InjectionMethod] and container.BuildUp(instance);
Example:
Typical DI constructor (NOT NEEDED IF YOU USE InjectionMethod) public
ClassConstructor(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }
This attribute causes this method to be called to setup DI.
[InjectionMethod] public void Initialize(DeviceHead pDeviceHead) {
this.DeviceHead = pDeviceHead; }

Inject Dependency in View Location Expander

I have implemented a Custom ViewLocationExpander in a vnext project. I want to read a app setting value from the appsettings.json file in the ViewLocationExpander and hence the IOptions<> has been injected into the custom ViewLocationExpander 's constructor. However, while adding the custom ViewLocationExpander to the RazorViewEngine options an object of the ViewLocationExpander is required, which cannot be created due to the dependency.
Below is the code
public MyViewLocationExpander(IOptions<MyAppSettings> MyAppSettings)
{
var appSettings = MyAppSettings.Value;
client = appSettings.Client // client is a private field and is used in ExpandViewLocations function
}
MyAppSettings.cs is as below:
public class MyAppSettings
{
public string Client { get; set; }
}
In Startup.cs ConfigureServices method
services.Configure<RazorViewEngineOptions>(config =>
{
//config.ViewLocationExpanders.Add(new MyViewLocationExpander());
// MyViewLocationExpander cannot be created as it has a dependency on IOptions<MyAppSettings>
});
Any help on how to add the custom ViewLocationExpander to the RazorViewEngineOptions would be great.
One way to resolve services from the container is to resolve them in ExpandViewsMethod
using Microsoft.Extensions.DependencyInjection;
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
var service = context.ActionContext.HttpContext.RequestServices.GetService<IService>();
}

Dependency Injection in PostSharp

I've just read the docs on PostSharp.net about Importing Dependencies from the Target Object and need some clarification from WCF service perspective.
This is my trimmed cache aspect in which I'm trying to use ICache via Unity:
[Serializable]
public class CacheAspect : OnMethodBoundaryAspect, IInstanceScopedAspect
{
[IntroduceMember(Visibility = Visibility.Family, OverrideAction = MemberOverrideAction.Ignore)]
[CopyCustomAttributes(typeof(ImportAttribute))]
[Import(typeof(ICache))]
public ICache Cache { get; set; }
[ImportMember("Cache", IsRequired = true)]
public Property<ICache> CacheProperty;
public override void OnEntry(MethodExecutionArgs args)
{
var cache = this.CacheProperty.Get();
}
object IInstanceScopedAspect.CreateInstance(AdviceArgs adviceArgs)
{
return this.MemberwiseClone();
}
void IInstanceScopedAspect.RuntimeInitializeInstance()
{
var container = new UnityContainer();
container.LoadConfiguration();
var distributedCache = container.Resolve<DistributedCache>();
this.CacheProperty.Set(distributedCache);
}
}
My issue is with the RuntimeInitializeInstance method.
I'd like to know if setting the CacheProperty in this method is the correct approach or should I be doing it differently ?
Initializing the ICache dependency in the [RuntimeInitializeInstance] method is one of the correct approaches, but the provided implementation is not efficient, because you create and configure a new container instance every time.
Usually, it's more convenient to let the DI container to resolve the dependencies for you instead of setting them manually.
The [IntroduceMember] attribute tells PostSharp to add the Cache property directly to your service class. When resolving the service instance during run-time, Unity container can set this Cache property for you automatically.
You can tell Unity to set the property value by annotating it with the [Dependency] attribute (Annotating Objects for Property (Setter) Injection). For this attribute to be copied to your service class you also need to apply the [CopyCustomAttributes] attribute.
[IntroduceMember(Visibility = Visibility.Family, OverrideAction = MemberOverrideAction.Ignore)]
[CopyCustomAttributes(typeof(DependencyAttribute))]
[Dependency]
public ICache Cache { get; set; }
The attributes in your example were copied from the documentation and demonstrate the same principle for the MEF container.

Autofac property injection into System.ComponentModel.DataAnnotations ValidationAttribute

I am not able to perform property injection into a custom data annotation validation attribute
public class CustomValidationAttribute : ValidationAttribute
{
public ILogger Logger { get; set; }
public CustomValidationAttribute(string keyPointer)
{ }
public override bool IsValid(object value)
{
// Implementation here
return true;
}
}
Now, on my MVC Application_Start method I have the following Autofac configuration:
// Autofac Ioc Container
var builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>().InstancePerHttpRequest();
builder.RegisterType<CustomValidationAttribute>()
.OnActivating(e =>
{
e.Instance.Logger = e.Context.Resolve<ILogger>();
});
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
I have also tried the autowiring capabilities:
builder.RegisterType<CustomValidationAttribute>().PropertiesAutowired();
I am guessing that the properties of an attribute on data annotations are resolved at compile time and are immune to runtime injection.
This methods works fine for MVC filter attributes but does not work for data annotation attributes.
Any help is really appreciated on alternate methods to make this work.
For reference, we had a problem with a ValidationAttribute which needed to do some database work using a Repository, which in turn used an Entity Framework DbContext.
Our problem was that the DbContext was being cached by the attribute. This led to the data it contained being stale which affected the result of the validation!
We fixed it by doing our Repository resolve inside an Autofac Lifetimescope declaration inside the IsValid method:
using Autofac;
...
public override bool IsValid(object value)
{
using (var lifetimeScope = MvcApplication.Container.BeginLifetimeScope())
{
var repo = lifetimeScope.Resolve<IMyRepo>();
// Do your validation checks which require the repo here
} // The repo will be released / disposed here
}
Just adding my solution here as I haven't found this solution documented for this problem anywhere else - perhaps it's just so obvious nobody else has been as dumb as me :)
You are correct in your analysis - Autofac has a mechanism to inject into Filter Attributes [which is achieved by not instantiating them as attributes and leans on facilities exposed MVC 3].
There is no such natural extension point applicable to Validation Attributes, and Autofac doesn't make any attempt to do so.
I ended up using a new validator and some reflection to set an instance of the property in the data annotation.
public class IocValidator : DataAnnotationsModelValidator<ValidationAttribute>
{
public IocValidator(ModelMetadata metadata, ControllerContext context,
ValidationAttribute attribute)
: base(metadata, context, attribute) { }
public override IEnumerable<ModelValidationResult> Validate(object container)
{
IEnumerable<PropertyInfo> props =
from p in Attribute.GetType().GetProperties()
where p.CanRead
&& p.CanWrite
&& (p.PropertyType.IsInterface || p.PropertyType.IsAbstract)
select p;
foreach (PropertyInfo prop in props)
{
var instance = IocHelper.Resolver.GetService(prop.PropertyType);
if (instance != null)
prop.SetValue(Attribute, instance, null);
}
return base.Validate(container);
}
}
Then in my Application_Start I registered my new validator adapter as such:-
DataAnnotationsModelValidatorProvider.RegisterDefaultAdapter(typeof(IocValidator));
There are definite performance implications on this approach and the dependence of the IocHelper in the Validator (ohh, the irony, dependency on the dependency injection container).
Any thoughts or better approaches are quite welcomed.
I solved it a bit differently (you're still wiring stuff via ValidationAttributes) in this answer, enabling one to write:
class MyModel
{
...
[Required, StringLength(42)]
[ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")]
public string MyProperty { get; set; }
....
}
public class MyDiDependentValidator : Validator<MyModel>
{
readonly IUnitOfWork _iLoveWrappingStuff;
public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff)
{
_iLoveWrappingStuff = iLoveWrappingStuff;
}
protected override bool IsValid(MyModel instance, object value)
{
var attempted = (string)value;
return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted);
}
}
With some helper classes (look over there), you wire it up e.g. in ASP.NET MVC like so in the Global.asax :-
DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
typeof(ValidatorServiceAttribute),
(metadata, context, attribute) =>
new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));

Database injection into a validation attribute with ASP MVC and Castle Windsor

I need some help - I am trying to use a custom validation attribute in an ASP.NET MVC web project that needs to make a database call.
I have windsor successfully working for the controllers and the IRepository interface is injected normally. The problem arrises when I need to inject the repository into the attribute class.
The attribute class has the following code:
public class ValidateUniqueUrlNodeAttribute : AbstractValidationAttribute
{
private readonly string message;
private readonly IArticleRepository articleRepository;
public ValidateUniqueUrlNodeAttribute(string message)
{
this.message = message;
}
public ValidateUniqueUrlNodeAttribute(string message, IArticleRepository articleRepository):this(message)
{
this.articleRepository = articleRepository;
}
public override IValidator Build()
{
var validator = new UniqueUrlNodeValidator(articleRepository) { ErrorMessage = message };
ConfigureValidatorMessage(validator);
return validator;
}
My problem is that I cannot seem to make Windsor intercept the contruction of the attribute to pass in the IArticleRepository
The current code in my global.asax file is as follows:
container = new WindsorContainer();
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(Container));
container
.RegisterControllers(Assembly.GetExecutingAssembly())
.AddComponent<IArticleRepository, ArticleRepository>()
.AddComponent<ValidateUniqueUrlNodeAttribute>();
Any help would be greatly appreciated.
AFAIK no dependency injection container can directly manage an attribute, since it's instantiated by the runtime and there's no way to intercept that.
However, they can cheat by either:
Using a static gateway to the container (example), or
Using a "BuildUp" feature that injects whatever dependencies are found within an already-constructed object. This is called BuildUp in Unity or InjectProperties in Autofac.
Windsor doesn't support #2 (ref1, ref2), so you can either:
Try one of the hacks to make Windsor support #2 (hack1, hack2)
Use a static gateway
Implement your own IValidatorBuilder and make it use Windsor to create validators. I'm sure this is implemented somewhere but I can't find it right now...
Don't know if this helps, but I subclassed ValidationAttribute to expose a Resolve<T>() method like so:
public abstract class IocValidationAttribute : ValidationAttribute
{
protected T Resolve<T>()
{
return IocHelper.Container().Resolve<T>();
}
}
Then it can be used in any custom ValidatorAttribute that needs to hit a database:
public class UniqueEmailAttribute : IocValidationAttribute
{
public override bool IsValid(object value)
{
ICustomerRepository customerRepository = Resolve<ICustomerRepository>();
return customerRepository.FindByEmail(value.ToString()) == null;
}
}
I think it's a variation of the 'Static Gateway' approach mentioned by Mauricio Scheffer. I don't know if this is a good design or not. I'm not a huge fan of it, I'd rather the dependency was injected more 'elegantly', though I can't use constructor injection obviously, I'd like to use Property injection but can't work out a way to hook into the ASP.NET MVC framework code to do this (I've even pored though the MVC2 source code).
I was able to wire it up [using Autofac as it happens, but it's just constructor injection via the ASP.NET MVC DependencyResolver] in this answer, enabling one to write:
class MyModel
{
...
[Required, StringLength(42)]
[ValidatorService(typeof(MyDiDependentValidator), ErrorMessage = "It's simply unacceptable")]
public string MyProperty { get; set; }
....
}
public class MyDiDependentValidator : Validator<MyModel>
{
readonly IUnitOfWork _iLoveWrappingStuff;
public MyDiDependentValidator(IUnitOfWork iLoveWrappingStuff)
{
_iLoveWrappingStuff = iLoveWrappingStuff;
}
protected override bool IsValid(MyModel instance, object value)
{
var attempted = (string)value;
return _iLoveWrappingStuff.SaysCanHazCheez(instance, attempted);
}
}
With some helper classes (look over there), you wire it up e.g. in ASP.NET MVC like so in the Global.asax :-
DataAnnotationsModelValidatorProvider.RegisterAdapterFactory(
typeof(ValidatorServiceAttribute),
(metadata, context, attribute) =>
new DataAnnotationsModelValidatorEx(metadata, context, attribute, true));
Hmm.
Can you test the effect of removing the (string message) ctor, and see if that at least forces Castle to use the ctor with the Repostiory ?
Otherwise we call AddComponent(name, type, type). Other than that it really should work...
Also does this hint at my first idea ? How do I use Windsor to inject dependencies into ActionFilterAttributes

Categories