Test that property has child validator FluentValidation - c#

The below test fails when I run it. I have an object, instruction that has a number of properties, most of which require their own validators. I want to be able to check that the validators for these child properties are present when set by the parent validator.
[Test]
public void ChildValidatorsSet()
{
_validator.ShouldHaveChildValidator(i => i.Property, typeof(FluentPropertyValidator));
_validator.ShouldHaveChildValidator(i => i.AdditionalInformation, typeof(FluentAdditionalInformationValidator));
}
Within the validator for this class I have the below rules defined that ensure the property in quest has a value set and sets a validator when the property is not null.
public FluentRemortgageInstructionValidator()
{
RuleFor(si => si.Property)
.NotNull().WithMessage("Solicitor Instruction: Instructions must have a linked property.");
RuleFor(si => si.Property)
.SetValidator(new FluentPropertyValidator()).When(si => si.Property != null);
RuleFor(si => si.AdditionalInformation)
.NotNull().WithMessage("Remortgage instructions must have an additional information table.");
RuleFor(si => si.AdditionalInformation)
.SetValidator(new FluentAdditionalInformationValidator()).When(si => si.AdditionalInformation != null);
}
Instruction class:
public class Instruction
{
[Key]
public AdditionalInformation AdditionalInformation { get; set; }
public Property Property { get; set; }
}
}
When an Instruction object with a valid Property property is passed through to the validator the validator should then set validators for Property and AdditionalInformation. Which is what happens when I run my code.
However I am unable to test for this, as there is no way to pass a valid object through to the ShouldHaveChildValidator method, and therefore no child validator is being set.
How do I design a test to check that these child validators are being set properly?

I'll give you an option so you can achieve what you want here, however I have to say that I haven't thought thoroughly about any side effects, so bear that in mind.
Your validators will always be set regardless of the property values, that's why you don't have to pass an instance of any object when calling ShouldHaveChildValidator method. The fact that they get executed or not is another story, and that as you know will depend on your rulesets.
So I cloned the fluent validation git repo and checked out how does the code check for the existence of the child validators.
For this call:
_validator.ShouldHaveChildValidator(i=>i.Property, typeof(FluentPropertyValidator));
This is what is does:
It gets the matching validators for the property expression you pass
in to the method call: i => i.Property
It filters the matching validators to get only those of type IChildValidatorAdaptor.
It throws an error if none of the selected validators are assignable from the type you pass to the method call: FluentPropertyValidator
It seems the code is missing the case where the validator is wrapped by another validator. That's the case of DelegatingValidator class which, by the way, is the type your child validator uses. So one possible solution is to also take those validator types into consideration.
I created a extension method you can use following the same pattern of the original one. Due to my lack of creativity when naming things (naming is tough), I named ShouldHaveChildValidatorCustom. This method is the same method in the code that also calls a couple of another methods that I just copied over from the sources of the FluentValidation so I could add the small modification.
Here is the complete extension class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using FluentValidation.Internal;
using FluentValidation.TestHelper;
using FluentValidation.Validators;
namespace YourTestExtensionsNamespace
{
public static class CustomValidationExtensions
{
public static void ShouldHaveChildValidatorCustom<T, TProperty>(this IValidator<T> validator, Expression<Func<T, TProperty>> expression, Type childValidatorType)
{
var descriptor = validator.CreateDescriptor();
var expressionMemberName = expression.GetMember()?.Name;
if (expressionMemberName == null && !expression.IsParameterExpression())
throw new NotSupportedException("ShouldHaveChildValidator can only be used for simple property expressions. It cannot be used for model-level rules or rules that contain anything other than a property reference.");
var matchingValidators = expression.IsParameterExpression() ? GetModelLevelValidators(descriptor) : descriptor.GetValidatorsForMember(expressionMemberName).ToArray();
matchingValidators = matchingValidators.Concat(GetDependentRules(expressionMemberName, expression, descriptor)).ToArray();
var childValidatorTypes = matchingValidators
.OfType<IChildValidatorAdaptor>()
.Select(x => x.ValidatorType);
//get also the validator types for the child IDelegatingValidators
var delegatingValidatorTypes = matchingValidators
.OfType<IDelegatingValidator>()
.Where(x => x.InnerValidator is IChildValidatorAdaptor)
.Select(x => (IChildValidatorAdaptor)x.InnerValidator)
.Select(x => x.ValidatorType);
childValidatorTypes = childValidatorTypes.Concat(delegatingValidatorTypes);
var validatorTypes = childValidatorTypes as Type[] ?? childValidatorTypes.ToArray();
if (validatorTypes.All(x => !childValidatorType.GetTypeInfo().IsAssignableFrom(x.GetTypeInfo())))
{
var childValidatorNames = validatorTypes.Any() ? string.Join(", ", validatorTypes.Select(x => x.Name)) : "none";
throw new ValidationTestException(string.Format("Expected property '{0}' to have a child validator of type '{1}.'. Instead found '{2}'", expressionMemberName, childValidatorType.Name, childValidatorNames));
}
}
private static IPropertyValidator[] GetModelLevelValidators(IValidatorDescriptor descriptor)
{
var rules = descriptor.GetRulesForMember(null).OfType<PropertyRule>();
return rules.Where(x => x.Expression.IsParameterExpression()).SelectMany(x => x.Validators)
.ToArray();
}
private static IEnumerable<IPropertyValidator> GetDependentRules<T, TProperty>(string expressionMemberName, Expression<Func<T, TProperty>> expression, IValidatorDescriptor descriptor)
{
var member = expression.IsParameterExpression() ? null : expressionMemberName;
var rules = descriptor.GetRulesForMember(member).OfType<PropertyRule>().SelectMany(x => x.DependentRules)
.SelectMany(x => x.Validators);
return rules;
}
}
}
And this is a test that should pass if you set the child validators to your classes and fail otherwise:
[Fact]
public void ChildValidatorsSet()
{
var _validator = new FluentRemortgageInstructionValidator();
_validator.ShouldHaveChildValidatorCustom(i => i.Property, typeof(FluentPropertyValidator));
_validator.ShouldHaveChildValidatorCustom(i => i.AdditionalInformation, typeof(FluentAdditionalInformationValidator));
}
Hope this helps!

