Using Unity's dependency injection in (data) annotations - c#

I am using Unity and have a Model tagged with data annotations:
public class SomeModel
{
[SlackDisplayName("ED0CAD76-263E-496F-ABB1-A4DFE6DEC5C2")]
public String SomeProperty { get; set; }
}
This SlackDisplayName property is a child class of DisplayName, which resolves a static Display Name for the property. I just wanted to make that dynamically by having this criteria fulfilled:
The use of this annotation is possible.
I can implement multilingual applications using that annotation.
Language templates get identified by a GUID
I must not pass a culture id to the annotation
So furthermore, my SlackDisplayName annotation looks like this:
/// <summary>
/// Annotation for non-fixed display names
/// </summary>
public class SlackDisplayNameAttribute : DisplayNameAttribute
{
/// <summary>
/// TODO
/// </summary>
/// <param name="identifierGUID"></param>
public SlackDisplayNameAttribute(String identifierGUID)
: this(Guid.Parse(identifierGUID))
{
}
/// <summary>
/// TODO
/// </summary>
/// <param name="identifier"></param>
public SlackDisplayNameAttribute(Guid identifier)
: base()
{
}
/// <summary>
/// The culture context to use.
/// </summary>
[Dependency]
public ICultureContext Context { get; set; }
/// <summary>
/// Gets the display name for the given GUID.
/// </summary>
public override string DisplayName
{
get
{
return "NOT_DEFINED";
//return Context.GetLanguageTemplate(new Guid());
}
}
}
And now the question is: How to get the ICultureContext from my Unity Container:
[Dependency]
public ICultureContext Context { get; set; }
It is registered, but I have no clue about how to get that property injected.

I have solved it myself!
First of all, you need the following Unity Extension and Strategy:
Info: Found here: UnityContainer.BuildUp() - Can I make it inject new instances into properties only if these are null?
public class RecursiveBuildUpContainerExtension : UnityContainerExtension {
protected override void Initialize(){
Context.Strategies.Add( new RecursiveBuildUpBuilderStrategy( Context.Container ), UnityBuildStage.PreCreation );
}
}
public class RecursiveBuildUpBuilderStrategy : BuilderStrategy {
readonly IUnityContainer container;
public RecursiveBuildUpBuilderStrategy( IUnityContainer container ) {
this.container = container;
}
public override void PreBuildUp( IBuilderContext context ) {
if( context.Existing == null ) return;
foreach( var prop in context.Existing.GetType( ).GetProperties( ) ) {
if( ContainsType<DependencyAttribute>( prop.GetCustomAttributes( true ) ) ) {
if( prop.GetValue( context.Existing, null ) == null ) {
var value = container.Resolve( prop.PropertyType );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
else {
var value = container.BuildUp( prop.PropertyType, prop.GetValue( context.Existing, null ) );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
}
}
foreach (var method in context.Existing.GetType().GetMethods() ){
if( ContainsType<InjectionMethodAttribute>( method.GetCustomAttributes( true ))){
var argsInfo = method.GetParameters( );
var args = new object[argsInfo.Length];
for( int i = 0; i < argsInfo.Length; i++ ) {
args[i] = container.Resolve( argsInfo[i].ParameterType );
}
method.Invoke( context.Existing, args );
}
}
context.BuildComplete = true;
}
private static bool ContainsType<T>( IEnumerable<object> objects ){
foreach (var o in objects){
if( o is T ) return true;
}
return false;
}
}
You need this, because it is responsible for injecting the properties on "BuildUp". Next to this, you need to register your extension
container.AddNewExtension<RecursiveBuildUpContainerExtension>();
Furthermore, you need to override the default DataAnnotationsModelMetadataProvider, because the default ModelMetaDataProvider does not use Unity to inject properties to annotations. To do this, implement this class:
public class DynamicModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private IUnityContainer _context;
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
foreach (Attribute attribute in attributes)
_context.BuildUp(attribute);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
public DynamicModelMetadataProvider(IUnityContainer context)
: base()
{
this._context = context;
}
}
After that, edit your bootstrapper and set the new ModelMetadataProvider, to make it clear to the MVC Framework that it has to use it:
ModelMetadataProviders.Current = new DynamicModelMetadataProvider(container);
Where container is your set up IUnityContainer. You should now have instances in your Annotations Instance when having set the [DependencyAttribute] and your Methods marked with the [InjectionMethod] should get called.
[Dependency]
public ICultureContext Context { get; set; }
Hope you could use this if you had a similar problem ;)

Related

Asp.Net Core Rules Engine with Programmatic Dependency Injection - Constructor on type 'type' not found

