Strange behaviour of fluentvalidation's SetCollectionValidator - c#

In my project, I am using FluentValidation of .net. Class on which I applied this validation is something like this:
[Validation(typeof(InputValidator))]
public class Inputs
{
public IEnumerable<string> MobileNos { get; set; }
}
InputValidator.cs file is something like this
public class InputValidator: AbstractValidator<Inputs>
{
public BlockMobileInputsValidator()
{
RuleFor(x => x.MobileNos).Cascade(CascadeMode.StopOnFirstFailure).NotEmpty()
.Must(x => x.Count() <= 100).WithMessage("List should not contain more than 100 mobile numbers.")
.SetCollectionValidator(new MobileValidator());
}
}
And MobileValidator.cs
public class MobileValidator:AbstractValidator<string>
{
public Mobilevalidator()
{
RuleFor(x => x).Matches("^[6789]\d{9}$").WithMessage("{PropertyValue} is not in correct mobile-number format");
}
}
Now when I pass {null,"7897897897"} list to MobileNos of Input class, it is not giving any error and list is accepted for further use.
I am not able to understand this strange behaviour. I also tried this
public class MobileValidator:AbstractValidator<string>
{
public Mobilevalidator()
{
RuleFor(x => x).NotNull().Matches("^[6789]\d{9}$").WithMessage("{PropertyValue} is not in correct mobile-number format");
}
}
but this also not working for above Input.
Can anyone tell why it is accepting null value?

I don't know why your code does not work but when you change InputValidator.cs to the following code, then you can get the desired result:
using FluentValidation;
using System.Linq;
public class InputsValidator : AbstractValidator<Inputs>
{
public InputsValidator()
{
RuleFor(x => x.MobileNos).Cascade(CascadeMode.StopOnFirstFailure).NotEmpty()
.Must(x => x.Count() <= 100).WithMessage("List should not contain more than 100 mobile numbers.");
RuleForEach(x => x.MobileNos).NotNull().SetValidator(new MobileValidator());
}
}
Then the following test passes:
using FluentValidation;
using Xunit;
using System;
using System.Collections.Generic;
namespace test
{
public class InputsValidatorTests
{
[Fact]
public void WhenContainsNull_ThenIsNotValid()
{
var inputs = new Inputs();
inputs.MobileNos = new List<string>() { null, "7897897897" };
var inputsValidator = new InputsValidator();
var result = inputsValidator.Validate(inputs);
Assert.False(result.IsValid);
}
}
}

Related

Access list from another class c#