Related

passing entity type as parameter in linq

How would I go about passing an entity type as a parameter in linq?
For e.g. The method will receive the entity name value as a string and I would like to pass the entity name to the below linq query. Is it possible to make the linq query generic ?
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = context.<EntityType>.Tolist();
return View(entityResults);
}
I would like to pass the Entity type as a parameter and return all property values.
Also, is it possible to filter results based the some property?
Assuming your context class is looking like this:
public class MyContext : DbContext
{
public DbSet<Entity1> Entity1 { get; set; }
public DbSet<Entity2> Entity2 { get; set; }
// and so on ...
}
simplest solution is to write method that looks like
private List<object> Selector(string entityTypeName)
{
if (entityTypeName == "Entity1")
return context.Entity1.ToList();
if (entityTypeName == "Entity2")
return context.Entity2.ToList()
// and so on
// you may add a custom message here, like "Unknown type"
throw new Exception();
}
But we don't want to hardcode this stuff, so let create Selector dynamically with Linq.Expressions
Define a Func field within your controller:
private readonly Func<string, List<object>> selector;
Now you can create a factory for this member:
private Func<string, List<object>> SelectByType()
{
var myContext = Expression.Constant(context);
var entityTypeName = Expression.Parameter(typeof(string), "entityTypeName");
var label = Expression.Label(typeof(List<object>));
var body = Expression.Block(typeof(MyContext).GetProperties()
.Where(p => typeof(IQueryable).IsAssignableFrom(p.PropertyType) && p.PropertyType.IsGenericType)
.ToDictionary(
k => Expression.Constant(k.PropertyType.GetGenericArguments().First().Name),
v => Expression.Call(typeof(Enumerable), "ToList", new[] {typeof(object)}, Expression.Property(myContext, v.Name))
)
.Select(kv =>
Expression.IfThen(Expression.Equal(kv.Key, entityTypeName),
Expression.Return(label, kv.Value))
)
.Concat(new Expression[]
{
Expression.Throw(Expression.New(typeof(Exception))),
Expression.Label(label, Expression.Constant(null, typeof(List<object>))),
})
);
var lambda = Expression.Lambda<Func<string, List<object>>>(body, entityTypeName);
return lambda.Compile();
}
and assign Func with it (somewhere in constructor)
selector = SelectByType();
Now you can use it like
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = selector(entityTypeName);
return View(entityResults);
}
You have two options:
Option 1: You know the entity type at compile time
If you know the entity type at compile time, use a generic method:
public ActionResult EntityRecords<TEntity>()
{
var entityResults = context.Set<TEntity>.ToList();
return View(entityResults);
}
Usage:
public ActionResult UserRecords()
{
return EntityRecords<User>();
}
Option 2: You know the entity type only at runtime
If you actually want to pass the entity type as a string, use the other overload of Set that takes a type:
public ActionResult EntityRecords(string entityType)
{
var type = Type.GetType(entityType);
var entityResults = context.Set(type).ToList();
return View(entityResults);
}
This assumes that entityType is a fully qualified type name including assembly. See this answer for details.
If the entities are all inside the same assembly as the context - or in another well known assembly - you can use this code instead to get the entity type:
var type = context.GetType().Assembly.GetType(entityType);
This allows you to omit the assembly in the string, but it still requires the namespace.
You can achieve what you want even if the context doesn't have DbSet properties (and if it does, that doesn't harm). It is by calling the DbContext.Set<TEntity>() method by reflection:
var nameSpace = "<the full namespace of your entity types here>";
// Get the entity type:
var entType = context.GetType().Assembly.GetType($"{nameSpace}.{entityTypeName}");
// Get the MethodInfo of DbContext.Set<TEntity>():
var setMethod = context.GetType().GetMethods().First(m => m.Name == "Set" && m.IsGenericMethod);
// Now we have DbContext.Set<>(), turn it into DbContext.Set<TEntity>()
var genset = setMethod.MakeGenericMethod(entType);
// Create the DbSet:
var dbSet = genset.Invoke(context, null);
// Call the generic static method Enumerable.ToList<TEntity>() on it:
var listMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(entType);
var entityList = listMethod.Invoke(null, new[] { dbSet });
Now you've got your list of entities.
One remark: To get rid of some performance impact due to reflection you could cache some types and non-generic method infos.
Another remark: I don't think I would recommend this. As said in a comment: this raises a couple of concerns. For example: are you going to allow a client application to get all unfiltered data of any entity table? Whatever it is you're doing: handle with care.
In your example, it looks like you have a controller action that's taking the entity name as a parameter, so you won't be able to make your method generic. But you can use reflection and avoid the use of generics for the most part.
public ActionResult EntityRecords(string entityTypeName)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = (IQueryable)entityProperty.GetValue(context);
var entityResults = entityQueryObject.Cast<object>().ToList();
return View(entityResults);
}
There are a few things to keep in mind, though:
The assumption is that you've got a property on your context corresponding to the given entityTypeName argument. If entityTypeName is actually the type name instead of the property name, you'll need to do extra work to find the appropriate property.
Your View will have to know what to do with a collection of objects where the type of the objects is not known at compile time. It'll probably have to use reflection to do whatever you intend for it to do.
There may be some security concerns in a method like this. For example, if the user provides "Database" or "Configuration", you could end up exposing information like your connection string, which has nothing to do with the actual entities you've stored.
Also, is it possible to filter results based the some property?
Yes, and it will involve a similar use of reflection and/or dynamic. You could use a library like Dynamic LINQ to pass strings into LINQ-like method overloads (Where, Select, etc.).
public ActionResult EntityRecords(string entityTypeName, FilterOptions options)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = entityProperty.GetValue(context);
var entityResults = ApplyFiltersAndSuch((IQueryable)entityQueryObject);
return View(entityResults);
}
private IEnumerable<object> ApplyFiltersAndSuch(IQueryable query, FilterOptions options)
{
var dynamicFilterString = BuildDynamicFilterString(options);
return query.Where(dynamicFilterString)
// you can add .OrderBy... etc.
.Cast<object>()
.ToList();
}

