I used following code to decorate all my ICommandHandler<> with Decorator1<>, using structuremap:
ObjectFactory.Configure(x =>
{
....
x.For(typeof(ICommandHandler<>))
.DecorateAllWith(typeof(Decorator1<>),
instance => instance.ReturnedType.Assembly == typeof(Class1).Assembly);
}
Is there any way to exclude one of my concrete CommandHandlers from this decoration?
Related
I am new at FluentValidation in general. I am writing a validator, and I can't seem to figure out how to do a .WithMessage with a WarningMessage instead of an ErrorMessage and use params.
I can do this:
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage("Some error message {0}", x => x.MyError);
But that sets it as an ErrorMessage and I need a Warning Message. I tried this but no dice:
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage(new WarningMessage("Some warning message {0}", x => x.MyError));
There's no direct implementation of Warning message in FluentValidation (or Mvc's ModelState).
In FluentValidation, you've got a
WithState() extension method that you can use for this purpose.
First, you can create an enum
public enum ValidationErrorLevel
{
Error,
Warning
}
Then, you can write a few extension methods, in a static class, to use warnings and errors.
One to use in your Validator classes
public static IRuleBuilderOptions<T, TProperty> AsWarning<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule)
{
return rule.WithState<T, TProperty>(x => ValidationErrorLevel.Warning);
}
You can use it this way
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage("Some error message {0}", x => x.MyError)
.AsWarning();
Few other to use to manage your validation results
public static IList<ValidationFailure> GetWarnings(this ValidationResult result)
{
return result.Errors.Where(m => m.CustomState != null && Convert.ToInt32(m.CustomState) == (int)ValidationErrorLevel.Warning).ToList();
}
public static IList<ValidationFailure> GetErrors(this ValidationResult result)
{
return result.Errors.Except(result.GetWarnings()).ToList();
}
Then, you should use
validator.Validate(<someclass>)
instead of
validator.ValidateAndThrow(<someclass>)
var results = validator.Validate(<someclass>);
You can then put errors in ModelState, for example
foreach (var error in result.GetErrors()) {
ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}
and do something else for Warnings, for example put it in TempData
TempData["warnings"] = new List<string>();
((List<string>)TempData[WarningMessageKey]).AddRange(result.GetWarnings().Select(warning => warning.ErrorMessage));
Then you can display them like any other TempData.
Validation (in general) attaches errors to the ModelState - which in itself is an object so if you put a break point on the if(ModelState.IsValid) line you can look at the other properties of the ModelState.
The thing with errors (and error messages) they are either there or not. If there is no issue there is no error message and if there is an issue an error message will be added.
If I were you, if the model state is not valid in your controller I would get all error messages using
var allErrors = ModelState.Values.SelectMany(v => v.Errors);
and then iterate over each one and decide what you want to do with it.
I hope this answers or at least helps as I am not totally sure what you are asking / trying to get to.
We're using MvvmCross in our app, and using the MvxSimpleIoCContainer
In the app startup, we register all of our Migrations.
it's easy do do since all migrations inherit from IMigration
typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.RegisterAsLazySingleton();
After the migrations are registered, we need to run them consecutively, and therefore the MigrationRunner looks a little something like this.
Mvx.Resolve<IMigrationRunner>().RunAll(SystemRole.Client, new List<IMigration>
{
Mvx.IocConstruct<Migration001>(),
Mvx.IocConstruct<Migration002>()
});
as you can see, I'm explicitely constructing each Migration using Mvx. This get's tedious and is prone to mistakes when a bunch of migrations end up in the app.
What I'd prefer to be able to do is resolve the entire collection in one fell swoop, and not have to touch it every time I create a new Migration.
Is there a way to do this via MvvmCross?
Pseudo Code
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client, Mvx.ResolveAll<IMigration>());
I would use LINQ to get the list of types. Unfortunately there's no way to get a list of registered types, so you'll have to enumerate the types again like you do for registration. You can even sort by type name. Now that you have a list of types, you can create a new list of instantiated/resolved types to pass into RunAll(). Something like:
var migrationTypes = typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.OrderBy(t => t.Name)
.ToList();
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client,
migrationTypes.Select(t => Mvx.Resolve(t)).ToList());
This is "browser" code, so no guarantees, but you get the gist.
Ok, so reflection is the answer to this problem for now, and eventually, I'd like to either extend our custom MvxServiceLocator : IServiceLocator to include something like
public IEnumerable<object> GetAllInstances(Type serviceType){...}
but for now I've just got a RunMigrations() method in the app
private void RunMigrations()
{
var migrationType = typeof (IMigration); // IMigration is in a separate assembly
var migrations = GetType().Assembly
.GetTypes()
.Where(
t => migrationType.IsAssignableFrom(t) && !t.IsAbstract)
.OrderBy(t => t.Name)
.Select(m => _serviceLocator.GetInstance(m) as IMigration)
.ToList();
var migrationRunner = new MigrationRunner(Mvx.Resolve<IDbProvider>());
migrationRunner.RunAll(SystemRole.Client, migrations);
}
where _serviceLocator.GetInstance(m) just lives in our custom MvxServiceLocator
public object GetInstance(Type serviceType)
{
return _ioCProvider.Resolve(serviceType);
}
Edit: here's how I extended our service locator wrapper.
public class MvxServiceLocator : IServiceLocator
{
private readonly IMvxIoCProvider _ioCProvider;
public MvxServiceLocator(IMvxIoCProvider ioCProvider)
{
_ioCProvider = ioCProvider;
}
public IEnumerable<TService> GetAllInstances<TService>()
{
var serviceType = typeof(TService);
var registrations = GetType().Assembly
.GetTypes()
.Where(
t => serviceType.IsAssignableFrom(t) && !t.IsAbstract)
.Select(m => (TService)_ioCProvider.Resolve(m));
return registrations;
}
}
Consider the following Fluent configuration;
FluentConfiguration config = Fluently.Configure();
config.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ConnectionString(ConfigurationManager.ConnectionStrings[dbKey].ConnectionString));
// MemberMap is in the same assembly as this class, contains
// maps for member and role entities as part of a default
// membership service provider
MappingAssemblies.Add(typeof(MemberMap).Assembly);
foreach (Assembly mappingAssembly in MappingAssemblies)
{
// For each assembly that has been added to MappingAssemblies
// we add to the current mapping configuration. This allows
// me to drop this helper into any solution and it provide
// standardized membership abilities AS WELL AS manage
// the solutions unique ClassMaps
config.Mappings(m =>
m.FluentMappings.AddFromAssembly(mappingAssembly)
);
}
if (exportSchema)
{
config.ExposeConfiguration(cfg =>
{
new SchemaExport(cfg)
.Create(true, true);
}
);
}
_sessionFactory = config.BuildSessionFactory();
This logic is held within a static class I call from within my Global.asax on application startup. The startup configuration will look something similar to;
Database.MappingAssemblies.Add(typeof(PageContentMap).Assembly);
// This is the method detailed above
Database.FluentConfigureSessionFactory("MySolutionsDb", true);
So the idea is that I have packaged my Member and Role entity objects into the same assembly as the Database helper object so that any solution I care to create can instantly gain my standardized membership abilities as well as being able to simply create its own solution specific ClassMaps and add them to the configuration object.
The issue is, the familiar call to;
config.Mappings(m =>
m.FluentMappings.AddFromAssembly(mappingAssembly)
);
only seems to be able to deal with a single assembly. Doesn't matter what is added to the list, only the last assembly added will be mapped. As an alternative to the above, I have tried holding a reference to MappingConfiguration (which is what 'm' stands for in the config.Mappings(m => ) ) but this did not work either. It is obvious that such a call to m.FluentMappings.AddFromAssembly or indeed any of the FluentMappings.Add methods will overwrite what is previously there but surely there is a way of getting this done ? It doesn't seem like that much of a 'weird' requirement.
Old question, but I managed to solve it after looking at this, so I'll try to answer it. This is how I did (the .ForEach() is an extension from NHibernate.Linq):
config.Mappings(m => MappingAssemblies.ForEach(a => m.FluentMappings.AddFromAssembly(a)))
I had to do it for auto-mapped stuff as well, and there the syntax is a bit different. I have an interface that marks all classes I want to automap:
config.Mappings(m =>
m.AutoMappings.Add(AutoMap.Assemblies(MappingAssemblies.ToArray())
.Where(x => x.GetInterfaces().Contains(typeof(IAutoMappedEntity)))))
Also, I don't have a "MappingAssemblies" that I set manually, I took the lazy approach of just including all my assemblies, so my config looks like this (using SQLite, this is from a test project):
var MappingAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith("MyCompanyName."));
Configuration configuration;
var sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
// This adds fluent mappings
.Mappings(m => MappingAssemblies.ForEach(a => m.FluentMappings.AddFromAssembly(a)))
// This adds automapped classes using specific configuration
.Mappings(m => m.AutoMappings.Add(AutoMap.Assemblies(new MyAutomapConfiguration(), MappingAssemblies)))
// This adds automapped classes that just use my magic interface
.Mappings(m => m.AutoMappings.Add(AutoMap.Assemblies(MappingAssemblies.ToArray()).Where(x => x.GetInterfaces().Contains(typeof(IAutoMappedEntity)))))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();
i'm trying to create a filter, using fluent nH (1.2) automapping with nH 2.1.2.
I've followed the example here, but I keep getting the exception:
filter-def for filter named 'DateFilter' was never used to filter classes nor collections..
the filter class:
public class DateFilter : FilterDefinition
{
public DateFilter()
{
WithName(Consts.FilterConsts.DATE_FILTER)
.AddParameter("date", NHibernate.NHibernateUtil.DateTime)
.WithCondition("DATEPART(dayofyear,EntityTime) = DATEPART(dayofyear, :date)")
;
}
}
and in the mapping override:
mapping.HasMany(x => x.Stuff)
.LazyLoad()
.ReadOnly()
.ApplyFilter<DateFilter>();
here's my configuration code.
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.DefaultSchema("dbo") //set default schema to enable full-qualified queries
.AdoNetBatchSize(batchSize > 0 ? batchSize : 1)
.UseReflectionOptimizer()
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.Cache(c => c.UseQueryCache()
.ProviderClass(
isWeb ? typeof(NHibernate.Caches.SysCache2.SysCacheProvider).AssemblyQualifiedName //in web environment- use sysCache2
: typeof(NHibernate.Cache.HashtableCacheProvider).AssemblyQualifiedName //in dev environmet- use stupid cache
))
)
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Domain.Entity>(cfg) //automapping the domain entities
.IncludeBase<Domain.SomethingBase>() //ensure that although SomethingBase is a base class, map it as well. this enables us to store all Something sub-classes in the same table
.IncludeBase<Domain.OrOtherBase>() //create a table for the abstract 'OrOtherBase' class
.UseOverridesFromAssemblyOf<MappingOverrides.MappingOverride>()
.Conventions.Add(DefaultCascade.All()) //make sure that all saves are cascaded (i.e when we save a zone, its queues are saved as well)
.Conventions.AddFromAssemblyOf<IdGenerationWithHiLoConvention>()
))
.Mappings(m => m.FluentMappings.Add(typeof(DateFilter)));
if I move the line before the automapping part, I get the exception:
NHibernate.MappingException: filter-def for filter named 'DateFilter' was not found.
can anybody tell me what I'm doing wrong?
OK, so I figured this out. When you add the mappings separately like that, they end up in different mappings and it will either complain that you never use the filter or complain that it can't find the filter, because it's not looking both places. The solution is to add it directly to the automap, so in your case like:
//other stuff up here
.Mappings(m => m.AutoMappings.Add(() => {
var a = AutoMap.AssemblyOf<Domain.Entity>(cfg)
.IncludeBase<Domain.SomethingBase>() //and also cascades and conventions and stuff
a.Add(typeof(DateFilter));
return a;
}));
Kinda gross because .Add() isn't fluent, but it does work.
I'm using Fluent NHibernate in order to auto map my entities.
This is the code I'm using for the auto mapping:
new AutoPersistenceModel()
.AddEntityAssembly(Assembly.GetAssembly(typeof(Entity)))
.Where(type => type.Namespace.Contains("Domain") && type.BaseType != null && type.BaseType.Name.StartsWith("DomainEntity") && type.BaseType.IsGenericType == true)
.WithSetup(s => s.IsBaseType = (type => type.Name.StartsWith("DomainEntity") && type.IsGenericType == true))
.ConventionDiscovery.Add(
ConventionBuilder.Id.Always(x => x.GeneratedBy.Increment())
);
This works just fine. But now I need to have Eager Loading in one single object of my domain. Found this answer. But when I add the line .ForTypesThatDeriveFrom<IEagerLoading>(map => map.Not.LazyLoad()) to the code and run it I get the following exception:
Error while trying to build the Mapping Document for IEagerLoading
Notice that I'm using an interface (IEagerLoading) to mark the objects that I want eager load.
Can anyone help how to do this? Remember that I want to keep the auto mapping functionality.
Thanks
The problem you're hitting is that ForTypesThatDeriveFrom<T> is a bit misleadingly named, and that it really means ForMappingsOf<T>, so it's trying to find a ClassMap<IEagerLoading> which obviously doesn't exist.
I believe you should be able to handle this with a custom IClassConvention. This is off the top of my head, but should work:
public class EagerLoadingConvention : IClassConvention
{
public bool Accept(IClassMap target)
{
return GetType().GetInterfaces().Contains(typeof(IEagerLoading));
}
public void Apply(IClassMap target)
{
target.Not.LazyLoad();
}
}