I have created two classes for my program using c#,The first one "PayRecordSettings" reads a CSV file (using CSV helper) that has 5 columns. The second class "createPayRecord" should take the list and create a sublist for each column. I managed to read the CSV file and call it from the Main and it displays the data but I haven't figured out how to take the data and pass it to other classes. This is my code
public class CsvImporter
{
public static List<PayRecordSettings> ImportPayRecords()
{
using (var path = new StreamReader("C:\\Users\\Import\\data.csv"))
{
using (var csvRead = new CsvReader(path, CultureInfo.InvariantCulture))
{
csvRead.Context.RegisterClassMap<CsvSettingsMap>();
var PayRecord = csvRead.GetRecords<PayRecordSettings>().ToList();
return PayRecord;
}
}
}
public static List<PayRecordSettings> createPayRecord()
{
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
return ImportPayRecords;
}
}
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
Map(m => m.EmployeeId).Name("EmployeeId");
Map(m => m.Hours).Name("Hours");
Map(m => m.Rate).Name("Rate");
Map(m => m.Visa).Name("Visa");
Map(m => m.YearToDate).Name("YearToDate");
}
}
public class PayRecordSettings
{
public int EmployeeId { get; set; }
public double Hours { get; set; }
public double Rate { get; set; }
public string Visa { get; set; }
public string YearToDate { get; set; }
}
Is there a way to make empty cells equal to 0?
I have updated my code based on Fildor comments. I have to report that the it works most of it. The CSV Helper settings didnt add a 0 when there is a empty cell. I have another question, from the method PayRecordsettings once I called the list how can I store this in new sublists? if I have 5 variables I would like to store a complete column of the CSV in a new list.
public static List<PayRecordSettings> createPayRecord()
{
var payRecords = ImportPayRecords();
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours);
}
return payRecords;
} /* I would like to create a new list of Employee
list of Hours
The code that you've posted looks like it is missing some stuff. You use variables that not have been created like ImportPayRecords and payRecords in your createPayRecord-method.
To accomplish what you want to do, you need to pass your data as a parameter to you function like so:
public static void CreatePayRecord(IList<PayRecordSettings> payRecords)
{
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
}
Another tip, stick to the recommended naming-conventions of C# and also try to find more descriptive names for your variables.
Is there a way to make empty cells equal to 0?
If I read the docs correctly, it should work something like this:
public class PayRecordSettings
{
public int EmployeeId { get; set; }
[Optional]
public double Hours { get; set; } = 0.0;
[Optional]
public double Rate { get; set; } = 0.0;
public string Visa { get; set; }
public string YearToDate { get; set; }
}
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
AutoMap(CultureInfo.InvariantCulture);
}
}
Disclaimer: Haven't tested this! So, take with a grain of salt.
"...but I havent figure out how to take the data and pass it to another classes"
You are mixing up some terms here:
public class CsvImporter // <- THIS is a class
{
// This is a (static) method
public static List<PayRecordSettings> ImportPayRecords()
{
using (var path = new StreamReader("C:\\Users\\Import\\data.csv"))
{
using (var csvRead = new CsvReader(path, CultureInfo.InvariantCulture))
{
csvRead.Context.RegisterClassMap<CsvSettingsMap>();
var PayRecord = csvRead.GetRecords<PayRecordSettings>().ToList();
return PayRecord;
}
}
}
// This is another static method.
public static List<PayRecordSettings> createPayRecord()
{
// this makes no sense: `payRecords` does not exist here.
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
// makes no sense, neither. If at all, it should be `ImportPayRecords()`.
return ImportPayRecords;
}
}
So, to fix your second method, try this:
public static List<PayRecordSettings> CreatePayRecord()
{
var payRecords = ImportPayRecords();
foreach (PayRecordSettings details in payRecords)
{
Console.WriteLine(details.Hours + " hours" + details.Rate);
}
return payRecords;
}
Mind that there is a dependency here between the two Methods, which is ... well ... let's say "suboptimal". Also CreatePayRecord doesn't actually do much creation. So you may want to refactor to using ImportPayRecords to get the list of model instances and maybe something like this to print them out:
public static void PrintPayRecords(IEnumerable<PayRecordSettings> payRecords)
{
foreach (PayRecordSettings details in payRecords)
{
// Sticking to your format, but looks kinda weird.
Console.WriteLine("{0} hours{1}", details.Hours, details.Rate);
}
}
First issue: Getting defaulting empty cells to 0.
You can use .Default() for a default value if the field is empty.
Also, for your map class you don't need .Name("EmployeeId") if the name of the property is already "EmployeeId". It will automatically assume that is the name. If you received a CSV file that had a heading of "EmpId" then you would need Map(m => m.EmployeeId).Name("EmpId")
public class CsvSettingsMap : ClassMap<PayRecordSettings>
{
public CsvSettingsMap()
{
Map(m => m.EmployeeId);
Map(m => m.Hours).Default(0);
Map(m => m.Rate).Default(0);
Map(m => m.Visa);
Map(m => m.YearToDate);
}
}
Second issue: From the method PayRecordsettings once I called the list how can I store this in new sublists? if I have 5 variables I would like to store a complete column of the CSV in a new list.
I'm really having difficulty understanding what you are looking to do. Could you give some sample data that would be in the PayRecordSettings and then some examples of how you want the sublist data to look like? Were you looking to output it into a new CSV file?

mongodb c# sort by field in a list of entries

I have a collection like this (I removed fields that are not related to the question)
{
_id:ObjectId('5dd7d946cd9c645f1cdc21ef'),
Versions: [
{
"Barcode" : "200830001128132700636"
},
{
"Barcode" : "200830001128132700637"
}
]
},
{
_id:ObjectId('5dd7d946cd9c645f1cdc21eg'),
Versions: [
{
"Barcode" : "200830001128132700638"
},
{
"Barcode" : "200830001128132700639"
}
]
}
I need to find the greatest (max) barcode in the whole collection.
I tried with a code like this:
var options = new FindOptions<Document>
{
Limit = 1,
Sort = Builders<Document>.Sort.Descending(d => d.Versions.Select(v => v.BarCode).Aggregate((v1, v2) => string.Compare(v1, v2) > 0 ? v1 : v2))
};
using var results = await _context.DocumentiItems.FindAsync(FilterDefinition<Document>.Empty, options);
But I get ArgumentNullException, I think it's unable to traslate the expression with the aggregate.
Can you suggest me a better approach?, if possible I want to avoid the use of BSON strings and use only labmda expressions.
The type of is DocumentiItems is IMongoCollection<Document>
this can be easily achieved with the AsQueryable() interface like so:
var result = collection.AsQueryable()
.SelectMany(i => i.Versions)
.OrderByDescending(v => v.Barcode)
.Take(1)
.Single();
here's a test program:
using MongoDB.Entities;
using MongoDB.Entities.Core;
using System;
using System.Linq;
namespace StackOverflow
{
public class Item : Entity
{
public Version[] Versions { get; set; }
}
public class Version
{
public string Barcode { get; set; }
}
public class Program
{
private static void Main(string[] args)
{
new DB("test", "localhost");
var result = DB.Queryable<Item>()
.SelectMany(i => i.Versions)
.OrderByDescending(v => v.Barcode)
.Take(1)
.Single();
Console.WriteLine($"max barcode: {result.Barcode}");
Console.Read();
}
}
}

Cannot implicitly convert type from System.Collection.Generic.IEnumerable.MyClass<Node> to MyClass<Node>

Following is the code for which the error is being generated:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Neo4jClient;
using Neo4jClient.Cypher;
namespace ConsoleApplication1
{
public class SNode
{
public int shelfid { get; set; }
public int floorid { get; set; }
}
public class PathsResult<TNode>
{
public IEnumerable<Node<TNode>> Nodes { get; set; }
public IEnumerable<RelationshipInstance<object>> Relationships { get; set; }
}
class Program
{
public static PathsResult<SNode> getPath(int sourceShelfId, int destinationShelfId, GraphClient client)
{
var pathsQuery =
client.Cypher
.Match("p = shortestPath((src:Node)-[*..150]-(dest:Point))")
.Where((SNode src) => src.shelfid == sourceShelfId)
.AndWhere((SNode dest) => dest.shelfid == destinationShelfId)
.Return(p => new PathsResult<SNode>
{
Nodes = Return.As<IEnumerable<Node<SNode>>>("nodes(p)"),
Relationships = Return.As<IEnumerable<RelationshipInstance<object>>>("rels(p)")
});
var res = pathsQuery.Results;
return res;
}
}
}
The error I'm receiving is that:
Cannot implicitly convert type System.Collection.Generic.IEnumerable<ConsoleApplication1.PathResult<ConsoleApplication1.SNode> >
to ConsoleApplication1.PathResult<ConsoleApplication1.SNode> >. An explicit conversion exists, are you missing a cast?
From what I understand pathQuery.result should return a single PathResult object. However, I tried to make a cast according to the above error like this:
var res = pathsQuery.Results.AsEnumerable<PathsResult<SNode> >;
And now the new error it gives is:
Cannot assign method group to implicitly-typed local variable
Where am I going wrong?
You have several possibilites:
change return type of getPath to IEnumerable...
Use only first element of the query result: Add .FirstOrDefault()
var res = pathsQuery.Results.FirstOrDefault();
Generally it is a good idea to set a breakpoint and put the mouse over var to inspect what type it is. Even better to avoid var in such cases and make the compiler report all problems.

Subclass a class that has Pointers to other classes

I want to subclass a Class in Parse that has Pointers to other classes.
Here is my class:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Parse;
using System.Threading.Tasks;
[ParseClassName("UserCars")] public class UserCars : ParseObject {
[ParseFieldName("car")]
public Car car
{
get { return GetProperty<Car>("car"); }
set { SetProperty<Car>(value, "car"); }
}
[ParseFieldName("isBumpers")]
public bool isBumpers
{
get { return GetProperty<bool>("isBumpers"); }
set { SetProperty<bool>(value, "isBumpers"); }
}
When I execute this query below isBumpers returns an accurate bool value, but car is always null:
var query = new ParseQuery<UserCars>();
query.WhereEqualTo("user", ParseUser.CurrentUser);
query.FindAsync().ContinueWith(t =>
{
IEnumerable<UserCars> userCars = t.Result;
foreach (UserCars u in userCars)
{
Debug.Log(u.car);
}
});
I tried adding this with no change:
IEnumerable<UserCars> userCars = t.Result;
foreach (UserCars u in userCars)
{
Task<Car> fCar = u.car.FetchIfNeededAsync();
Debug.Log(u.car);
}
What am I missing?
fCar is a Task, so basicly you should wait for it to complete.
Try writing this instead:
foreach (UserCars u in userCars )
{
u.car.FetchIfNeededAsync().ContinueWith(result =>
{
Debug.Log(u.car);
};
}
This will make sure you wait for the data to be correctly fetched.

Setup and verify expression with Moq

Is there a way to setup and verify a method call that use an Expression with Moq?
The first attempt is the one I would like to get it to work, while the second one is a "patch" to let the Assert part works (with the verify part still failing)
string goodUrl = "good-product-url";
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(m=>m.Url== goodUrl).Returns(new Product() { Title = "Good product", ... });
}
[Test]
public void MyTest()
{
var controller = GetController();
var result = ((ViewResult)controller.Detail(goodUrl)).Model as ProductViewModel;
Assert.AreEqual("Good product", result.Title);
productsQuery.Verify(x => x.GetByFilter(t => t.Url == goodUrl), Times.Once());
}
Thet test fail at the Assert and throw a null reference exception, because the method GetByFilter is never called.
If instead I use this
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(It.IsAny<Expression<Func<Product, bool>>>())).Returns(new Product() { Title = "Good product", ... });
}
The test pass the Assert part, but this time is the Verify that fail saying that it is never called.
Is there a way to setup a method call with a specific expression instead of using a generic It.IsAny<>()?
Update
I tried also the suggestion by Ufuk Hacıoğulları in the comments and created the following
Expression<Func<Product, bool>> goodUrlExpression = x => x.UrlRewrite == "GoodUrl";
[Setup]
public void SetUp()
{
productsQuery.Setup(x => x.GetByFilter(goodUrlExpression)).Returns(new Product() { Title = "Good product", ... });
}
[Test]
public void MyTest()
{
...
productsQuery.Verify(x => x.GetByFilter(goodUrlExpression), Times.Once());
}
But I get a null reference exception, as in the first attempt.
The code in my controller is as follow
public ActionResult Detail(string urlRewrite)
{
//Here, during tests, I get the null reference exception
var entity = productQueries.GetByFilter(x => x.UrlRewrite == urlRewrite);
var model = new ProductDetailViewModel() { UrlRewrite = entity.UrlRewrite, Culture = entity.Culture, Title = entity.Title };
return View(model);
}
The following code demonstrates how to test in such scenarios. The general idea is that you execute the passed in query against a "real" data. That way, you don't even need "Verify", as if the query is not right, it will not find the data.
Modify to suit your needs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Moq;
using NUnit.Framework;
namespace StackOverflowExample.Moq
{
public class Product
{
public string UrlRewrite { get; set; }
public string Title { get; set; }
}
public interface IProductQuery
{
Product GetByFilter(Expression<Func<Product, bool>> filter);
}
public class Controller
{
private readonly IProductQuery _queryProvider;
public Controller(IProductQuery queryProvider)
{
_queryProvider = queryProvider;
}
public Product GetProductByUrl(string urlRewrite)
{
return _queryProvider.GetByFilter(x => x.UrlRewrite == urlRewrite);
}
}
[TestFixture]
public class ExpressionMatching
{
[Test]
public void MatchTest()
{
//arrange
const string GOODURL = "goodurl";
var goodProduct = new Product {UrlRewrite = GOODURL};
var products = new List<Product>
{
goodProduct
};
var qp = new Mock<IProductQuery>();
qp.Setup(q => q.GetByFilter(It.IsAny<Expression<Func<Product, bool>>>()))
.Returns<Expression<Func<Product, bool>>>(q =>
{
var query = q.Compile();
return products.First(query);
});
var testController = new Controller(qp.Object);
//act
var foundProduct = testController.GetProductByUrl(GOODURL);
//assert
Assert.AreSame(foundProduct, goodProduct);
}
}
}

Categories