C# reflection get implementing property of an interface property

I have an interface that I've defined a custom attribute on a property and I want to retrieve the relevant property from a derived instance of that interface.
public interface ITopicProcessor<T>
{
[TopicKey]
string TopicName { get; }
[OtherAttribute]
string OtherProperty { get; }
void Process(T message);
}
public class MyClassProcessor : ITopicProcessor<MyClass>
{
public string TopicName => "MyTopic";
public string OtherProperty => "Irrelevant";
public void Process(MyClass message)
{
}
}
I can get close with the following - the main issue is that the derived interface type doesn't seem to have the same custom attributes as the generic type definition. I'm pretty sure it's partly due to needing to use the underlying method implementation rather than use the property value directly
// iType is typeof(ITopicProcessor<MyClass>)
// I also have access to the generic type definition if need be - i.e. typeof(ITopicProcessor<>)
Func<Type, string> subscriberTypeToTopicKeySelector = iType =>
{
// Creating an instance via a dependency injection framework
var instance = kernel.Get(iType);
var classType = instance.GetType();
var interfaceMap = classType.GetInterfaceMap(iType);
// interfaceMap.InterfaceMethods contains underlying get_property method, but no custom attributes
var interfaceMethod = interfaceMap.InterfaceMethods
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.ToList();
var classMethodInfo = interfaceMap.TargetMethods[Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod)];
return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
.ToString();
};
Implementing an interface is not inheriting from a class. This is why such appributes do not propagate from interface to class. See: bradwilson.typepad.com/blog/2011/08/interface-attributes-class-attributes.html
But there are workarounds: Can a C# class inherit attributes from its interface?
Going off #thehennyy's comment I got something that not only works, but can handle the [TopicKey] attribute being applied to either a property or method. For my needs I only want it to appear once in an interface, but anyone else can extend this solution for their needs
subscriberTypeToTopicKeySelector = iType =>
{
var instance = kernel.Get(iType);
var classType = instance.GetType();
var interfaceMap = classType.GetInterfaceMap(iType);
var iTopicKeyPropertyGetMethods = iType.GetProperties()
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.Select(x => x.GetMethod);
var iTopicKeyMethods = iType.GetMethods()
.Where(x => x.HasAttribute<TopicKeyAttribute>())
.Union(iTopicKeyPropertyGetMethods);
var indexOfInterfaceMethod = Array.IndexOf(interfaceMap.InterfaceMethods, iTopicKeyMethods.Single());
var classMethodInfo = interfaceMap.TargetMethods[indexOfInterfaceMethod];
return classMethodInfo.Invoke(instance, BindingFlags.Default, null, null, CultureInfo.CurrentCulture)
.ToString();
};