I'v developed a Rules Engine library, called RulesChain, that works perfectly when the rules doesn't need that any dependency to be injected.
The primary goal with this library is to simplify writing business rules in .NET environment based on Rules Design Pattern and Chain of Responsability Pattern to work like ASP.Net Core middlewares works.
When I need to have any dependency injected I get this error:
System.MissingMethodException: 'Constructor on type 'AspNetCoreRulesChainSample.Rules.ShoppingCartRules.IsValidCupomRule' not found.'
What is the problem?
My abstract Rule class needs to receive the next rule to be called on it's constructor. But I can't add the put an specific Rule on constructor because the chain is resolved on RuleChain class
How it Works?
Basically all rules have a ShouldRun method that defines if the run method should be called a Run method that applies the Business Rule. And the Invoke method that is called inside the rule when it needs to call the next rule.
This is the rule with dependency injection that throws an error:
public class IsValidCupomRule : Rule<ApplyDiscountContext>
{
private ISalesRepository _salesRepository;
public IsValidCupomRule(Rule<ApplyDiscountContext> next, ISalesRepository salesRepository) : base(next)
{
_salesRepository = salesRepository;
}
public override ApplyDiscountContext Run(ApplyDiscountContext context)
{
// Gets 7% of discount;
var myDiscount = context.Context.Items.Sum(i => i.Price * 0.07M);
context = _next.Invoke(context) ?? context;
// Only apply first order disccount if the discount applied by the other rules are smaller than this
if (myDiscount > context.DiscountApplied)
{
context.DiscountApplied = myDiscount;
context.DiscountTypeApplied = "Cupom";
}
return context;
}
public override bool ShouldRun(ApplyDiscountContext context)
{
return !string.IsNullOrWhiteSpace(context.Context.CupomCode)
&& context.Context.Items?.Count > 1
&& _salesRepository.IsCupomAvaliable(context.Context.CupomCode);
}
}
A basic rule without dependency is like that.
public class BirthdayDiscountRule : Rule<ApplyDiscountContext>
{
public BirthdayDiscountRule(Rule<ApplyDiscountContext> next) : base(next)
{ }
public override ApplyDiscountContext Run(ApplyDiscountContext context)
{
// Gets 10% of discount;
var birthDayDiscount = context.Context.Items.Sum(i => i.Price * 0.1M);
context = _next.Invoke(context);
// Only apply birthday disccount if the discount applied by the other rules are smaller than this
if (birthDayDiscount > context.DiscountApplied)
{
context.DiscountApplied = birthDayDiscount;
context.DiscountTypeApplied = "Birthday Discount";
}
return context;
}
public override bool ShouldRun(ApplyDiscountContext context)
{
var dayAndMonth = context.ClientBirthday.ToString("ddMM");
var todayDayAndMonth = DateTime.Now.ToString("ddMM");
return dayAndMonth == todayDayAndMonth;
}
}
And the abstract rule is that:
public abstract class Rule<T> : IRule<T>
{
protected readonly Rule<T> _next;
protected Rule(Rule<T> next)
{
_next = next;
}
/// <summary>
/// Valides if the rules should be executed or not
/// </summary>
/// <returns></returns>
public abstract bool ShouldRun(T context);
/// <summary>
/// Executes the rule
/// </summary>
/// <returns></returns>
public abstract T Run(T context);
public virtual T Invoke(T context)
{
if(ShouldRun(context))
return Run(context);
else
return _next != null
? _next.Invoke(context)
: context;
}
}
To create my chain of rules I just need to do this:
public ShoppingCart ApplyDiscountOnShoppingCart(ShoppingCart shoppingCart)
{
// Create the chain
var shoppingCartRuleChain = new RuleChain<ApplyDiscountContext>()
.Use<IsValidCupomRule>()
.Use<BirthdayDiscountRule>()
.Use<FirstOrderDiscountRule>()
.Build();
// Create the chain context
var shoppingCartRuleContext = new ApplyDiscountContext(shoppingCart);
shoppingCartRuleContext.Properties["IsFirstOrder"] = true;
shoppingCartRuleContext.ClientBirthday = DateTime.UtcNow;
// Invoke the RulesChain
shoppingCartRuleContext = shoppingCartRuleChain.Invoke(shoppingCartRuleContext);
// Get data form the Chain result and return a ShoppingCart with new data.
shoppingCart.Discount = shoppingCartRuleContext.DiscountApplied;
shoppingCart.DiscountType = shoppingCartRuleContext.DiscountTypeApplied;
return shoppingCart;
}
The principal point for me here is that I can put any Rule in the .Use<IRule>() call and it permits that the rules does not depends on each other and the chain can be changed without the need of refactoring each rule. I'm doing this on Build() method.
This methos just invert the order of each rule on the chain and creates a new instance of each rule, and adds the last Rule instance as the next Rule of he new Rule.
This is the RuleChain class
public class RuleChain<T> : IRuleChain<T>
{
private readonly IList<Type> _components = new List<Type>();
public IRuleChain<T> Use<TRule>()
{
_components.Add(typeof(TRule));
return this;
}
public IRule<T> Build()
{
IRule<T> app = EndOfChainRule<T>.EndOfChain();
foreach (var component in _components.Reverse())
{
app = (IRule<T>)Activator.CreateInstance(component,app);
}
return app;
}
}
Here is how I instantiate new Rules with the next Rule: app = (IRule<T>)Activator.CreateInstance(component,app);
Other information that may be useful:
This is how I resolve Dependencies on IoC module
public static class Modules
{
public static void AddRepository(this IServiceCollection services)
{
services.AddScoped<ISalesRepository, SalesRepository>();
}
public static void AddRules(this IServiceCollection services)
{
services.AddScoped<IsValidCupomRule>();
services.AddScoped<FirstOrderDiscountRule>();
services.AddScoped<BirthdayDiscountRule>();
services.AddScoped<ShoppingCartRulesChain>();
}
}
My startup.cs Configure is this:
public void ConfigureServices(IServiceCollection services)
{
services.AddRepository();
services.AddRules();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Whats my question?
How can I instantiate a new class based on the same Rule<T> class and with dependencies of IServiceCollection?
The RulesChain source code is available at: https://github.com/lutticoelho/RulesChain
This sample source code is available at: https://github.com/lutticoelho/AspNetCoreRulesChainSample
If anyone needs more information about, or to put more code on the question feel free to ask at the comments and I'll provide any changes needed in this question.
Now there is a lot to unpack here. First observation would be with the RuleChain class.
If the intention is to allow for Dependency Injection via constructor injection then the current design of the class will need to be refactored to allow for that.
Since the current design is modeled behind the Asp.Net Core Middleware pipeline, I would suggest using delegates to store and handle the desired invocation.
First define a delegate to handle the rule processing
/// <summary>
/// A function that can process a <see cref="TContext"/> dependent rule.
/// </summary>
/// <typeparam name="TContext"></typeparam>
/// <param name="context"></param>
/// <returns>A task that represents the completion of rule processing</returns>
public delegate Task RuleHandlingDelegate<TContext>(TContext context);
The advantage of using the delegate is that it can be late bound to an actual implementation after all the necessary dependencies have be resolved.
Also note that this generic delegate definition uses Task to allow for asynchronous operations
This does require a change to the IRuleChain<T> definition.
/// <summary>
/// Defines a class that provides the mechanisms to configure an application's rules pipeline execution.
/// </summary>
/// <typeparam name="TContext">The context shared by all rules in the chain</typeparam>
public interface IRuleChain<TContext> {
/// <summary>
/// Adds a rule to the application's request chain.
/// </summary>
/// <returns>The <see cref="IRuleChain{T}"/>.</returns>
IRuleChain<TContext> Use<TRule>();
/// <summary>
/// Builds the delegate used by this application to process rules executions.
/// </summary>
/// <returns>The rules handling delegate.</returns>
RuleHandlingDelegate<TContext> Build();
}
And the implementation.
In order to allow other arguments to be injected into the rule implementation, the chain will need to be able to resolve constructor argument.
public abstract class RuleChain<TContext> : IRuleChain<TContext> {
private readonly Stack<Func<RuleHandlingDelegate<TContext>, RuleHandlingDelegate<TContext>>> components =
new Stack<Func<RuleHandlingDelegate<TContext>, RuleHandlingDelegate<TContext>>>();
private bool built = false;
public RuleHandlingDelegate<TContext> Build() {
if (built) throw new InvalidOperationException("Chain can only be built once");
var next = new RuleHandlingDelegate<TContext>(context => Task.CompletedTask);
while (components.Any()) {
var component = components.Pop();
next = component(next);
}
built = true;
return next;
}
public IRuleChain<TContext> Use<TRule>() {
components.Push(createDelegate<TRule>);
return this;
}
protected abstract object GetService(Type type, params object[] args);
private RuleHandlingDelegate<TContext> createDelegate<TRule>(RuleHandlingDelegate<TContext> next) {
var ruleType = typeof(TRule);
MethodInfo methodInfo = getValidInvokeMethodInfo(ruleType);
//Constructor parameters
object[] constructorArguments = new object[] { next };
object[] dependencies = getDependencies(ruleType, GetService);
if (dependencies.Any())
constructorArguments = constructorArguments.Concat(dependencies).ToArray();
//Create the rule instance using the constructor arguments (including dependencies)
object rule = GetService(ruleType, constructorArguments);
//return the delegate for the rule
return (RuleHandlingDelegate<TContext>)methodInfo
.CreateDelegate(typeof(RuleHandlingDelegate<TContext>), rule);
}
private MethodInfo getValidInvokeMethodInfo(Type type) {
//Must have public method named Invoke or InvokeAsync.
var methodInfo = type.GetMethod("Invoke") ?? type.GetMethod("InvokeAsync");
if (methodInfo == null)
throw new InvalidOperationException("Missing invoke method");
//This method must: Return a Task.
if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
throw new InvalidOperationException("invalid invoke return type");
//and accept a first parameter of type TContext.
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != 1 || parameters[0].ParameterType != typeof(TContext))
throw new InvalidOperationException("invalid invoke parameter type");
return methodInfo;
}
private object[] getDependencies(Type middlewareType, Func<Type, object[], object> factory) {
var constructors = middlewareType.GetConstructors().Where(c => c.IsPublic).ToArray();
var constructor = constructors.Length == 1 ? constructors[0]
: constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
if (constructor != null) {
var ctorArgsTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray();
return ctorArgsTypes
.Skip(1) //Skipping first argument since it is suppose to be next delegate
.Select(parameter => factory(parameter, null)) //resolve other parameters
.ToArray();
}
return Array.Empty<object>();
}
}
With this abstract chain, it is now the responsibility of it's implementation to define how to resolve any dependencies.
Following the sample context, that is simple enough. Since using the default DI extension, then the chain should depend on IServiceProvider for types whose arguments are not known and Activator for those with provided constructor arguments.
public class DiscountRuleChain : RuleChain<ApplyDiscountContext> {
private readonly IServiceProvider services;
public DiscountRuleChain(IServiceProvider services) {
this.services = services;
}
protected override object GetService(Type type, params object[] args) =>
args == null || args.Length == 0
? services.GetService(type)
: Activator.CreateInstance(type, args);
}
With all of the above provided so far, there were some changes that allowed for a cleaner design.
Specifically IRule<TContext> and its default implementation.
public interface IRule<TContext> {
Task Invoke(TContext context);
}
public abstract class Rule<TContext> : IRule<TContext> {
protected readonly RuleHandlingDelegate<TContext> next;
protected Rule(RuleHandlingDelegate<TContext> next) {
this.next = next;
}
public abstract Task Invoke(TContext context);
}
Any Context specific rules can now be abstracted to target a specific domain
For example
public abstract class DiscountRule : Rule<ApplyDiscountContext> {
protected DiscountRule(RuleHandlingDelegate<ApplyDiscountContext> next) : base(next) {
}
}
This simplified the implementations specific to discounts in the sample and allowed for dependencies to be injected
IsValidCupomRule
public class IsValidCupomRule : DiscountRule {
private readonly ISalesRepository _salesRepository;
public IsValidCupomRule(RuleHandlingDelegate<ApplyDiscountContext> next, ISalesRepository salesRepository)
: base(next) {
_salesRepository = salesRepository;
}
public override async Task Invoke(ApplyDiscountContext context) {
if (cupomAvailable(context)) {
// Gets 7% of discount;
var myDiscount = context.Context.Items.Sum(i => i.Price * 0.07M);
await next.Invoke(context);
// Only apply discount if the discount applied by the other rules are smaller than this
if (myDiscount > context.DiscountApplied) {
context.DiscountApplied = myDiscount;
context.DiscountTypeApplied = "Cupom";
}
} else
await next(context);
}
private bool cupomAvailable(ApplyDiscountContext context) {
return !string.IsNullOrWhiteSpace(context.Context.CupomCode)
&& context.Context.Items?.Count > 1
&& _salesRepository.IsCupomAvaliable(context.Context.CupomCode);
}
}
FirstOrderDiscountRule
public class FirstOrderDiscountRule : DiscountRule {
public FirstOrderDiscountRule(RuleHandlingDelegate<ApplyDiscountContext> next) : base(next) { }
public override async Task Invoke(ApplyDiscountContext context) {
if (shouldRun(context)) {
// Gets 5% of discount;
var myDiscount = context.Context.Items.Sum(i => i.Price * 0.05M);
await next.Invoke(context);
// Only apply discount if the discount applied by the other rules are smaller than this
if (myDiscount > context.DiscountApplied) {
context.DiscountApplied = myDiscount;
context.DiscountTypeApplied = "First Order Discount";
}
} else
await next.Invoke(context);
}
bool shouldRun(ApplyDiscountContext context) {
return (bool)(context.Properties["IsFirstOrder"] ?? false);
}
}
The following test was used to verify expected behavior of the rules engine.
[TestClass]
public class RulesEngineTests {
[TestMethod]
public async Task Should_Apply_Cupom_Discount() {
//Arrange
var cupomCode = "cupomCode";
var services = new ServiceCollection()
.AddSingleton<ISalesRepository>(sp =>
Mock.Of<ISalesRepository>(_ => _.IsCupomAvaliable(cupomCode) == true)
)
.BuildServiceProvider();
// Create the chain
var shoppingCartRuleChain = new DiscountRuleChain(services)
.Use<IsValidCupomRule>()
.Use<FirstOrderDiscountRule>()
.Build();
ShoppingCart shoppingCart = new ShoppingCart {
CupomCode = cupomCode,
Items = new List<ShoppingCartItem> {
new ShoppingCartItem { Price = 10M },
new ShoppingCartItem { Price = 10M },
}
};
var expectedDiscountType = "Cupom";
var expectedDiscountApplied = 1.40M;
// Create the chain context
var shoppingCartRuleContext = new ApplyDiscountContext(shoppingCart);
shoppingCartRuleContext.Properties["IsFirstOrder"] = true;
shoppingCartRuleContext.ClientBirthday = DateTime.UtcNow;
//Act
await shoppingCartRuleChain.Invoke(shoppingCartRuleContext);
// Get data from the context result and verify new data.
shoppingCart.Discount = shoppingCartRuleContext.DiscountApplied;
shoppingCart.DiscountType = shoppingCartRuleContext.DiscountTypeApplied;
//Assert (using FluentAssertions)
shoppingCart.DiscountType.Should().Be(expectedDiscountType);
shoppingCart.Discount.Should().Be(expectedDiscountApplied);
}
}
Note how the dependency to be injected was mocked to test the expected behavior in isolation.

