I want to use FluentValidation in our production site and wants to know what the performance could be. So I wrote a fairly simple test, to my surprise, FluentValidation seems to be really poor. I am wondering maybe my test is not correct? If the test is unfortunately correct, is there anyway to improve the performance?
The result in (ms):
My test:
public class TestItem
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Value4 { get; set; }
public string Value5 { get; set; }
public string Value6 { get; set; }
public string Value7 { get; set; }
public string Value8 { get; set; }
public string Value9 { get; set; }
public string Value10 { get; set; }
}
public class ItemValidator : AbstractValidator<TestItem>
{
public ItemValidator()
{
RuleFor(x => x.Value1).NotEmpty();
RuleFor(x => x.Value2).NotEmpty();
RuleFor(x => x.Value3).NotEmpty();
RuleFor(x => x.Value4).NotEmpty();
RuleFor(x => x.Value5).NotEmpty();
RuleFor(x => x.Value6).NotEmpty();
RuleFor(x => x.Value7).NotEmpty();
RuleFor(x => x.Value8).NotEmpty();
RuleFor(x => x.Value9).NotEmpty();
RuleFor(x => x.Value10).NotEmpty();
}
}
public class StupidValidator
{
public void Validate(TestItem item)
{
string.IsNullOrEmpty(item.Value1);
string.IsNullOrEmpty(item.Value2);
string.IsNullOrEmpty(item.Value3);
string.IsNullOrEmpty(item.Value4);
string.IsNullOrEmpty(item.Value5);
string.IsNullOrEmpty(item.Value6);
string.IsNullOrEmpty(item.Value7);
string.IsNullOrEmpty(item.Value8);
string.IsNullOrEmpty(item.Value9);
string.IsNullOrEmpty(item.Value10);
}
}
internal class Program
{
static void Main(string[] args)
{
ItemValidator val = new ItemValidator();
StupidValidator stupidValidator= new StupidValidator();
List<TestItem> items = new List<TestItem>();
for (int i = 0; i < 100000; i++)
{
items.Add(new TestItem());
}
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < items.Count; i++)
{
val.Validate(items[i]);
}
sw.Stop();
Console.WriteLine("fluentValidation uses:"+sw.Elapsed.TotalMilliseconds);
Console.ReadLine();
sw.Restart();
for (int i = 0; i < items.Count; i++)
{
stupidValidator.Validate(items[i]);
}
sw.Stop();
Console.WriteLine("Stupid one uses:" + sw.Elapsed.TotalMilliseconds);
Console.ReadLine();
}
}
Update 1:
If I create the fluent validator in this way, the validation takes around 130ms, still much slower than the stupid one, but better than before.
public class ItemValidator : AbstractValidator<TestItem>
{
public override ValidationResult Validate(ValidationContext<TestItem> context)
{
ValidationResult result = new ValidationResult()
{
};
var item= context.InstanceToValidate;
string.IsNullOrEmpty(item.Value1);
string.IsNullOrEmpty(item.Value2);
string.IsNullOrEmpty(item.Value3);
string.IsNullOrEmpty(item.Value4);
string.IsNullOrEmpty(item.Value5);
string.IsNullOrEmpty(item.Value6);
string.IsNullOrEmpty(item.Value7);
string.IsNullOrEmpty(item.Value8);
string.IsNullOrEmpty(item.Value9);
string.IsNullOrEmpty(item.Value10);
return result;
}
}
Related
I am having this issue when I am dealing with Geometry datatypes when I change the property to string everything works like a charm. Below you may see that I used schema filter to remove Ignored data member , and document filter to remove anything related to nettopology.
Property Name = GeoPoly
Swagger Config Class
public static IServiceCollection AddSwaggerModule(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v2", new OpenApiInfo { Title = "Test API", Version = "0.0.1" });
c.SchemaFilter<MySwaggerSchemaFilter>();
c.DocumentFilter<RemoveBogusDefinitionsDocumentFilter>();
c.ResolveConflictingActions(x => x.First());
});
return services;
}
public static IApplicationBuilder UseApplicationSwagger(this IApplicationBuilder app)
{
app.UseSwagger(c =>
{
c.RouteTemplate = "{documentName}/api-docs";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/v2/api-docs", "Test API");
});
return app;
}
}
public class MySwaggerSchemaFilter : Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var ignoreDataMemberProperties = context.Type.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);
foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
{
var propertyToHide = schema.Properties.Keys
.SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());
if (propertyToHide != null)
{
schema.Properties.Remove(propertyToHide);
}
}
}
}
public class RemoveBogusDefinitionsDocumentFilter : Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Components.Schemas.Remove("Districts");
swaggerDoc.Components.Schemas.Remove("Geometry");
swaggerDoc.Components.Schemas.Remove("CoordinateSequenceFactory");
swaggerDoc.Components.Schemas.Remove("GeometryOverlay");
swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
swaggerDoc.Components.Schemas.Remove("CoordinateEqualityComparer");
swaggerDoc.Components.Schemas.Remove("NtsGeometryServices");
swaggerDoc.Components.Schemas.Remove("GeometryFactory");
swaggerDoc.Components.Schemas.Remove("OgcGeometryType");
swaggerDoc.Components.Schemas.Remove("Coordinate");
swaggerDoc.Components.Schemas.Remove("Point");
}
}
Entity Class
public class Districts : BaseEntity<long>
{
public string DistrictsDesc { get; set; }
public string DistrictsDescAr { get; set; }
[IgnoreDataMember]
[Column(TypeName = "geometry")]
public Geometry GeoPoly { get; set; }
public IList<Records> Records { get; set; } = new List<Records>();
public long? RegionsId { get; set; }
public Regions Regions { get; set; }
public long? CitiesId { get; set; }
public Cities Cities { get; set; }
}
Is there a way to stop swashbuckle gen from dealing with datatypes other than documents filter ?
This is my DTO
public class Part1
{
public Part1()
{
Value = new List<ValueList>();
}
public int Id{ get; set; }
public List<ValueList> Value { get; set; }
}
public class ValueList
{
public int Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
and this is my table structure and I want to save above dto into below dto using automapper
public class Part1
{
public int Id{ get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
Do you want to know how to make the list map to singel string in the new model?
You can check my demo, in mapper profile, define the three values to map:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Part1, Part1DTO>() //Part1DTO is your below model
.ForMember(x => x.Value1, m => m.MapFrom(src => src.Value[0].Value1))
.ForMember(x => x.Value2, m => m.MapFrom(src => src.Value[0].Value2))
.ForMember(x => x.Value3, m => m.MapFrom(src => src.Value[0].Value3));
}
}
Controller:
public IActionResult Index()
{
Part1 part1= new Part1()
{
Id=1,
Value=new List<ValueList> { new ValueList { Value1=1,Value2="b",Value3="c"} }
}; //define old model
var result = _mapper.Map<Part1DTO>(part1); // map to new
return View(result);
}
Don't forget to add this in ConfigureServices:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
Result:
I want to convert BotViewModel to Bot using AutoMapper.
There is an example input below from type BotViewModel. If I want to add a new bot to the database, that model should be matched/mapped to Bot model and Bot's foreign keys should be resolved.
In other words, BotViewModel.Symbol should be matched to CryptoPair.Symbol and BotViewModel.Interval to TimeInterval.Interval.
{
"id": 0,
"name": "Bot 1",
"status": true,
"symbol": "LTCBTC",
"interval": 6
}
My code below is working exactly as I described it above. It uses http://docs.automapper.org/en/stable/Custom-value-resolvers.html. The thing is that I want to make _context.CryptoPairs.FirstOrDefault async FirstOrDefault => FirstOrDefaultAsync.
I want to do that because I feel like that's not the best practice. Basically, I'm looking for an advice if that's okay or not. By the way, if the symbol or interval cannot be matched because it simply doesn't exist in the database, var bot = _mapper.Map<Bot>(botViewModel); should just throw an exception which is later handled by my controller.
public class CryptoPairResolver : IValueResolver<BotViewModel, Bot, int>
{
private readonly BinanceContext _context;
public CryptoPairResolver(BinanceContext context)
{
_context = context;
}
public int Resolve(BotViewModel source, Bot destination, int destMember, ResolutionContext context)
{
return _context.CryptoPairs.FirstOrDefault(p => p.Symbol == source.Symbol).Id;
}
}
public class TimeIntervalResolver : IValueResolver<BotViewModel, Bot, int>
{
private readonly BinanceContext _context;
public TimeIntervalResolver(BinanceContext context)
{
_context = context;
}
public int Resolve(BotViewModel source, Bot destination, int destMember, ResolutionContext context)
{
return _context.TimeIntervals.FirstOrDefault(i => i.Interval == source.Interval).Id;
}
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<BotViewModel, Bot>()
.ForMember(dest => dest.CryptoPairId, opt => opt.MapFrom<CryptoPairResolver>())
.ForMember(dest => dest.TimeIntervalId, opt => opt.MapFrom<TimeIntervalResolver>());
}
}
Models:
public class Bot
{
public int Id { get; set; }
public string Name { get; set; }
public bool Status { get; set; }
public int CryptoPairId { get; set; }
public CryptoPair CryptoPair { get; set; }
public int TimeIntervalId { get; set; }
public TimeInterval TimeInterval { get; set; }
}
public class BotViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Status { get; set; }
public string Symbol { get; set; }
public KlineInterval Interval { get; set; }
}
public class CryptoPair
{
public int Id { get; set; }
public string Symbol { get; set; }
public List<Bot> Bots { get; set; }
}
public class TimeInterval
{
public int Id { get; set; }
public KlineInterval Interval { get; set; }
public List<Bot> Bots { get; set; }
}
Edit:
public async Task<Bot> CreateAsync(BotViewModel botViewModel)
{
// Map BotViewModel to Bot
var cryptoPair = await _context.CryptoPairs.FirstOrDefaultAsync(e => e.Symbol == botViewModel.Symbol);
if (cryptoPair == null)
{
throw new BadRequestException();
}
var timeInterval = await _context.TimeIntervals.FirstOrDefaultAsync(e => e.Interval == botViewModel.Interval);
if (timeInterval == null)
{
throw new BadRequestException();
}
var bot = new Bot
{
Name = botViewModel.Name,
Status = botViewModel.Status,
CryptoPairId = cryptoPair.Id,
TimeIntervalId = timeInterval.Id
};
// Create code
_context.Bots.Add(bot);
await _context.SaveChangesAsync();
return bot;
}
Hello I have problem in one query. Why it's always return no value.
public List<UserDetail> userSearchModel(UserSearchModel searchModel)
{
string userid = User.Identity.GetUserId();
var user = _dbContext.UserDetails.Where(x => x.Id == userid);
var result = _dbContext.UserDetails.Except(user).ToList().AsQueryable();
if (searchModel != null)
{
if (searchModel.LanguageId.Count() != 0)
{
List<UserDetailLanguage> usrDetails = new List<UserDetailLanguage>();
foreach (var item in searchModel.LanguageId)
{
var details = _dbContext.UserDetailLanguages.Where(x => x.LanguageId == item).ToList();
foreach (var item2 in details)
{
usrDetails.Add(item2);
}
}
result = result.Where(x => x.UserDetailLanguages == usrDetails);
}
}
return result.ToList();
}
I want to get results which are the same in usrDetails list and in result.UserDetailLanguages.
In result.UserDetailLanguages I have record equals to record in usrDetails but this not want retrieve.
Here is my model:
public class UserDetail
{
public UserDetail()
{
this.UserDetailLanguages = new HashSet<UserDetailLanguage>();
}
[Key, ForeignKey("User")]
public string Id { get; set; }
public DateTime Birthday { get; set; }
public string Sex { get; set; }
public string Country { get; set; }
public string About { get; set; }
[NotMapped]
public int Age { get { return DateTime.Now.Year - Birthday.Year; } }
public virtual ApplicationUser User { get; set; }
public virtual ICollection<UserDetailLanguage> UserDetailLanguages { get; set; }
}
public class UserDetailLanguage
{
public Int32 Id { get; set; }
public virtual UserDetail UserDetail { get; set; }
public string UserDetailId { get; set; }
public virtual Language Language { get; set; }
public Int32 LanguageId { get; set; }
public Boolean IsKnown { get; set; }
public static implicit operator List<object>(UserDetailLanguage v)
{
throw new NotImplementedException();
}
}
public class Language
{
public Language()
{
this.UserDetailLanguages = new HashSet<UserDetailLanguage>();
}
public int Id { get; set; }
public string Value { get; set; }
public string Name { get; set; }
public virtual ICollection<UserDetailLanguage> UserDetailLanguages { get; set; }
}
What I'm doing wrong?
If you want to see if your value is in a list you use the Contains function of the list -- like this:
result = result.Where(x => usrDetails.Contains(x.UserDetailLanguage));
If you want to see if there are any items in both lists you can use intersection like this:
result = result.Where(x => usrDetails.Intersect(x.UserDetailLanguage).Count() > 0);
Looks like you are checking equality between lists in following code
result = result.Where(x => x.UserDetailLanguages == usrDetails);
This might not work, to check equality for lists you can use something like
Enumerable.SequenceEqual(FirstList.OrderBy(fList => fList),
SecondList.OrderBy(sList => sList))
i m getting the exception "No persister for: MVCTemplate.Common.Entities.User" . I Google this issue and apply all the solution i found. but all are useless for me.
Does anyone know what i m doing wrong ?
my User Class code is
public class User
{
public virtual Guid UserID { get; private set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual string FullName { get; set; }
public virtual string Email { get; set; }
public virtual TimeSpan LastLogin { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual IList<UserInRole> UserInRoles { get; set; }
}
User Mapping :
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("tblUsers");
Id(user => user.UserID).GeneratedBy.GuidComb();
Map(user => user.UserName).Not.Nullable();
Map(user => user.Password).Not.Nullable();
Map(user => user.FullName).Not.Nullable();
Map(user => user.Email).Not.Nullable();
Map(user => user.LastLogin).Not.Nullable();
Map(user => user.IsActive).Nullable();
Map(user => user.CreationDate).Not.Nullable();
HasMany(user => user.UserInRoles);
}
}
FNH Configuration :
return Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("FNHConnection"))
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<User>())
.BuildSessionFactory();
Thanks
Double check that your mapping class is public.
Check that you have something like this in your fluent config....
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserMap>())
The following will cause this error in simple terms:
private static void MainFN()
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
Data[] balance = new Data[12];
for (int i = 0; i < 12; i++)
{
balance[i] = new Data();
balance[i].Test1 = "Example Data " + (i + 1).ToString();
balance[i].Test2 = i + 11;
balance[i].Test3 = (i % 2 == 0);
session.SaveOrUpdate(balance[i]); //Should be like this
}
//session.SaveOrUpdate(balance); //This will give the error