Use LinqKit PredicateBuilder for related model (EF Core)

I want to use LinqKit's PredicateBuilder and pass the predicate into .Any method for related model.
So I want to build a predicate:
var castCondition = PredicateBuilder.New<CastInfo>(true);
if (movies != null && movies.Length > 0)
{
castCondition = castCondition.And(c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
castCondition = castCondition.And(c => c.RoleId == roleType);
}
And then use it to filter model that has relation to model in predicate:
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
But this causes a System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(Convert(__castCondition_0, Func``2))': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.UnaryExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
I saw similar question and answer there suggests to use .Compile. Or one more question that build an extra predicate.
So I tried to use extra predicate
var tp = PredicateBuilder.New<Name>(true);
tp = tp.And(n => n.CastInfo.Any(castCondition.Compile()));
IQueryable<Name> result = _context.Name.AsExpandable().Where(tp);
Or use compile directly
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castCondition.Compile()));
But I have an error about Compile: System.NotSupportedException: Could not parse expression 'n.CastInfo.Any(__Compile_0)'
So is it possible to convert the result from PredicateBuilder to pass into Any?
Note: I was able to build the desired behavior combining expressions, but I don't like that I need extra variables.
System.Linq.Expressions.Expression<Func<CastInfo,bool>> castExpression = (c => true);
if (movies != null && movies.Length > 0)
{
castExpression = (c => movies.Contains(c.MovieId));
}
if (roleType > 0)
{
var existingExpression = castExpression;
castExpression = c => existingExpression.Invoke(c) && c.RoleId == roleType;
}
IQueryable<Name> result = _context.Name.AsExpandable().Where(n => n.CastInfo.Any(castExpression.Compile()));
return await result.OrderBy(n => n.Name1).Take(25).ToListAsync();
So I assume I just miss something about builder.
Update about versions: I use dotnet core 2.0 and LinqKit.Microsoft.EntityFrameworkCore 1.1.10
Looking at the code, one will assume that the type of castCondition variable is Expression<Func<CastInfo, bool>> (as it was in earlier versions of PredicateBuilder).
But if that was the case, then n.CastInfo.Any(castCondition) should not even compile (assuming CastInfo is a collection navigation property, so the compiler will hit Enumerable.Any which expects Func<CastInfo, bool>, not Expression<Func<CastInfo, bool>>). So what's going on here?
In my opinion, this is a good example of C# implicit operator abuse. The PredicateBuilder.New<T> method actually returns a class called ExpressionStarter<T>, which has many methods emulating Expression, but more importantly, has implicit conversion to Expression<Func<T, bool>> and Func<CastInfo, bool>. The later allows that class to be used for top level Enumerable / Queryable methods as replacement of the respective lambda func/expression. However, it also prevents the compile time error when used inside the expression tree as in your case - the complier emits something like n.CastInfo.Any((Func<CastInfo, bool>)castCondition) which of course causes exception at runtime.
The whole idea of LinqKit AsExpandable method is to allow "invoking" expressions via custom Invoke extension method, which then is "expanded" in the expression tree. So back at the beginning, if the variable type was Expression<Func<CastInfo, bool>>, the intended usage is:
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.Invoke(c)));
But now this doesn't compile because of the reason explained earlier. So you have to convert it first to Expression<Func<T, bool> outside of the query:
Expression<Func<CastInfo, bool>> castPredicate = castCondition;
and then use
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castPredicate.Invoke(c)));
or
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(castPredicate.Compile()));
To let compiler infer the expression type, I would create a custom extension method like this:
using System;
using System.Linq.Expressions;
namespace LinqKit
{
public static class Extensions
{
public static Expression<Func<T, bool>> ToExpression<T>(this ExpressionStarter<T> expr) => expr;
}
}
and then simply use
var castPredicate = castCondition.ToExpression();
It still has to be done outside of the query, i.e. the following does not work:
_context.Name.AsExpandable().Where(n => n.CastInfo.Any(c => castCondition.ToExpression().Invoke(c)));
It may not be exactly related to the original question, but considering the following model :
public Class Music
{
public int Id { get; set; }
public List<Genre> Genres { get; set; }
}
public Class Genre
{
public int Id { get; set; }
public string Title { get; set; }
}
List<string> genresToFind = new() {"Pop", "Rap", "Classical"};
If you are trying to find all Musics that their genres exist in genresToFind list, here's what you can do:
Create PredicateBuilder expressions chain on Genre model :
var pre = PredicateBuilder.New<Genre>();
foreach (var genre in genresToFind)
{
pre = pre.Or(g => g.Title.Contains(genre));
}
Then execute your query like this :
var result = await _db.Musics.AsExpandable()
.Where(m => m.Genres
.Any(g => pre.ToExpression().Invoke(g)))
.ToListAsync();
ToExpression() is a generic extension method that we've created to convert ExpressionStarter<Genre> type to Expression<Func<Genre, bool>> :
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> ToExpression<T> (this
ExpressionStarter<T> exp) => exp;
}
Also, you'll need LinqKit.Microsoft.EntityFrameworkCore package for efcore.

