I use FluentValidation v10.4.0. How can I create own custom rule with preliminary transformation ?
public static IRuleBuilderOptionsConditions<T, string> SomeRuleName<T>(
this IRuleBuilder<T, string> ruleBuilder, Expression<Func<T, string>> expression)
{
return ruleBuilder
// error happens below. Transform is not included in IRuleBuilderOptionsConditions
.Transform(expression, value =>
{
// some transformation
})
.NotEmpty();
}
Thanks in advance for any help !
public class MyValidator : AbstractValidator<MyClassToValidate>
{
public MyValidator()
{
RuleFor(x => x).CustomAsync(async (model, context, cancellationToken) =>
{}
}
}
You can instantiate the class or register it in startup class using AddFluentValidation() method.
Related
I need to customize the way MyAutoMapper profile maps my objects to DTOs. From one of my ApplicationServices, I use an ObjectMapper for a relatively simple mapping. The catch is that ABP's AutoMapper isn't the normal AutoMapper that everyone knows about.
Below is a snippet of what it would ideally look like; Except opt.MapFrom(m => Localizer[m.Type.ToString()]) and _objectMapper.Map<Preparation, DtoPreparation>(preparation, _localizer) cannot work that way.
public class MyAutoMapperProfile : Profile
{
public MyAutoMapperProfile()
{
CreateMap<Preparation, DtoPreparation>()
.ForMember(m => m.PreparatorType, opt => opt.MapFrom(m => m.Type))
.ForMember(m => m.PreparatorTypeString, opt => opt.MapFrom(m => Localizer[m.Type.ToString()]));
}
}
public class SlipsAppService : TaxesAppService
{
private readonly IObjectMapper<TaxesApplicationModule> _objectMapper;
private readonly ISlipsManager _slipsManager;
private readonly IStringLocalizer<TaxesResource> _localizer;
public SlipsAppService(ISlipsManager iSlipsManager, IObjectMapper<TaxesApplicationModule> objectMapper, IStringLocalizer<TaxesResource> localizer)
{
_objectMapper = objectMapper;
_slipsManager = iSlipsManager;
_localizer = localizer;
}
[Microsoft.AspNetCore.Mvc.HttpPost("/api/slips/get-or-create-preparation")]
public async Task<DtoPreparation> GetOrCreateCurrentPreparation(BaseGetterInput input)
{
var preparation = await _slipsManager.GetOrCreatePreparation(input.Id);
return _objectMapper.Map<Preparation, DtoPreparation>(preparation, _localizer);
}
}
I can't find a way to pass any information from my ApplicationService to the AutoMapper Profile, as IObjectMapper.Map<>() has no parameters for additional options or objects, unlike the normal AutoMapper.
Maybe there is a way to register the Profile in dependency injection, but with my limited knowledge of the framework, I couldn't find a clue...
For now, my problem is only with Localization, but really it can apply to anything. Since my DTOs contain other nested DTOs, managing extra stuff outside of the AutoMapper isn't an option, unless I change the structure of my application just for a workaround.
Since you are using one mapping profile per appservice, here is a good suggestion that works for me:
Create a class the implements the IMappingAction interface.
In the implementation of the Process method, inject your ILocalizer and use it with the source and destination.
In your mapping, instead of passing the localizer, chain call with AfterMap.
Here is an example:
public class MyAutoMapperProfile : Profile
{
public MyAutoMapperProfile()
{
CreateMap<Preparation, DtoPreparation>()
.ForMember(m => m.PreparatorType, opt => opt.MapFrom(m => m.Type))
.AfterMap<PreparationDtoLocalizerAction>;
}
}
public class PreparationDtoLocalizerAction : IMappingAction<Preparation, DtoPreparation>
{
private readonly IStringLocalizer<TaxesResource> _localizer;
public PreparationDtoLocalizerAction(IStringLocalizer<TaxesResource> localizer)
{
_localizer = localizer;
}
public void Process(Preparation source, DtoPreparation destination)
{
destination.PreparatorTypeString = _localizer[source.Type.ToString()]
}
}
public class SlipsAppService : TaxesAppService
{
private readonly IObjectMapper<TaxesApplicationModule> _objectMapper;
private readonly ISlipsManager _slipsManager;
public SlipsAppService(ISlipsManager iSlipsManager, IObjectMapper<TaxesApplicationModule> objectMapper)
{
_objectMapper = objectMapper;
_slipsManager = iSlipsManager;
}
[Microsoft.AspNetCore.Mvc.HttpPost("/api/slips/get-or-create-preparation")]
public async Task<DtoPreparation> GetOrCreateCurrentPreparation(BaseGetterInput input)
{
var preparation = await _slipsManager.GetOrCreatePreparation(input.Id);
return _objectMapper.Map<Preparation, DtoPreparation>(preparation);
}
}
Set up MVC with the extension method
services.AddMvc()
Then in a controller, and this may apply to GET also, create a method for the POST action with a parameter supplied in the body, e.g.
[HttpPost("save")]
public Entity Save([FromBody]Entity someEntity)
When the action is called the MVC pipeline will call the ParameterBinder which in turn calls DefaultObjectValidator. I don't want the validation (its slow for one thing, but more importantly is looping on complex cyclical graphs), but it seems the only way to turn off validation in the pipeline is something like this:
public class NonValidatingValidator : IObjectModelValidator
{
public void Validate(ActionContext actionContext, ValidationStateDictionary validationState, string prefix, object model)
{
}
}
and in the StartUp/ConfigureServices:
var validator = services.FirstOrDefault(s => s.ServiceType == typeof(IObjectModelValidator));
if (validator != null)
{
services.Remove(validator);
services.Add(new ServiceDescriptor(typeof(IObjectModelValidator), _ => new NonValidatingValidator(), ServiceLifetime.Singleton));
}
which seems like a sledgehammer. I've looked around and can't find an alternative, also tried to remove the DataAnnotationModelValidator without success, so would like to know if there's a better/correct way to turn off validation?
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
should disable automatic model state validation.
You should consider to use the ValidateNeverAttribute, which is nearly undocumented and well hidden by Microsoft.
[ValidateNever]
public class Entity
{
....
}
This gives you fine grained control over which entities to validate and which not.
As of aspnet core 3.1, this is how you disable model validation as seen in docs:
First create this NullValidator class:
public class NullObjectModelValidator : IObjectModelValidator
{
public void Validate(ActionContext actionContext,
ValidationStateDictionary validationState, string prefix, object model)
{
}
}
Then use it in place of the real model validator:
services.AddSingleton<IObjectModelValidator, NullObjectModelValidator>();
Note that this only disable Model validation, you'll still get model binding errors.
The .AddMvc() extension method has an overload where you can configure a lot of things. One of these things is the list of ModelValidatorProviders.
If you clear this list, e.g.:
services.AddMvc(options => options.ModelValidatorProviders.Clear());
validation should not take place any longer.
Use this extension method:
public static IServiceCollection DisableDefaultModelValidation(this IServiceCollection services)
{
ServiceDescriptor serviceDescriptor = services.FirstOrDefault<ServiceDescriptor>((Func<ServiceDescriptor, bool>) (s => s.ServiceType == typeof (IObjectModelValidator)));
if (serviceDescriptor != null)
{
services.Remove(serviceDescriptor);
services.Add(new ServiceDescriptor(typeof (IObjectModelValidator), (Func<IServiceProvider, object>) (_ => (object) new EmptyModelValidator()), ServiceLifetime.Singleton));
}
return services;
}
public class EmptyModelValidator : IObjectModelValidator
{
public void Validate(ActionContext actionContext, ValidationStateDictionary validationState, string prefix, object model)
{
}
}
Ussage:
public void ConfigureServices(IServiceCollection services)
{
services.DisableDefaultModelValidation();
}
Create empty model validator class.
public class EmptyModelValidator : IObjectModelValidator {
public void Validate(
ActionContext actionContext,
ValidationStateDictionary validationState,
string prefix,
object model) {
}
}
Replace DefaultModelValidator with EmptyModelValidator in configure services method.
services.Replace(
new ServiceDescriptor(typeof(IObjectModelValidator),
typeof(EmptyModelValidator),
ServiceLifetime.Singleton)
);
EmptyModelValidator not validates model so ModelState.IsValid always return false.
To turn off validation for everything inheriting a class:
var mvc = services.AddMvc(options =>
{
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(VMClass)));
}); // to avoid validation of the complete world by following VMClass refs
I've found some very strange behaviour of AutoMapper.
This simple code
internal class Program
{
private static void Main(string[] args)
{
Mapper.Initialize(cfg => { cfg.CreateMap<MyClass1, MyClass2>(); });
Mapper.Initialize(cfg => { cfg.CreateMap<MyClass3, MyClass4>(); });
var dto = new MyClass1();
Mapper.Map<MyClass1, MyClass2>(dto);
}
}
public class MyClass1
{
}
public class MyClass2
{
}
public class MyClass3
{
}
public class MyClass4
{
}
generates exception:
Missing type map configuration or unsupported mapping.
Mapping types: MyClass1 -> MyClass2 ConsoleApplication2.MyClass1 ->
ConsoleApplication2.MyClass2
but if change order of two initialize lines like this
Mapper.Initialize(cfg => { cfg.CreateMap<MyClass3, MyClass4>(); });
Mapper.Initialize(cfg => { cfg.CreateMap<MyClass1, MyClass2>(); });
everything is fine. What's wrong? What's going on?
Long story short, Jimmy temporarily moved away from the static stuff in AutoMapper in favour of instance based mapping. However, as Jimmy commented:
The static mapper is not removed, just the ability to call CreateMap anywhere in the code
The answer to question what is wrong is that you're trying to initialise twice, rather then once.
To answer the next question about how to have different configurations scattered across the code, you use Profile
To answer how you configure all of it, please see below:
For AutoMapper 5.1.1
There is a MapperConfigurationExpression
https://github.com/AutoMapper/AutoMapper/blob/master/src/AutoMapper/Configuration/MapperConfigurationExpression.cs
You can pass to the mapper, or Mapper takes a Action<IMapperConfigurationExpression>.
IMapperConfigurationExpression exposes this:
void AddProfile(Profile profile)
So you can pretty much do the same as below, but register everything against a IMapper interface, which is what it seems like 4.2.1 was heading towards.
For AutoMapper 4.2.1 (Short Intro to Profiles)
Here is an example profile:
using AutoMapper;
using TreasuryRecords.Database.Models;
using TreasuryRecords.Requests.Account.Models;
public class AccountMappings : Profile
{
protected override void Configure()
{
this.CreateMap<RegisterDto, Client>()
.ForMember(x => x.UserName, c => c.MapFrom(x => x.Email));
}
}
Here is an example of how I register my profiles:
using System;
using System.Linq;
using System.Reflection;
using AutoMapper;
using TreasuryRecords.Requests.Authenticate.Login;
public static class AutoMapperConfig
{
public static void Configure()
{
Assembly
.GetExecutingAssembly()
.RegisterConfigurations();
typeof(LoginRequest)
.Assembly
.RegisterConfigurations();
}
public static void RegisterConfigurations(this Assembly assembly)
{
var types = assembly.GetTypes();
var automapperProfiles = types
.Where(x => typeof(Profile).IsAssignableFrom(x))
.Select(Activator.CreateInstance)
.OfType<Profile>()
.ToList();
// so here you can pass in the instance of mapper
// I just use the static for ease
automapperProfiles.ForEach(Mapper.Configuration.AddProfile);
}
}
This is how I add it to DI:
public static void RegisterAutoMapper(this IUnityContainer container)
{
container.RegisterType<IMapper>(new InjectionFactory(_ => Mapper.Instance));
}
I'm using unity here, but it is pretty simple, just register Mapper.Instance against the interface IMapper.
Then I inject IMapper and use it like so:
this.mapper.Map<Client>(message.RegistrationDetails);
I'm trying to get autofac to inject an factory into a saga on creation, and I can't get it working. I've had no problem injecting the factory into a consumer so I know it's registered correctly, so I'm assuming I'm not registering the sagas correctly and autofac isn't building them up.
Here's my registration code:
var mapTypes = assembly.GetTypes()
.Where(type => type.Implements(typeof(SagaClassMapping<>)));
builder.Register(context => GetSessionFactory(mapTypes)).AsSelf().SingleInstance();
// register all sagas and consumers
builder.RegisterAssemblyTypes(assembly)
.Where(type => type.IsAssignableTo<ISaga>() || type.IsAssignableTo<IConsumer>())
.AsSelf();
builder
.RegisterGeneric(typeof(NHibernateSagaRepository<>))
.As(typeof(ISagaRepository<>))
.SingleInstance();
builder
.Register(context => ServiceBusFactory.New(sbc =>
{
sbc.UseLog4Net();
var queueUri = new Uri(ConfigurationManager.AppSettings["ReceiveQueue"]);
var scope = context.Resolve<ILifetimeScope>();
sbc.UseRabbitMq(transportConfig =>
transportConfig.ConfigureHost(queueUri, hostConfig =>
{
hostConfig.SetUsername(ConfigurationManager.AppSettings["busUser"]);
hostConfig.SetPassword(ConfigurationManager.AppSettings["busPassword"]);
}));
sbc.ReceiveFrom(queueUri);
sbc.Subscribe(cfg => cfg.LoadFrom(scope));
}))
.SingleInstance();
The saga itself is pretty standard
public class MySaga : SagaStateMachine<MySaga>, ISaga
{
public Guid CorrelationId { get; private set; }
public Func<MyObject> ObjectBuilder { get; set; }
public MySaga() { }
public MySaga(Guid correlationId)
{
CorrelationId = correlationId;
}
Static MySaga()
{
Define(() =>
{ .... }
}
I've tried adding the Func<MyObject> to a constructor, but it's not hit, it does work in a consumer so I know Autofac can build a Func<MyObject>. I also tried using property injection with no luck:
builder.RegisterAssemblyTypes(assembly)
.Where(type => type.IsAssignableTo<ISaga>() || type.IsAssignableTo<IConsumer>())
.PropertiesAutowired()
.AsSelf();
and
builder.RegisterType<MySaga>()
.OnActivated(arg => arg.Instance.MyBuilder =
arg.Context.Resolve<Func<MyObject>>())
.AsSelf();
Any help on what I'm doing wrong would be appreciated.
I got a reply from Chris Patterson on the masstransit-discuss group that pointed out I was probably doing it wrong.
Automatonymous is a better choice if you have dependencies, since the state machine and the state itself are separate classes.
Injecting dependencies into a class hydrated via NHibernate is never going to end well. There are a couple of helper classes that can be used to perform property-injection into the saga after it is loaded from NHibernate, the decorating saga repository I think has been posted here.
Here is the example of the injecting repository for Magnum:
https://github.com/MassTransit/MassTransit/blob/master/src/MassTransit.Tests/Saga/Injecting_Specs.cs
Given it's NHibernate that's hydrating the object I should be looking there for the hook. I've got a workaround for my current issue, but I'll post an answer here if/when I find one.
old question but it took a while for us to figure out a suitable way to wiring up sagas and autofac and then adding nhibernate on top of that.
First create a wiring class (property injection)
public class MySagaRepository<TSaga> : ISagaRepository<TSaga> where TSaga : class, ISaga
{
private ISagaRepository<TSaga> _inner;
private readonly IComponentContext _context;
public MySagaRepository(ISagaRepository<TSaga> inner, IComponentContext context)
{
_inner = inner;
_context = context;
}
public IEnumerable<Action<IConsumeContext<TMessage>>> GetSaga<TMessage>(IConsumeContext<TMessage> context, Guid sagaId, InstanceHandlerSelector<TSaga, TMessage> selector, ISagaPolicy<TSaga, TMessage> policy) where TMessage : class
{
return _inner.GetSaga(context, sagaId, (saga, message) =>
{
_context.InjectProperties(saga);
return selector(saga, message);
}, policy);
}
public IEnumerable<Guid> Find(ISagaFilter<TSaga> filter)
{
return _inner.Find(filter);
}
public IEnumerable<TSaga> Where(ISagaFilter<TSaga> filter)
{
return _inner.Where(filter).Select(x =>
{
_context.InjectProperties<TSaga>(x);
return x;
});
}
public IEnumerable<TResult> Where<TResult>(ISagaFilter<TSaga> filter, Func<TSaga, TResult> transformer)
{
return _inner.Where(filter, x =>
{
_context.InjectProperties(x);
return transformer(x);
});
}
public IEnumerable<TResult> Select<TResult>(Func<TSaga, TResult> transformer)
{
return _inner.Select(x =>
{
_context.InjectProperties(x);
return transformer(x);
});
}
}
Then wire it
builder.RegisterGeneric(typeof(NHibernateSagaRepository<>)).Named("innerRepo", typeof(ISagaRepository<>));
builder.RegisterGenericDecorator(typeof(MySagaRepository<>), typeof(ISagaRepository<>), fromKey: "innerRepo");
For the persistance it was just a matter of
public class SagaPersistenceHandler
{
public static ISessionFactory CreateSessionFactory()
{
var provider = new SqlServerSessionFactoryProvider(ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString, new[]
{
typeof (MySagaMap)
});
return provider.GetSessionFactory();
}
}
Now wire that
builder.Register(c => SagaPersistenceHandler.CreateSessionFactory()).As<ISessionFactory>();
and the saga mapping to the saga (not included)
public MySagaMap()
{
SchemaAction(NHibernate.Mapping.ByCode.SchemaAction.None);
Table("dbo.[tTable]");
Property(x => x.Id);
Property(x => x.CloseDate);
}
}
All thats left is to register your saga
builder.RegisterType<MySaga>().AsSelf();
worked well (credits goes to #erikkallen)
I made an adapter for Autofac to register and configure MT state machine sagas.
The repo is here.
It is available on nuget.org.
You can register your consumers and state machines by calling:
builder.RegisterSagaStateMachines(sagasAssembly); // register all state machines
builder.RegisterConsumers(consumersAssembly); // register consumers (standard MassTransit Autofac integration)
Note that RegisterConsumers is the standard MassTransit.Autofac extension.
In your endpoint configuration you need to call the configuration like this:
x.ReceiveEndpoint(queueName, c =>
{
c.LoadConsumers(container);
c.LoadStateMachineSagas(container);
});
Pre-condition is that you have to register ISagaRepository implementations as well.
I am trying to create a custom resolver for automapper which needs to access one of my data repositories to retreive the logged in users account.
Here is my code so far...
public class FollowingResolver : ValueResolver<Audio, bool>
{
readonly IIdentityTasks identityTasks;
public FollowingResolver(IIdentityTasks identitTasks)
{
this.identityTasks = identitTasks;
}
protected override bool ResolveCore(Audio source)
{
var user = identityTasks.GetCurrentIdentity();
if (user != null)
return user.IsFollowingUser(source.DJAccount);
return false;
}
}
However I am getting this error:
FollowingResolver' does not have a default constructor
I have tried adding a default contrstructor but my repository never gets initialised then.
This is my autoampper initialisation code:
public static void Configure(IWindsorContainer container)
{
Mapper.Reset();
Mapper.Initialize(x =>
{
x.AddProfile<AccountProfile>();
x.AddProfile<AudioProfile>();
x.ConstructServicesUsing(container.Resolve);
});
Mapper.AssertConfigurationIsValid();
}
Am I missing something, is it even possible to do it like this or am I missing the boat here?
Found the solution shorlty after...i was forgetting to add my resolvers as an IoC container.
Works great now!
I was getting the same error using Castle Windsor while trying to inject a service.
I had to add:
Mapper.Initialize(map =>
{
map.ConstructServicesUsing(_container.Resolve);
});
before Mapper.CreateMap calls.
Created a ValueResolverInstaller like this:
public class ValueResolverInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IValueResolver>()
.LifestyleTransient());
}
}
and the ValueResolver itself:
public class DivergencesResolver : ValueResolver<MyClass, int>
{
private AssessmentService assessmentService;
public DivergencesResolver(AssessmentService assessmentService)
{
this.assessmentService = assessmentService;
}
protected override int ResolveCore(MyClass c)
{
return assessmentService.GetAssessmentDivergences(c.AssessmentId).Count();
}
}