Find all references/declarations of an object at runtime in c# | structuremap

I have a class LanguagePopupMessage which is used all over the application (and external libraries). If this class is constructed it fetches the namespace where it's created and adds a suffix to be unique.
The Question is: How can get all LanguagePopupMessage definitions including the fieldname parameter?
Im using structuremap in my application. It's also scanning all libraries at startup, so maybe there is a possiblity how to automaticate it. 👀
using System;
using System.Diagnostics;
namespace ConsoleApp1
{
/// <summary>
/// Creates the namespace for a popup window and has an additional flag for the caption
/// </summary>
public class LanguagePopupMessage
{
public string Namespace { get; }
public string Caption => $"{Namespace}Caption";
public LanguagePopupMessage(string fieldName)
{
if(string.IsNullOrEmpty(fieldName))
throw new ArgumentNullException(nameof(fieldName));
if (_GetNamespace() is Type type)
{
Namespace = $"{type}.{fieldName}";
}
else
{
throw new InvalidOperationException("could not fetch the namespace");
}
}
private Type _GetNamespace()
{
StackTrace st = new StackTrace();
foreach (var sf in st.GetFrames())
{
var type = sf.GetMethod().DeclaringType;
if (type != GetType())
{
return type;
}
}
return null;
}
public override string ToString()
{
return $"Namespace '{Namespace}' Caption '{Caption}'";
}
}
class Program
{
//ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
//ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded");
static void Main(string[] args)
{
Console.ReadKey();
}
}
}
namespace ConsoleApp1.Subfolder
{
public class SubfolderClass
{
/// <summary>
/// ConsoleApp1.Subfolder.SubfolderClass.FooMessage
/// ConsoleApp1.Subfolder.SubfolderClass.FooMessageCaption
/// </summary>
public static readonly LanguagePopupMessage Message = new LanguagePopupMessage("FooMessage");
}
}
I made a custom IRegistrationConvention - FindAllLanguagePopupMessages for structuremap. During runtime a new container scans all libraries -> all Types if there are any FieldInfo of type LanguagePopupMessage and adding it into a collection.
To get better performance, I made an Attribute - ContainsTranslationDefinition to filter the classes.
Sourcecode
public class ContainsTranslationDefinition : Attribute
{ }
/// <summary>
/// Creates the namespace for a popup window and has an additional flag for the caption
/// </summary>
public class LanguagePopupMessage
{
public string Namespace { get; }
public string Caption => $"{Namespace}Caption";
public LanguagePopupMessage(string fieldName)
{
if(string.IsNullOrEmpty(fieldName))
throw new ArgumentNullException(nameof(fieldName));
if (_GetNamespace() is Type type)
{
Namespace = $"{type}.{fieldName}";
}
else
{
throw new InvalidOperationException("could not fetch the namespace");
}
}
private Type _GetNamespace()
{
StackTrace st = new StackTrace();
foreach (var sf in st.GetFrames())
{
var type = sf.GetMethod().DeclaringType;
if (type != GetType())
{
return type;
}
}
return null;
}
public override string ToString()
{
return $"Namespace '{Namespace}' Caption '{Caption}'";
}
}
/// <summary>
/// Add <see cref="LanguagePopupMessage"/> into the <see cref="Container.Model"/> type lifecycle
/// </summary>
public class FindAllLanguagePopupMessages : IRegistrationConvention
{
private readonly ILifecycle _Lifecycle = new UniquePerRequestLifecycle();
public void ScanTypes(TypeSet types, Registry registry)
{
List<LanguagePopupMessage> dec = new List<LanguagePopupMessage>();
foreach (Type type in types.AllTypes())
{
if (!Attribute.IsDefined(type, typeof(ContainsTranslationDefinition)))
{
continue;
}
_FindConfigDeclarations(type, dec);
}
foreach (LanguagePopupMessage languagePopupMessage in dec)
{
Console.WriteLine($"{languagePopupMessage}");
}
}
private static void _FindConfigDeclarations(Type type, List<LanguagePopupMessage> declarations)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy);
declarations.AddRange(fields
.Where(info => info.IsInitOnly && typeof(LanguagePopupMessage).IsAssignableFrom(info.FieldType))
.Select(info => (LanguagePopupMessage)info.GetValue(null)));
// find all nested class types and run method recursively
foreach (var nestedType in type.GetNestedTypes(BindingFlags.Public))
{
_FindConfigDeclarations(nestedType, declarations);
}
}
}
[ContainsTranslationDefinition]
public class TestClass
{
private static readonly LanguagePopupMessage _CONFIG_1 = new LanguagePopupMessage("ConfigNotLoaded1");
private static readonly LanguagePopupMessage _CONFIG_2 = new LanguagePopupMessage("ConfigNotLoaded2");
}
[ContainsTranslationDefinition]
public class Program
{
//ConsoleApp1.Program.PopupMessage.ConfigNotLoaded
//ConsoleApp1.Program.PopupMessage.ConfigNotLoadedCaption
private static readonly LanguagePopupMessage _CONFIG_NOT_LOADED_POPUP_MESSAGE = new LanguagePopupMessage("ConfigNotLoaded3");
static void Main(string[] args)
{
// Create container and tell where to look for depencies
IContainer container = new Container(c => c.Scan(scanner =>
{
scanner.TheCallingAssembly();
scanner.WithDefaultConventions();
scanner.AssembliesFromApplicationBaseDirectory();
scanner.With(new FindAllLanguagePopupMessages());
}));
Console.ReadKey();
}
}
Preview