Automapper: is it possible to extend same rule to all members? [duplicate]

I want to use automapper to map between my public data contracts and my DB models. I have created a class which automatically goes through all the contracts are creates mappings. The only problem I have is that I only want to map values from the contract to the DB model if the value is not null. I have looked at other question on here but cant see examples that use custom resolvers.
Here is some of my code
var mapToTarget = AutoMapper.Mapper.CreateMap(contract, mappedTo);
foreach (var property in contract.GetProperties().Where(property => property.CustomAttributes.Any(a => a.AttributeType == typeof(MapsToProperty))))
{
var attribute = property.GetCustomAttributes(typeof(MapsToProperty), true).FirstOrDefault() as MapsToProperty;
if (attribute == null) continue;
mapToTarget.ForMember(attribute.MappedToName,
opt =>
opt.ResolveUsing<ContractToSourceResolver>()
.ConstructedBy(() => new ContractToSourceResolver(new MapsToProperty(property.Name, attribute.SourceToContractMethod, attribute.ContractToSourceMethod))));
}
private class ContractToSourceResolver : ValueResolver<IDataContract, object>
{
private MapsToProperty Property { get; set; }
public ContractToSourceResolver(MapsToProperty property)
{
Property = property;
}
protected override object ResolveCore(IDataContract contract)
{
object result = null;
if (Property.ContractToSourceMethod != null)
{
var method = contract.GetType()
.GetMethod(Property.ContractToSourceMethod, BindingFlags.Public | BindingFlags.Static);
result = method != null ? method.Invoke(null, new object[] {contract}) : null;
}
else
{
var property =
contract.GetType().GetProperties().FirstOrDefault(p => p.Name == Property.MappedToName);
if (property != null)
{
result = property.GetValue(contract);
}
}
return result;
}
}
And this is how I want to use it
AutoMapper.Mapper.Map(dataContract, dbModel)
This currently works great but if there is a NULL value in the dataContract then it will replace the existing value in the dbModel, this is not what I want. How do I make AutoMapper ignore null source values?
EDIT
As pointed out in one of the answers there is this
Mapper.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));
This would be ideal except for the fact that .ForAllMembers is not accessible from
Mapper.CreateMap(SourceType, DestinationType)
UPDATE: IsSourceValueNull is not available starting from V5.
If you want all source properties with null values to be ignored you could use:
Mapper.CreateMap<SourceType, DestinationType>()
.ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));
Otherwise, you can do something similar for each member.
Read this.
For newer versions that utilize the Instance API, use this instead:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Condition(
(source, destination, sourceMember, destMember) => (sourceMember != null)));
});
Note: This functionality works as of 5.0.2, breaking on later versions as of this writing. Waiting for the next 5.2.x release is recommended if upgrading from 5.0.2.
The solution here works for my project, which is using AutoMapper 6.0.2. In previous projects using AutoMapper 4, I had used IsSourceValueNull to achieve similar behavior. I have nullable types mapped to non-nullable types in my project, this solution is able to handle that scenario.
I made a small change to the original solution. Instead of checking the type of the property to be mapped, I set the filter in ForAllPropertyMaps to check the type of the source object, so that the custom resolver only applies to maps from that source object. But the filter can be set to anything as needed.
var config = new MapperConfiguration(cfg =>
{
cfg.ForAllPropertyMaps(
pm => pm.TypeMap.SourceType == typeof(<class of source object>),
(pm, c) => c.ResolveUsing<object, object, object, object>(new IgnoreNullResolver(), pm.SourceMember.Name));
});
class IgnoreNullResolver : IMemberValueResolver<object, object, object, object>
{
public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
{
return sourceMember ?? destinationMember;
}
}
I've got the same exact issue with mapping up the conditionals to the non-generic types. Here's how I solved it.
Wire up:
foreach (PropertyInfo p in type.GetProperties().Where(x => x.GetCustomAttributes<SkipMapIfNullAttribute>().Any()))
map.ForMember(p.Name, x => x.ResolveUsing(typeof(SkipMapIfNullResolver)).FromMember(p.Name));
The second .FromMember is required so the value for the member is passed to the value resolver, rather than the full model.
The resolver looks like this:
public class SkipMapIfNullResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
if (source.Value == null)
source.ShouldIgnore = true;
return source;
}
}

