I am trying to write method in AutoMapper class. My situation is as below.
ClinicListVm = AutoMapperConfig.mapper
.Map<GetClinicsByUserName_Result, ClinicListViewModel>(c);
I already map ClinicListViewModel with GetClinicsByUserName_Result now I want to manipulate one attribute my destination model as below.
ClinicListVm.ProgressBarCssClass = string.Empty;
if (ClinicListVm.PercentComplete == 100)
{
ClinicListVm.ProgressBarCssClass = "progress-bar-success";
}
else if (DateTime.Now.Subtract(ClinicListVm.BillerStartDateTime ?? DateTime.Now).TotalDays > MaxDaysInDataEntry)
{
// partial is a warning color
ClinicListVm.ProgressBarCssClass = "progress-bar-partial";
}
So, How I can include this code it self in automapper class.
Thanks
I personally think #Rajmond Burgaj's anwer is a good one. Using a ResolveUsing(), or a custom resolver, is in this case a good way to go.
However, I just want to share a more pragmatic alternative that may result in the same and might help you in the future. You may want to consider abstracting the conditional if...then... logic to a seperate function. For example:
private string DetermineProgressBarState(SourceClass source)
{
if (source.PercentComplete == 100) return "progress-bar-success";
var MaxDaysInDataEntry = 42; // missing in your sample
return DateTime.Now.Subtract(source.BillerStartDateTime ?? DateTime.Now).TotalDays > MaxDaysInDataEntry
? "progress-bar-partial"
: null;
}
With that, you can easily map it in your configuration, like so:
Mapper.Initialize((config =>
{
config.CreateMap<SourceClass, TargetClass>()
.ForMember(
dest => dest.ProgressBarCssClass,
opt => opt.MapFrom(src => DetermineProgressBarState(src))
);
}));
This runs exactly as you'd expect. Here is the full sample, as a XUnit test (but you'll get the picture):
public class SourceClass
{
public int PercentComplete { get; set; }
public DateTime? BillerStartDateTime { get; set; }
}
public class TargetClass
{
public string ProgressBarCssClass { get; set; }
}
public class UnitTest1
{
[Fact]
public void Test1()
{
// arrange - configure the automapper
Mapper.Initialize((config =>
{
config.CreateMap<SourceClass, TargetClass>()
.ForMember(
dest => dest.ProgressBarCssClass,
opt => opt.MapFrom(src => DetermineProgressBarState(src))
);
}));
// arrange - create a
var source = new SourceClass() { PercentComplete = 100 };
// act - map source to target
var target = Mapper.Map<TargetClass>(source);
// assert - verify the result
target.ProgressBarCssClass.Should().Be("progress-bar-success");
}
private string DetermineProgressBarState(SourceClass source)
{
if (source.PercentComplete == 100) return "progress-bar-success";
var MaxDaysInDataEntry = 42; // missing in your sample
return DateTime.Now.Subtract(source.BillerStartDateTime ?? DateTime.Now).TotalDays > MaxDaysInDataEntry
? "progress-bar-partial"
: null;
}
}
But... I totally agree with the comment that AutoMapper is not the place to put this logic in. The reason is that you're actually defining markup logic (html/css data) in your mapping logic. This is better placed inside your view, presumably your .cshtml.
With that in mind, should you choose to refactor this somehow "the proper way" in the future, the DetermineProgressBarState() function will still be helpful. You'll just move the code from your AutoMapper config to your controller (or .cshtml helper).
If you want to execute another function while mapping you can use ResolveUsing method and put your logic in there.
CreateMap<GetClinicsByUserName_Result, ClinicListViewModel>()
.ForMember(d => d.ProgressBarCssClass , o => o.ResolveUsing(s =>
{
//Do your custom logic here.
}))
.AfterMap((src, dest) =>
{
//Do your logical after mapping has been done
dest.ProgressBarCssClass = string.Empty;
if (dest.PercentComplete == 100)
{
dest.ProgressBarCssClass = "progress-bar-success";
}
else if (DateTime.Now.Subtract(dest.BillerStartDateTime ??
DateTime.Now).TotalDays > MaxDaysInDataEntry)
{
// partial is a warning color
dest.ProgressBarCssClass = "progress-bar-partial";
}
});
Not quite sure where that MaxDaysInDataEntry is coming from but if it can be retrieved inside this method your are good to go this way otherwise if MaxDaysInDataEntry is a variable generated from business logic elsewhere then it is a problem!
Anyway let me know if it help and where that variable is coming from!
Related
Imagine you have a class like :
public enum Kind { Kind1, Kind2 }
public class MyForm
{
public string Kind { get; set; }
public ACustomClass1 Custom1 { get; set; }
public ACustomClass2 Custom2 { get; set; }
}
And you want to validate Custom1 with Custom1Validator when Kind == Kind1 (and Custom2 with Custom2Validator when Kind == Kind2, obviously)
What is the best way to proceed with version 8.6.0 ?
At the moment, I've done like this (but I find it is awkward):
public class MyFormValidator : AbstractValidator<MyForm>
{
public MyFormValidator (IStringLocalizer<Strings> localizer, Custom1Validator validator1, Custom2Validator validator2)
{
//validate Kind and then, in function of Kind, use correct validator
RuleFor(x => x).Custom((f, context) => {
if (!Enum.TryParse<Kind>(f.Kind, out var kind))
{
context.AddFailure(localizer["Invalid Kind"]);
return;
}
switch (kind)
{
case Kind.Kind1:
if (f.Custom1 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (! validator1.Validate(f.Custom1, out var firstError))
{
context.AddFailure(firstError);
}
break;
case Kind.Kind2:
if (f.Custom2 == null)
{
context.AddFailure(localizer["Invalid Kind"]);
}
else if (!validator2.Validate(f.Custom2, out var firstError))
{
context.AddFailure(firstError);
}
break;
}
});
}
}
Note that I am using asp.net core with dependency injection (this is why there is IStringLocalizer and I can not use SetValidator for Custom1 and Custom2)
What I'd like instead is something like
RuleFor(x => x.Kind).NotEmpty().IsEnumName(typeof(Kind)).withMessage(_ => localizer["Invalid Kind"]);
RuleFor(x => x.Custom1).NotEmptyWhen(f => f.Kind == Kind.Custom1.ToString()).withMessage(_ => localizer["Invalid Kind"])
RuleFor(x => x.Custom1).SetValidator(validator1); //would be executed only when custom1 is not null
//same for custom2
The problem is that I do not see how to do code the NotEmptyWhen method
Restructure?
By the looks of your posted code snippets, I presume that MyForm will never have a populated Custom1 and Custom2 property in the same request. So, instead of having a parent model that holds both payload kinds, I would encourage you to directly use the model that represents the payload being validated. Then you won't run into this nasty pattern of checking the kind wherever necessary.
One of your form endpoints accepts a Custom1, which has an associated Custom1Validator. Another one of your form endpoints accepts a Custom2, which has an associated Custom2Validator. They are decoupled. You can safely change one without affecting the other.
Use Fluent Validation Conditions (When/Unless)
If you're dead set on having one model responsible for representing the payload of multiple requests (please don't), you can use the When() method provided by the library. Take a look at their documentation on conditional rules.
Imagine I have this class:
public class MyClass
{
public string Id { get; set; }
public string Name { get; set; }
public IList<MyOtherClass> MyOtherClassList { get; set; }
}
And this method on my service layer which I want to unit-test with Moq and xunit:
public IList<MyClass> GetAll()
{
var options = new FindOptions<MyClass>
{
Projection = Builders<MyClass>.Projection
.Exclude(m => m.MyOtherClassList)
};
return MyClassRepository.GetAll(options)
}
And this would be the test:
[Fact]
public void GetAll_Success()
{
//Arrange
List<MyClass> expected = ... ;
var mockMyClassRepository = new Mock<IMyClassRepository>();
mockMyClassRepository.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>())).Returns(expected);
var myClassService = new MyClassService(mockMyClassRepository);
//Act
var result = myClassService.GetAll();
//Assert
Assert.Equal(expected, result);
mockMyClassRepository.Verify(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()), Times.Once);
}
I want to avoid using It.IsAny for the FindOptions and be sure that the findOptions are excluding the MyOtherClassList attribute but I'm not sure how to compare the FindOptions class.
Any suggestion?
Hows this
mockMyClassRepository
.Setup(m => m.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns(FindOptions<MyClass> a) =>
{
if (a.MyOtherClass != null || a.MyOtherClass.Length() > 0)
throw new InvalidOperationException();
else
return expected;
});
You can use an expression in the Returns to inspect the provided argument so you can apply what ever filtering you want.
mockMyClassRepository
.Setup(_ => _.GetAll(It.IsAny<FindOptions<MyClass>>()))
.Returns((FindOptions<MyClass> options) => {
var projection = options.Projection;
//...use as desired to filter the expected.
return expected;
});
The same could have been done in a Callback where the expected could be manipulated/altered, again based on the passed argument.
Do note that what ever is done with the passed argument is an implementation concern that applies to what is done by the actual repository, which you are trying to mock.
The only way to be sure that the findOptions are actually excluding the MyOtherClassList attribute would be via an integration test.
If you're looking to be more specific on the .Verify call, you can leave the .Setup call to use It.IsAny, then use additional constrains on the verify call, like this:
mockMyClassRepository.Verify(m => m.GetAll(It.Is<FindOptions<MyClass>>(options =>
options.Projection == .... //whatever logic would return true for your success criteria
)), Times.Once);
I'm using Reactive UI for an MVVM WPF project, and when a property changes I need to know:
The value prior to change
The new value (i.e. the change)
I have a viewmodel (Deriving from ReactiveObject) with a property declared on it such:
private AccountHolderType _accountHolderType;
public AccountHolderType AccountHolderType
{
get { return _accountHolderType; }
set { this.RaiseAndSetIfChanged(ref _accountHolderType, value); }
}
In the constructor I'm trying to do the following:
this.WhenAnyValue(vm => vm.AccountHolderType)
.Subscribe((old,curr) => { // DO SOMETHING HERE });
but the WhenAnyValue method doesn't have such an overload, and the Reactive documentation is quite lacking.
I can gain access to a simple WhenAnyValue such that:
this.WhenAnyValue(vm => vm.AccountHolderType)
.Subscribe(val => { // DO SOMETHING HERE });
this lets me observe the changes and get the latest change, but I need access to the prior value.
I'm aware I could implement this as a simple property such that:
public AccountHolderType AccountHolderType
{
get { // }
set
{
var prev = _accountHolderType;
_accountHolderType = value;
// Do the work with the old and new value
DoSomething(prev, value);
}
}
but given the project is using Reactive UI I want to be as "reactive-y" as possible.
How about this:
this.WhenAnyValue(vm => vm.AccountHolderType)
.Buffer(2, 1)
.Select(b => (Previous: b[0], Current: b[1]))
.Subscribe(t => {
//Logic using previous and new value for AccountHolderType
});
I think you have missed this straight forward Buffer function.
Something like:
var previousValue = this.WhenAnyValue(vm => vm.AccountHolderType);
var currentValue = previousValue.Skip(1);
var previousWithCurrent =
previousValue.Zip(currentValue, (prev, curr) => { /* DO SOMETHING HERE */ });
I am trying to do something like this:
AutoMapper.Mapper.CreateMap<UrlPickerState, Link>()
.ForMember(m=>m.OpenInNewWindow,map=>map.MapFrom(s=>s.NewWindow))
.AfterMap((picker, link) => link = !string.IsNullOrWhiteSpace(link.Url)?link:null) ;
var pickerState = new UrlPickerState();
var linkOutput = AutoMapper.Mapper.Map<Link>(pickerState);
However, the assigned value of link is not used in any execution path.
I would like linkOutput to be null, but it is not.
How would I make the destination object null?
Details of objects involved:
public class Link
{
public string Title { get; set; }
public string Url { get; set; }
public bool OpenInNewWindow { get; set; }
}
public class UrlPickerState
{
public string Title { get; set; }
public string Url { get; set; }
public bool NewWindow { get; set; }
//.... etc
}
Here's a fiddle: http://dotnetfiddle.net/hy2nIa
This is the solution I used in the end, it was a bit more manual internally, but does not require any extra plumbing.
If anyone has a more elegant solution, it would be appreciated.
config.CreateMap<UrlPickerState, Link>()
.ConvertUsing(arg =>
{
if (string.IsNullOrWhiteSpace(arg.Url))
{
return null;
}
return new Link()
{
Url = arg.Url,
OpenInNewWindow = arg.NewWindow,
Title = arg.Title,
};
});
I created the following extension method to solve this problem.
public static IMappingExpression<TSource, TDestination> PreCondition<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mapping
, Func<TSource, bool> condition
)
where TDestination : new()
{
// This will configure the mapping to return null if the source object condition fails
mapping.ConstructUsing(
src => condition(src)
? new TDestination()
: default(TDestination)
);
// This will configure the mapping to ignore all member mappings to the null destination object
mapping.ForAllMembers(opt => opt.PreCondition(condition));
return mapping;
}
For the case in question, it can be used like this:
Mapper.CreateMap<UrlPickerState, Link>()
.ForMember(dest => dest.OpenInNewWindow, opt => opt.MapFrom(src => src.NewWindow))
.PreCondition(src => !string.IsNullOrWhiteSpace(src.Url));
Now, if the condition fails, the mapper will return null; otherwise, it will return the mapped object.
I think that will have to be done outside the mapping. Since AutoMapper requires an instance to map to, setting the destination to null seems like it should go outside the mapping.
I would instead do something like:
AutoMapper.Mapper.CreateMap<UrlPickerState, Link>()
.ForMember(m=>m.OpenInNewWindow,map=>map.MapFrom(s=>s.NewWindow));
var pickerState = new UrlPickerState();
Link linkOutput = null;
if(!string.IsNullOrWhiteSpace(pickerState.Url)) // or whatever condition is appropriate
linkOutput = AutoMapper.Mapper.Map<Link>(pickerState);
I hawe all day strugle my head and i canot find any solution to my case so i nead help. Hear is my problem: I hawe two classes that implement one interface
public interface ICacheObject
{
string Get();
}
public class WebCacheObject : ICacheObject
{
public string Get()
{
return "Web";
}
}
public class SysteCacheObject : ICacheObject
{
public string Get()
{
return "System";
}
}
So in some other clase For Example in Class Test I nead to inject the WebCacheObject and in Test2 clase i nead to inject SystemCacheObject. I did this in Initialize:
ObjectFactory.Initialize(c =>{ c.For<IMessage>().Use<Message>();
c.For<ICacheObject>().ConditionallyUse(t =>{t.If(g => g.RequestedName == "HTTP")
.ThenIt.Is.ConstructedBy(
() =>
new WebCacheObject());
t.If(g =>g.RequestedName =="OtherCache")
.ThenIt.Is.ConstructedBy(
() =>
new SysteCacheObject
());
});
But I dont know how to Call Test-s clase-s so if I call so the condition is true (or how to change condition so this will work)
ObjectFactory.GetInstance<'ITest>()
the Test Clase will hawe WebCache in other case SystemCache???
Sorry for my bad English.
I think you should avoid the conditional construction syntax with if - if there is a simpler alternative. In your case I think that this will do:
For<ICacheStorage>().Use<NullObjectCache>();
For<Test>().Use<Test>.Ctor<ICacheStorage>().Is<HttpContextCacheAdapter>();
For<Test2>().Use<Test2>.Ctor<ICacheStorage>().Is<HttpContextCacheAdapter>();
Thanks i did find the solution if eny one of you neade hear is it (i did change some thing in condition and is working ):
x.For<ICacheStorage>().ConditionallyUse(c =>
{
c.If(t => t.ParentType == typeof(Test) ||
t.ParentType == typeof(Test2))
.ThenIt
.Is.ConstructedBy(
by => new HttpContextCacheAdapter());
c.TheDefault.Is.ConstructedBy(
by => new NullObjectCache());
});
Sorry for my bad English