ASP.Net Web API - How to unregister custom action filter?

I have developed a custom action filter-LogActionWebApiFilter for Web API 2:
namespace Utility.Logger.WebApi.RequestResponse.Attributes
{
/// <summary>
/// Log action web API action filter
/// </summary>
internal class LogActionWebApiFilter : ActionFilterAttribute
{
#region Private Fields
/// <summary>
/// The current identifier
/// </summary>
private static Guid currentId;
/// <summary>
/// The request start time
/// </summary>
private static DateTime requestStartTime;
/// <summary>
/// The logger
/// </summary>
private IEventLogger logger;
#endregion Private Fields
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LogActionWebApiFilter"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public LogActionWebApiFilter(IEventLogger logger)
{
this.logger = logger;
}
#endregion Public Constructors
#region Public overrided Methods
/// <summary>
/// Occurs after the action method is invoked.
/// </summary>
/// <param name="actionExecutedContext">The action executed context.</param>
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.ActionContext.ActionDescriptor.GetCustomAttributes<NoLogWebApiFilter>().Any())
{
return;
}
// Some business logic
this.logger.DebugFormat("API Call of {0}()", actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
}
/// <summary>
/// Occurs before the action method is invoked.
/// </summary>
/// <param name="actionContext">The action context.</param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<NoLogWebApiFilter>().Any())
{
return;
}
// Some business logic
}
#endregion Public overrided Methods
}
}
Now, I want to register/unregister this custom action filter run-time later after application started, based on a flag.
Here is my filter registration code:
public void StartLogging()
{
var existingFilter
= System.Web.Http.GlobalConfiguration.Configuration.Filters.FirstOrDefault(
f =>
f.Instance.GetType().ToString().Equals(typeof(LogActionWebApiFilter).ToString()
));
if (existingFilter == null)
{
System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(new LogActionWebApiFilter(this.logger));
}
}
Here is my filter unregistration code:
public void StopLogging()
{
var existingFilter
= System.Web.Http.GlobalConfiguration.Configuration.Filters.FirstOrDefault(
f =>
f.Instance.GetType().ToString().Equals(typeof(LogActionWebApiFilter).ToString()
));
if (existingFilter != null)
{
System.Web.Http.GlobalConfiguration.Configuration.Filters.Remove(existingFilter.Instance);
existingFilter = null;
}
}
Problem :-
On each Application_BeginRequest()-event, I check the flag (has some business logic), and based on value of the flag I call respective method out of above 2 methods.
However, any of the above method doesn't work.
Even filter is added into System.Web.Http.GlobalConfiguration.Configuration.Filters, but OnActionExecuting() / OnActionExecuted() any of them are not invoked.
Please guide me, what's I'm doing wrong here.
Thanks in advance,
Balaguru.
It's not possible to control the action filter in Application_BeginRequest() event. Action filters are global Filters which can only register at Application_Start().
I would suggest , you can add or remove filter through configuration file.
<configSections>
<section name="filters" type="ConfigurableFilters.FiltersSettings, AssemblyName "/>
</configSections>
<filters>
<add type="ConfigurableFilters.BarFilter, AssemblyName" />
<add type="ConfigurableFilters.FooFilter, AssemblyName" />
</filters>
In that case you'll need a ConfigurationElement.
Public class FilterAction : ConfigurationElement
{
[ConfigurationProperty("type", IsRequired = true, IsKey = true)]
public string Type
{
get { return base["type"] as string; }
set { base["type"] = value; }
}
}
And a ConfigurationElementCollection.
public class FilterActionCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{ return new FilterAction();}
protected override object GetElementKey(ConfigurationElement element)
{ return ((FilterAction) element).Type;}
}
And a ConfigurationSection.
public class FiltersSettings : ConfigurationSection
{
public static FiltersSettings Settings
{
get {
var section = ConfigurationManager.GetSection("filters")
as FiltersSettings;
return section ?? new FiltersSettings();
}
}
[ConfigurationProperty("", IsDefaultCollection = true)]
public FilterActionCollection Filters
{
get { return base[_filtersProperty] as FilterActionCollection; }
set { base[_filtersProperty] = value; }
}
private readonly ConfigurationProperty _filtersProperty =
new ConfigurationProperty(
null, typeof (FilterActionCollection), null,
ConfigurationPropertyOptions.IsDefaultCollection);
}
One way to apply the configured filters is to use the following code during application startup:
var filters = FiltersSettings.Settings.Filters;
foreach (var filter in filters.Cast<FilterAction>())
{
var filterType = Type.GetType(filter.Type);
GlobalFilters.Filters.Add(Activator.CreateInstance(filterType));
}
The simplest answer here would be to add a toggle value to Request.Properties and interrogate this in your logging code.
public void StartLogging()
{
// Check the value does not already exist etc. and either add or modify.
if (actionContext.Request.Properties.ContainsKey("ShouldLog")) {
actionContext.Request.Properties["ShouldLog"] = true;
}
else {
actionContext.Request.Properties.Add("ShouldLog", true);
}
}
In your Logger:
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.Properties.ContainsKey("ShouldLog"))
{
// Parse and check the value of ShouldLog before returning when false.
return;
}
// Some business logic
}