Get Attributes of a Func

I need to check an incoming Func<TIn, TOut> for a specific attribute.
So far I have:
var methodInfo = cachedMethod.GetMethodInfo();
var isCachable = methodInfo.CustomAttributes.
Any(x => x.AttributeType == typeof(CachedAttribute));
However I'm finding that the CustomAttributes property is empty.
What's the proper way to find the attributes applied to an incoming func? In my case the func is a static method in another class.
Update
Here's a small sample showing how I'm trying to use this:
The HTML helper:
public static IHtmlString CachedPartial<TModel>(this HtmlHelper helper,
string partialName,
TModel model,
Func<string, string> cachedMethod)
{
var methodInfo = cachedMethod.GetMethodInfo();
var isCachable = methodInfo.IsDefined(typeof (CachedAttribute));
if (!isCachable)
{
throw new Exception("...");
}
return new MvcHtmlString(cachedMethod("foo"));
}
The cached method
[Cached]
public static string GenrateSiteMapGraphHtml(string siteCode)
{
return "Foo";
}
This is being called from a Razor view:
#Html.CachedPartial("Foo",
Model,
HtmlHelperExtensions.GenrateSiteMapGraphHtml)
I may not quite understand what is your problem, but I'll suggest two things:
You need to be sure that CustomAttributes isn't empty, use
if (methodInfo.CustomAttributes!=NULL) //or whatever is analogue of NULL for that type of property
{/some action/}
You can't figure out why is this property empty. The first thing to check is visibility.
In my case the func is a static method in another class
So go and check out if that class is visible for the area of code where you are trying to invoke that static method

Categories