I've derived a small test from Microsoft article on testing EF with moq: Testing with a mocking framework
namespace TestingDemo
{
public class BloggingContext : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public string Name { get; set; }
}
[TestClass]
public class QueryTests
{
[TestMethod]
public void Test()
{
var data = new List<Blog> { new Blog { Name = "AAA" } }.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
//var a = mockSet.Object;
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var b = mockSet.Object.ToList();
}
}
}
It works fine until I uncomment the line var a = mockSet.Object;
After this I end up with exception on the line var b = mockSet.Object.ToList();
System.NotImplementedException: 'The member 'IEnumerable.GetEnumerator' has not been implemented on type 'DbSet`1Proxy' which inherits from 'DbSet`1'. Test doubles for 'DbSet`1' must provide implementations of methods and properties that are used.'
Am i missing something?
UPD:
Using Moq 4.10.1, EF 6.2.0, VS Community 2019 16.6.1, .NET 4.7.2
You actually need to create DbContext mock as well like:
var context = new Mock<BloggingDbContext>();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
context.Setup(c => c.Blogs).Returns(mockSet.Object);
var blogs = context.Object.Blogs.ToList(); // usage
The problem is you are adding the IQueryable<T> setups after you have obtained an instance of the mocked object (mockSet.Object); it won't work and that's also why it works fine once you comment out the assignment to a. You should only retrieve the mocked object once you have added setups for the behaviour you want to consume.
Interestingly once you have added those setups you can change them (override using another setup) at any time. You've just got to do the initial registration prior to retrieving the mocked object. I do it all the time with my system mock libraries.
I am using AutoMapper profiles for mapping entities. In one of the mapping profiles I need to call a service method. I m trying to inject the service using profile constructor but then I m not able to register/add it. Any ideas how can I handle this?
public class HistoryProfile : Profile
{
private readonly MappingService _mappingService;
public HistoryProfile(MappingService mappingService)
{
_mappingService = mappingService;
this.CreateMap<HistoryCHR, History>()
.ForMember(h => h.BirthDate, hisChr => hisChr.MapFrom(x => x.DateOfBirth))
.....................
}
}
private static void InitializeSpecificProfiles()
{
Mapper.Initialize(
cfg =>
{
cfg.AddProfile(new HistoryProfile());
});
}
I've upgraded Automapper from 4.2.1 to 5.0.0. I'm using the static API in a WebApi2 project and I'm trying to get the mapping to work, so I tried following this SO answer.
So I changed the code to the following:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(main =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
config.AssertConfigurationIsValid();
});
}
}
The above is called from Global.asax.
However, I get exception:
Mapper not initialized. Call Initialize with appropriate configuration.
What is the correct way to initialize Automapper, and do I need to change all my controllers now for mapping?
EDIT1
Firstly, the code above must be:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.Configuration.AssertConfigurationIsValid();
Secondly, the problem might be in the following method which I use to ignore missing properties:
public static IMappingExpression<TSource, TDestination> IgnoreUnmapped<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.Configuration.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
I'm assuming 'Mapper.Configuration' is not yet configured because the above method is called within Initialize which configures the mapping.
Is there an existing method within Automapper itself which I can use instead of the above?
EDIT2
Would the following syntax work?
cfg.CreateMap<MyModel, MyDto>().ReverseMap().ForAllMembers(opt => opt.Ignore());
Actually your code does nothing now. You have to change it like this:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.AssertConfigurationIsValid();
}
}
UPD (after EDIT1):
Try to use expression.TypeMap instead of Mapper.Configuration.FindTypeMapFor<TSource, TDestination>()
Maybe this helps:
Setup the Configuration:
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
Then where the mapping should take place:
var mapper = new Mapper(config);
OrderDto dto = mapper.Map<OrderDto>(order);
You could also expose the 'config' as a static property, and use that in your project. There's also an alternative to create a static 'Mapper' property that is configured. Then you can use that static 'Mapper' property in your project.
For example I have validator with two validation rules:
// Rule 1
RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) != 0)
.WithMessage("User with provided Email was not found in database!");
// Rule 2
RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) >= 1)
.WithMessage("There are multiple users with provided Email in database!");
As you can see there are two calls to database with same method. How do I call it once and reuse the data for other rules?
Another issue when displaying error messages:
RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) >= 1)
.WithMessage("There are multiple users with following Email '{0}' in database!",
(model, email) => { return email; });
Is there a better way to display error messages not all the time writing those lambda expressions to retrieve property? Like saving model somewhere and then use it later. Simple and easy to implement solutions would be nice!
For #1, There isn't a way to do this I'm afraid. Validators are designed to be stateless so they can be reused across threads (in fact, it's highly recommended you create validator instances as singletons as they're very expensive to instantiate. The MVC integration does this by default). Don't mess with static fields as you'll run into threading issues.
(Edit: in this particular simple case you can just combine the rules into a single call to Must, but in general you can't share state between rules)
For #2, This depends on the property validator you're using. Most property validators actually allow you to use the {PropertyValue} placeholder, and the value will automatically be inserted. However, in this case you're using the "Must" validator (PredicateValidator) which doesn't support placeholders.
I have a list of which validators support custom placeholders here: https://github.com/JeremySkinner/FluentValidation/wiki/c.-Built-In-Validators
Just came across this question while looking for a better way ;)
Another way is to override the ValidateAsync and Validate methods and store the result in a local field which can be accessed by the rules as follows:
public class MyValidator : AbstractValidator<MyCommand>
{
User _user = User.Empty;
public MyValidator()
{
RuleFor(o => o.Email)
.Must((_) => !_user.IsEmpty)
.WithMessage("User with provided Email was not found in database!");
// Rule 2
//other rules which can check _user
}
public override async Task<ValidationResult> ValidateAsync(ValidationContext<MyCommand> context, CancellationToken cancellation = default)
{
var cmd = context.InstanceToValidate;
// you could wrap in a try block if this throws, here I'm assuming empty user
_user = await _repository.GetUser(cmd.Email);
return await base.ValidateAsync(context, cancellation);
}
public override ValidationResult Validate(ValidationContext<SubmitDecisionCommand> context) => ValidateAsync(context).Result;
}
Part 1
You want to reduce database calls from 2 to 1, so you need to use field to save database call result, because validator rules code actually work in "runtime".
Validator class:
public class MyValidator : Validator<UserAccount>
{
private int? _countOfExistingMails;
private string _currentEmail;
private object locker = new object();
public MyValidator()
{
CallEmailValidations();
// other rules...
}
}
Here is separate method for mail validation calls. As far as Must take expression as parameter, you can pass method name with it's arguments:
public void CallEmailValidations()
{
RuleFor(o => o.Email).Must(x => EmailValidation(x, 0))
.WithMessage("User with provided Email was not found in database!");
RuleFor(o => o.Email).Must(x => EmailValidation(x, 1))
.WithMessage("There are multiple users with provided Email in database!");
}
And validation method's body itself:
public bool EmailValidation(string email, int requiredCount)
{
var isValid = false;
lock(locker)
{
if (email != _currentEmail || _currentEmail == null)
{
_currentEmail = email;
_countOfExistingMails = (int)GetDataDataFromDB(email);
}
if (requiredCount == 0)
{
isValid = _countOfExistingMails != 0; // Rule 1
}
else if (requiredCount == 1)
{
isValid = _countOfExistingMails <= 1; // Rule 2
}
}
// Rule N...
return isValid;
}
UPDATE:
This code works, but better approach is to implement caching in data access layer method.
Part 2
Here is rewritten rule:
RuleFor(o => o.Email).Must((email) => GetDataDataFromDB(email) >= 1)
.WithMessage("There are multiple users with following Email '{0}' in database!", m => m.Email)
From "C# in depth":
When the lambda expression only needs a single parameter, and that
parameter can be implicitly typed, C# 3 allows you to omit the
parentheses, so it now has this form
GOTCHAS:
Do not pass explicitly this to lambda-expressions. It could cause preformance issues as I know. There is no reason to create extra-closure.
I suppose you use DataContext in some form inside GetDataDataFromDB method. So you have to control lifetime of your context, because validator object instantiated as singletone.
What you can do is to use WhenAsync. I have created an extension method to make things easier.
public static class ValidatorExtensions
{
public static void ResolveDataAsync<TEntity, TData>(
this AbstractValidator<TEntity> validator,
Func<TEntity, CancellationToken, Task<TData>> resolver,
Action<ValueAccessor<TData>> continuation)
{
TData data = default;
var isInitialized = false;
var valueAccessor = new ValueAccessor<TData>(() =>
{
if (!isInitialized)
{
throw new InvalidOperationException("Value is not initialized at this point.");
}
return data;
});
validator.WhenAsync(async (entity, token) =>
{
data = await resolver(entity, token);
return isInitialized = true;
},
() => continuation(valueAccessor));
}
}
public class ValueAccessor<T>
{
private readonly Func<T> _accessor;
public ValueAccessor([NotNull] Func<T> accessor)
{
_accessor = accessor ?? throw new ArgumentNullException(nameof(accessor));
}
public T Value => _accessor();
}
Usage:
public class ItemCreateCommandValidator : AbstractValidator<ItemCreateCommand>
{
private readonly ICategoryRepository _categoryRepository;
public ItemCreateCommandValidator(ICategoryRepository categoryRepository)
{
_categoryRepository = categoryRepository;
this.ResolveDataAsync(CategoryResolver, data =>
{
RuleFor(x => x.CategoryIds)
.NotEmpty()
.ForEach(subcategoryRule => subcategoryRule
.Must(x => data.Value.ContainsKey(x))
.WithMessage((_, id) => $"Category with id {id} not found."));
});
}
private Func<ItemCreateCommand, CancellationToken, Task<Dictionary<int, Category>>> CategoryResolver =>
async (command, token) =>
{
var categories = await _categoryRepository.GetByIdsAsync(command.SubcategoryIds, token);
return categories.ToDictionary(x => x.Id);
};
}
Works fine to me, but there are a few GOTCHAS:
The validator usually have to be defined as Scoped or Transient (Scoped is better for performance) in order to be compatible with lifecycle of it's dependencies (e.g. repository passed in constructor).
You can't access the data.Value right inside ResolveDataAsync callback. This is because the value is not initialized by that time. By this time validator is in creation phase and ValidateAsync method was not called => nothing to validate => value can't be accessed.
It can be used only in AbstractValidator methods:
this.ResolveDataAsync(CategoryResolver, data =>
{
var value = data.Value; // Throws InvalidOperationException
RuleFor(x => x.CategoryIds)
.NotEmpty()
.ForEach(subcategoryRule => subcategoryRule
.Must(data.Value.ContainsKey) // Also throws
.WithMessage((_, id) => $"Category with id {id} not found."));
});
These gotchas also occur with other approaches, such as overriding the ValidateAsync method, and there is not much you can do about them.
You can also call ResolveDataAsync with different resolvers depending on condition when using WhenAsync, UnlessAsync. This will help you not to load data that is not needed in all cases every time:
WhenAsync(myCondition1, () => this.ResolveDataAsync(myResolver1, data => { ... }))
UnlessAsync(myCondition2, () => this.ResolveDataAsync(myResolver2, data => { ... }))
I used following statement to decorate all my ICommandHandlers<> with Decorator1<>:
ObjectFactory.Configure(x =>
{
x.For(typeof(ICommandHandler<>)).DecorateAllWith(typeof(Decorator1<>));
});
But because Decorator1<> implements ICommandHandlers<>, the Decorator1<> class decorates itself too.
So, the problem is that the Decorator1 registers inadvertently, when I register all the ICommandHandler<>'s.
How can I filter DecorateWithAll()to decorate all ICommandHandler<>, except Decorator1<>?
ObjectFactory is obsolete.
You can exclude Decorator1<> from being registered as a vanilla ICommandHandler<> with code like this:
var container = new Container(config =>
{
config.Scan(scanner =>
{
scanner.AssemblyContainingType(typeof(ICommandHandler<>));
scanner.Exclude(t => t == typeof(Decorator1<>));
scanner.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
});
config.For(typeof(ICommandHandler<>)).DecorateAllWith(typeof(Decorator1<>));
});
UPDATE
For multiple modules you can use The Registry DSL to compose a portion of your application
using StructureMap;
using StructureMap.Configuration.DSL;
public class CommandHandlerRegistry : Registry
{
public CommandHandlerRegistry()
{
Scan(scanner =>
{
scanner.AssemblyContainingType(typeof(ICommandHandler<>));
scanner.Exclude(t => t == typeof(Decorator1<>));
scanner.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
});
For(typeof(ICommandHandler<>)).DecorateAllWith(typeof(Decorator1<>));
}
}
Only the Composition Root needs to be aware of all the Registry implementations.
var container = new Container(config =>
{
config.AddRegistry<CommandHandlerRegistry>();
});
You even have the option of finding all Registry instances at runtime (you still need to ensure all the required assemblies are loaded)
var container = new Container(config =>
{
var registries = (
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.DefinedTypes
where typeof(Registry).IsAssignableFrom(type)
where !type.IsAbstract
where !type.Namespace.StartsWith("StructureMap")
select Activator.CreateInstance(type))
.Cast<Registry>();
foreach (var registry in registries)
{
config.AddRegistry(registry);
}
});