Writing a generic FluentValidation custom validator to check unique constraint

Really new to C#, ASP.NET MVC and FluentValidation.
i have a user model like:
public class UserDetails{
public int ID { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
for now, i've been validating the UserName and Email using FluentValidation, something like:
public AdminDetailsValidator(){
RuleFor(ad => ad.UserName).NotNull().Must(UniqueUserName(UserName)).WithMessage("UserName not Available");
RuleFor(ad => ad.Email).NotNull().Must(UniqueEmail(Email)).WithMessage("This Email id has already been registered"); ;
}
public bool UniqueUserName(string un)
{
if (UserDbContext.userDetails.SingleOrDefault(p => p.UserName == un) == null)
{
return true;
}
else
{
return false;
}
}
public bool UniqueEmail(string em)
{
if (UserDbContext.userDetails.SingleOrDefault(p => p.Email == em) == null)
{
return true;
}
else
{
return false;
}
}
But i'd rather want a more generic UniqueValidator, that i can use with multiple classes and properties. Or Atleast, i don't have to make a separate function for each property. So i looked into the custom validators. But i have no idea, how i can use that feature for my needs.
I want to do something like this:
RuleFor(ad => ad.Email).NotNull().SetValidator(new UniquePropertyValidator<UserDbContext>(userDetails.Email).WithMessage("This Email id has already been registered");
Is that even possible to do that? I want to pass the DbContext as type parameter and property as an argument(or some variation of it, whichever works). and the method can check the property against the table and return if it's unique or not.
Have you looked into using lambdas and generics? I haven't used FluentValidation so this might not be the correct method for a validator.
var dbContext = new UserDbContext();
RuleFor(ud => ud.Email)
.NotNull()
.SetValidator(
new UniquePropertyValidator<UserDetails>
(ud, ud => ud.Email, () => dbcontext.userDetails)
.WithMessage("This Email id has already been registered");
public class UniquePropertyValidator<T> {
public UniquePropertyValidator(T entity, Func<T,string> propertyAccessorFunc, Func<IEnumerable<T>> collectionAccessorFunc) {
_entity = entity;
_propertyAccessorFunc = propertyAccessorFunc;
_collectionAccessorFunc =collectionAccessorFunc;
}
public bool Validate(){
//Get all the entities by executing the lambda
var entities = _collectionAccessorFunc();
//Get the value of the entity that we are validating by executing the lambda
var propertyValue = _propertyAccessorFunc(_entity);
//Find the matching entity by executing the propertyAccessorFunc against the
//entities in the collection and comparing that with the result of the entity
//that is being validated. Warning SingleOrDefault will throw an exception if
//multiple items match the supplied predicate
//http://msdn.microsoft.com/en-us/library/vstudio/bb342451%28v=vs.100%29.aspx
var matchingEntity = entities.SingleOrDefault(e => _propertyAccessorFunc(e) == propertyValue);
return matchingEntity == null;
}
}
I have also been trying to find an elegant solution for this validator, but the solution provided so far seems to fetch all the data and then check for uniqueness. That is not very good in my opinion.
When trying to use the implementation proposed below, I get an error that LINQ to Entities does not support Invoke (i.e. executing a Func<> inside the Where clause). Is there any workaround?
public class UniqueFieldValidator<TObject, TViewModel, TProperty> : PropertyValidator where TObject : Entity where TViewModel : Entity
{
private readonly IDataService<TObject> _dataService;
private readonly Func<TObject, TProperty> _property;
public UniqueFieldValidator(IDataService<TObject> dataService, Func<TObject, TProperty> property)
: base("La propiedad {PropertyName} tiene que ser unica.")
{
_dataService = dataService;
_property = property;
}
protected override bool IsValid(PropertyValidatorContext context)
{
var model = context.Instance as TViewModel;
var value = (TProperty)context.PropertyValue;
if (model != null && _dataService.Where(t => t.Id != model.Id && Equals(_property(t), value)).Any())
{
return false;
}
return true;
}
}
public class ArticuloViewModelValidator : AbstractValidator<ArticuloViewModel>
{
public ArticuloViewModelValidator(IDataService<Articulo> articuloDataService)
{
RuleFor(a => a.Codigo).SetValidator(new UniqueFieldValidator<Articulo, ArticuloViewModel, int>(articuloDataService, a => a.Codigo));
}
}
We can solve this problem simply by working with LINQ to Entities.
Here is a static method used to determine whether the given value is unique in the specified DbSet:
static class ValidationHelpers
{
/// <summary>
/// Determines whether the specified <paramref name="newValue"/> is unique inside of
/// the given <paramref name="dbSet"/>.
/// </summary>
/// <param name="dbSet"></param>
/// <param name="getColumnSelector">
/// Determines the column, with which we will compare <paramref name="newValue"/>
/// </param>
/// <param name="newValue">
/// Value, that will be checked for uniqueness
/// </param>
/// <param name="cancellationToken"></param>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TColumn"></typeparam>
/// <returns></returns>
public static async Task<bool> IsColumnUniqueInsideOfDbSetAsync<TEntity, TColumn>(DbSet<TEntity> dbSet,
Expression<Func<TEntity, TColumn>> getColumnSelector,
TColumn newValue,
CancellationToken cancellationToken)
where TEntity : class
{
return !await dbSet
.Select(getColumnSelector)
.AnyAsync(column => column.Equals(newValue), cancellationToken);
}
}
Example of using
We have the following entity:
public class Category
{
// ...
public string Title { get; set; }
// ...
}
And a DbContext class:
public interface ApplicationDbContext
{
// ...
public DbSet<Category> Category { get; set; }
// ...
}
Let's say a user wants to create a new category. We want to validate the title of this category for uniqueness:
RuleFor(c => c.Title)
.MustAsync
(
(newTitle, token) => ValidationHelpers.IsColumnUniqueInsideOfDbSetAsync
(_context.Category, c => c.Title, newTitle, token)
)
.WithMessage("{PropertyName} must be unique");
Note: _context is an object of type ApplicationDbContext.

Custom Data Annotations ASP.NET MVC C#

I have the follwing question about MVC 2 with C#.
Here is my Model:
public class Pmjob
{
[Tooltext="Hier soll der Name eingegeben werden"]
[DisplayName("Type")]
public int Name { get; set; }
}
Now I want to reach the Tooltext item in my view, e. g.:
#Html.ToolTextFor(Model => Model.Pmjob.Name)
or in the BL:
if ( Model.Pmjob.Name.Tooltext == "") {
}
Is this possible?
Create an abstract class MetaDataAttribute :
public abstract class MetadataAttribute : Attribute
{
/// <summary>
/// Method for processing custom attribute data.
/// </summary>
/// <param name="modelMetaData">A ModelMetaData instance.</param>
public abstract void Process(ModelMetadata modelMetaData);
}
Make your attribute inherit from MetaDataAttribute :
public class ToolTextAttribute : MetadataAttribute
{
public string Text { get; set; }
public TooltextAttribute(string text)
{
this.Text = new text;
}
public override void Process(ModelMetadata modelMetaData)
{
modelMetaData.AdditionalValues.Add("ToolText", this.Text);
}
}
Create the custom MetaDataProvider :
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(modelMetadata));
return modelMetadata;
}
}
And replace the default one (global.asax.cs) :
protected void Application_Start()
{
// snipped
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}
Finally, you can access it in your view (or in a Html Helper ) :
(string)ViewData.ModelMetadata.AdditionalValues.Where(x => x.Key == "ToolText").SingleOrDefault()
Source :
http://weblogs.asp.net/seanmcalinden/archive/2010/06/11/custom-asp-net-mvc-2-modelmetadataprovider-for-using-custom-view-model-attributes.aspx
http://weblogs.asp.net/seanmcalinden/archive/2010/06/12/asp-net-mvc-2-auto-complete-textbox-custom-view-model-attribute-amp-editortemplate.aspx

Categories

Resources