Fluent Validation changing CustomAsync to MustAsync - c#

Could some one please help me to resolved this? i'm trying to change CustomAsync to MustAsync, but i couldn't make things to work. Below is my custom method
RuleFor(o => o).MustAsync(o => {
return CheckIdNumberAlreadyExist(o)
});
private static async Task<ValidationFailure> CheckIdNumberAlreadyExist(SaveProxyCommand command)
{
if (command.Id > 0)
return null;
using (IDbConnection connection = new SqlConnection(ConnectionSettings.LicensingConnectionString))
{
var param = new DynamicParameters();
param.Add("#idnumber", command.IdNumber);
var vehicle = await connection.QueryFirstOrDefaultAsync<dynamic>("new_checkDuplicateProxyIdNumber", param, commandType: CommandType.StoredProcedure);
return vehicle != null
? new ValidationFailure("IdNumber", "Id Number Already Exist")
: null;
}
}

To make it work with the latest version of the FluentValidation, I had to use the codes like below.
RuleFor(ws => ws).MustAsync((x, cancellation) => UserHasAccess(x)).WithMessage("User doesn't have access to perform this action");
Please notice the lambda expression here MustAsync((x, cancellation) => UserHasAccess(x)), without this I was always getting an error as cannot convert from 'method group' to 'Func<Worksheet, CancellationToken, Task<bool>>
Below is my custom UserHasAccess function.
private async Task <bool> UserHasAccess(Worksheet worksheet) {
var permissionObject = await _dataProviderService.GetItemAsync(worksheet.FileItemId);
if (permissionObject is null) return false;
if (EditAccess(permissionObject.Permission)) return true;
return false;
}

I'm assuming you're using a version of FluentValidation prior to version 6, as you're not passing in a Continuation Token, so I've based my answer on version 5.6.2.
Your example code does not compile, for starters, as you're missing a semi-colon in your actual rule. You are also evaluating two different properties on the SaveProxyCommand parameter.
I've built a very small POC based on some assumptions:
Given 2 classes:
public class SaveProxyCommand {
public int Id { get; set; }
}
public class ValidationFailure {
public string PropertyName { get; }
public string Message { get; }
public ValidationFailure(string propertyName, string message){
Message = message;
PropertyName = propertyName;
}
}
And a validator:
public class SaveProxyCommandValidator : AbstractValidator<SaveProxyCommand>{
public SaveProxyCommandValidator()
{
RuleFor(o => o).MustAsync(CheckIdNumberAlreadyExists)
.WithName("Id")
.WithState(o => new ValidationFailure(nameof(o.IdNumber), "Id Number Already Exist"));
}
private static async Task<bool> CheckIdNumberAlreadyExists(SaveProxyCommand command) {
if (command.Id > 0)
return true;
var existingIdNumbers = new[] {
1, 2, 3, 4
};
// This is a fudge, but you'd make your db call here
var isNewNumber = !(await Task.FromResult(existingIdNumbers.Contains(command.IdNumber)));
return isNewNumber;
}
}
I didn't include the call to the database, as that's not part of your problem. There are a couple of things of note here:
You're not setting the .WithName annotation method, but when you're setting up a validation rule for an object you have to do this, as FluentValidation expects you to specify specific properties to be validated by default, if you pass in an entire object it just doesn't know how to report errors back.
Must/MustAsync need to return a bool/Task<bool> instead of a custom object. To get around this, you can specify a custom state to be returned when failing validation.
You can then get access to this like this:
var sut = new SaveProxyCommand { Id = 0, IdNumber = 3 };
var validator = new SaveProxyCommandValidator();
var result = validator.ValidateAsync(sut).GetAwaiter().GetResult();
var ValidationFailures = result.Errors?.Select(s => s.CustomState).Cast<ValidationFailure>();
The above does not take into account empty collections, it's just an example of how to dig into the object graph to retrieve custom state.
As a suggestion, fluentvalidation works best if you set up individual rules per property, instead of validating the entire object. My take on this would be something like this:
public class SaveProxyCommandValidator : AbstractValidator<SaveProxyCommand>{
public SaveProxyCommandValidator()
{
RuleFor(o => o.IdNumber).MustAsync(CheckIdNumberAlreadyExists)
.Unless(o => o.Id > 0)
.WithState(o => new ValidationFailure(nameof(o.IdNumber), "Id Number Already Exist"));
}
private static async Task<bool> CheckIdNumberAlreadyExists(int numberToEvaluate) {
var existingIdNumbers = new[] {
1, 2, 3, 4
};
// This is a fudge, but you'd make your db call here
var isNewNumber = !(await Task.FromResult(existingIdNumbers.Contains(numberToEvaluate)));
return isNewNumber;
}
}
This read more like a narrative, it uses the .Unless construct to only run the rule if Id is not more than 0, and does not require the evaluation of the entire object.

Related

Recursion is causing double results

I have 2 methods.
Method 1 calls a google drive service which returns an array of an object of files.
Method 2 calls the first method to get the returned list, which calls the google drive service which activates the method you see below for a "second" time.
I may be wrong but I think the recursion is causing my final list results to be doubled. Or it may be because I have declared files outside of this method. Once the line files.AddRange(....) is called by the second method it technically already contains the files from the first method. I am not quite sure how to solve my issue.
readonly Stack myStack = new Stack();
readonly HashSet<BFile> files = new Hashset<BFile>();
readonly HashSet<BFile> pushedList = new HashSet<BFile>();
public async Task<(BFile[]? files, string? error)> GetFiles(string parentId, bool includePermissions)
{
var service = service..
if (service != null)
{
var listRequest = service.Files.List();
do
{
var response = await listRequest.ExecuteAsync();
var folders = response.Files.Where(f => f.MimeType == "application/vnd.google-apps.folder");
var allOtherFiles = response.Files.Where(f => f.MimeType != "application/vnd.google-apps.folder");
files.AddRange(folders.Where(f => f.Name != "$ExclaimerSignatures").Select(f => mapFile(f)));
files.AddRange(allOtherFiles.Select(f => mapFile(f)));
var missingFiles = files.Where(f => !pushedList.Contains(f)).ToList();
missingFiles.ForEach(myStack.Push);
pushedList.UnionWith(missingFiles);
while (myStack.Count != 0)
{
var temp = (BFile)myStack.Peek();
myStack.Pop();
await GetFiles(temp.Id, true);
}
listRequest.PageToken = response.NextPageToken;
} while (listRequest.PageToken != null);
return (files.ToArray(), null);
}
else
return (null, "Something went wrong");
}
edit: to answer a question from below the only reason why I have stack with recursion tree walking is I use the stack to keep track of stuff that has been visited. I am not sure if that is bad or not, it was just simply what I came up with upon initially writing this code
I think the issue is that HashSet is not determinating BFiles like unique, and it is a reason why HashSet<BFile> files have duplicated values.
To fix it, you can override Equals and GetHashCode in your class BFile. If BFile is not your class, check if maybe it has already it implemented. If not, you can do it with the IEqualityComparer interface. Here is a simple example:
var files = new HashSet<BFile>(new Comparer());
var items = Enumerable.Empty<BFile>(); //Can be any collection
files.UnionWith(items);
class Comparer : EqualityComparer<BFile>
{
public override bool Equals(BFile? x, BFile? y) => x.Id == y.Id;
public override int GetHashCode(BFile obj) => obj.Id.GetHashCode();
}
class BFile
{
public string Id { get; set; }
public string Name { get; set; }
public byte[] Content { get; set; }
}
I hope it will help you.

how to use ready-made Func<> delegate in QueryAsync?

I have such a problem that I have the same Func in my methods. I took it out separately, but now the problem is how to pass parameters to it. I want to use it in several places, but I don't know how to get the necessary parameters from the QueryAsync method
My func delegate
private Func<Authors, AuthorInPrintingEditions, PrintingEditions, Authors> _relationDelegate =
(Authors authors, AuthorInPrintingEditions relation, PrintingEditions printingEdition) =>
{
if (!_dictionaryResult.ContainsKey(authors.Id))
{
_dictionaryResult.Add(authors.Id, new Authors()
{
Id = authors.Id,
Name = authors.Name,
AuthorInPrintingEditions = printingEdition == null
? new List<AuthorInPrintingEditions>()
: new List<AuthorInPrintingEditions>()
{
new AuthorInPrintingEditions()
{
PrintingEdition = printingEdition
}
}
});
}
else
{
if (printingEdition != null)
{
_dictionaryResult[authors.Id].AuthorInPrintingEditions.Add(new AuthorInPrintingEditions()
{
PrintingEdition = printingEdition
});
}
}
return authors;
};
My method, when I want use it
using (var conneсtion = CreateConnection())
{
await conneсtion.QueryAsync<Authors, AuthorInPrintingEditions, PrintingEditions, Authors>(query,
(authors, authorInPrintingEditions, printingEdition), <--- in this row I want use delegate
new { skip = skip, pageSize = pageSize }, splitOn: "Id,AuthorId,Id");
return _dictionaryResult.Values.ToList();
}
I don't understand how to pass parameters to it and whether it is generally possible, or it will still be easier to copy and paste
If you wish to reuse the delegate and also be able to use parameters,
try using something like this:
public class AuthorsMapper
{
public int Parameter1;
public string Parameter2;
public Func<Authors, AuthorInPrintingEditions, PrintingEditions, Authors> Map =>
// Your existing code that can now use parameters
}
And use it like this:
var mapper = new AuthorsMapper
{
Parameter1 = 742,
Parameter2 = "House"
};
await conneсtion.QueryAsync<Authors, AuthorInPrintingEditions, PrintingEditions, Authors>
(query, mapper.Map, ...);

Php to C# aspnet mvc

i have transform a php/js code to js/c#, but i stuck for update the new value.
The php code is :
`if (isset($_POST['update'])) {
foreach($_POST['positions'] as $position) {
$index = $position[0];
$newPosition = $position[1];
$conn->query("UPDATE country SET position = '$newPosition' WHERE id='$index'");
}
exit('success');
}`
My "empty" c# code
[HttpPost]
public ActionResult Index (userTable index)
{
picturesEntities MyDb = new picturesEntities();
homeViewModel HVM = new homeViewModel();
HVM.userTables = MyDb.userTables.ToList();
if (Request["update"] != null)
{
foreach (Request["positions"])
{
MyDb.SaveChanges();
}
return View(HVM);
}
}
If someone could help me for it that would be great, i'm stuck on it for days and i didn't find a workning solution yet.
Thanks to everyone who read my message.
Most ASP.NET will bind a custom class which will be compatible to your request.
public class UserPositionsRequest
{
public bool Update { get; set; }
// For orderly, this actually be a list of a custom class
public List<int[]> Positions { get; set; }
}
This by any means is not a complete and working solution, the following code was never been tested and can be consider as pseudo-like code.
Also, the .Id and .Position should be the same sensitivity as in Db.
// Binding our UserPositionsRequest class
public void Index(UserPositionsRequest request) {
// Checking if we should update, if you will change the request to boolean type: "true"
// ..on the client side, then you could actually change the condition to be: if (request.Update)
if (request.Update == 1) {
// Creating database connection using (I assume) EntityFramework
using (var picturesEntities = new picturesEntities()) {
// Building a dictionary for fast lookup. Key, Value as the 0, 1 arg respectfully
var usersDataToUpdate = request.Positions.ToDictionary(p => p[0], p => p[1]);
// Finding the entries that needs to be updated
var usersEntitiesToUpdate = picturesEntities.userTables.Where(cntry => usersDataToUpdate.ContainsKey(cntry.Id));
// Iterating over the entities
foreach (var userEntity in usersEntitiesToUpdate) {
// Updating their position.
userEntity.Position = usersDataToUpdate[userEntity.Id];
}
picturesEntities.SaveChanges();
}
}
// Probably you wanted to return something here, but it's probably an ajax and you can skip that.
}

Unit Testing issue with Moq and Include (EF6)

I have done fair bit research and tried all sorts of different ways of getting the Test to pass but now i need some help.
I am trying to test the following repository method:
public class PortalsRepository : BaseRepository<PortalDomainRole>, IPortalsRepository
{
public PortalsRepository(IAuthDbContext context) : base(context)
{
}
public IEnumerable<PortalRole> GetRoles(string domainName)
{
return Set.Include(x => x.PortalDomain)
.Include(x => x.PortalRole)
.Where(x => x.PortalDomain.Name.ToLower() == domainName)
.Select(x => x.PortalRole)
.ToList();
}
}
The Context looks like:
public interface IAuthDbContext : IDbContextBase
{
}
public interface IDbContextBase
{
IDbSet<T> Set<T>() where T : class;
IEnumerable<DbValidationError> GetEntityValidationErrors();
int SaveChanges();
Task<int> SaveChangesAsync();
Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
My Unit Test Set-up Looks like:
protected override void GivenThat()
{
var mockRolesSet = GetMockDbSet(PortalRoles().AsQueryable());
mockRolesSet.Setup(x => x.Include("PortalRole")).Returns(mockRolesSet.Object);
var mockDomainsSet = GetMockDbSet(PortalDomains().AsQueryable());
mockDomainsSet.Setup(x => x.Include("PortalDomain")).Returns(mockDomainsSet.Object);
var mockPortalDomanRolesSet = GetMockDbSet(PortalDomainRoles().AsQueryable());
mockPortalDomanRolesSet.Setup(x => x.Include("PortalRole")).Returns(mockPortalDomanRolesSet.Object);
mockPortalDomanRolesSet.Setup(x => x.Include("PortalDomain")).Returns(mockPortalDomanRolesSet.Object);
var customDbContextMock = new Mock<IAuthDbContext>();
customDbContextMock.Setup(x => x.Set<PortalRole>()).Returns(mockRolesSet.Object);
customDbContextMock.Setup(x => x.Set<PortalDomain>()).Returns(mockDomainsSet.Object);
customDbContextMock.Setup(x => x.Set<PortalDomainRole>()).Returns(mockPortalDomanRolesSet.Object);
ClassUnderTest = new PortalsRepository(customDbContextMock.Object);
}
My Unit Test Supporting Methods:
public List<PortalDomainRole> PortalDomainRoles()
{
var data = new List<PortalDomainRole>
{
new PortalDomainRole { PortalRoleId = 2, PortalDomainId = 1},
new PortalDomainRole { PortalRoleId = 1, PortalDomainId = 2},
new PortalDomainRole { PortalRoleId = 2, PortalDomainId = 2}
};
return data;
}
public List<PortalDomain> PortalDomains()
{
var data = new List<PortalDomain>
{
new PortalDomain { Name = "google.co.uk", PortalDomainId = 1 },
new PortalDomain { Name = "bbc.com", PortalDomainId = 2 }
};
return data;
}
public List<PortalRole> PortalRoles()
{
var data = new List<PortalRole>
{
new PortalRole {Name = "New Products", PortalRoleId = 1},
new PortalRole {Name = "Classic Products", PortalRoleId = 2}
};
return data;
}
When the unit test executes the method in question, i get:
System.NullReferenceException : Object reference not set to an instance of an object.
Most likely it does not know how to handle the nested include statements - i have followed many online questions and tutorials and now i am stuck.
My answer is probably a bit controversial, but in my experience, the best way to test your repository layer (or whatever you call the code that does the actual data access), is by having it actually call the database during testing.
When you are writing your unit test, you are assuming that Entity Framework works in a specific way. But sometimes it works in different ways than you expect, and thus a test may pass, even though the code doesn't work correctly.
Take this example, that illustrates the problem (the last EF version I worked with was version 4, but I assume that my statement is still true for EF6)
public class Foo {
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public bool Active {
get { return StartDate < DateTime.Now && EndDate > DateTime.Now }
}
}
public class FooRepository {
public IEnumerable<Foo> ActiveFoos { get { return DataContext.Foos.Where(x => x.Active) } }
}
Testing this FooRepository against a mocked data access will pass, but executing against a real database will throw an exception. That is because EF will try to create an SQL clause for the Where(x => x.Active), but because Active is not a field in the database, EF will have no idea how to translate the query to SQL.
So your unit test provides a false positive. Executing the tests against the database will make it fail, as it should.

Restful Web Api parameter as an array of int

Ideally I would like to have an URL in following format:
/api/categories/1,2,3...N/products
And this would return all products for the specified categories. Having one API call with multiple category IDs saves me several database calls, thus improves performance.
I can easily implement this in a following way.
public HttpResponseMessage GetProducts(string categoryIdsCsv)
{
// <1> Split and parse categoryIdsCsv
// <2> Get products
}
However, this doesn't look like a clean clean solution, and possibly breaking SRP principle. I also tried using ModelBinder, however it adds parameters to query string.
Questions:
Is there a clean way to implement such URL structure?
Or is there a different/better approach to retrieve all products for multiple categories?
Please let me know if you need any further clarification.
I've just found an answer to my question. Route attribute had missing parameter when using ModelBinder.
[Route("api/categories/{categoryIds}/products")]
public HttpResponseMessage GetProducts([ModelBinder(typeof(CategoryIdsModelBinder))] CategoryIds categoryIds)
{
// <2> Get products using categoryIds.Ids
}
And CategoryIds would be
public class CategoryIds
{
public List<int> Ids{ get; set; }
}
And CategoryIdsModelBinder would be
public class CategoryIdsModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(CategoryIds))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
var key = val.RawValue as string;
if (key == null)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type");
return false;
}
var values = val.AttemptedValue.Split(',');
var ids = new List<int>();
foreach (var value in values)
{
int intValue;
int.TryParse(value.Trim(), out intValue);
if (intValue > 0)
{
ids.Add(intValue);
}
}
if (ids.Count > 0)
{
var result = new CategoryIds
{
Ids= ids
};
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(
bindingContext.ModelName, "Cannot convert value to Location");
return false;
}
We can use Post methods
[RoutePrefix ( "api/categories" )]
public class TestController
{
[HttpPost]
[Route ( "getProducts" )]
public HttpResponseMessage GetProducts ( HttpRequestMessage request )
{
HttpResponseMessage message = null;
string input = string.Empty;
input = request.Content.ReadAsStringAsync ().Result;
var ids = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>> ( input );
}
}
Unfortunately Web API can not parse your data as array or as some kind of your custom object out of the box.
If you want to parse your url param as array you can try to do:
Write your own route constraint which will read and convert your param from string to array of ints/strings/whatever;
Write your custom type converter and use it with your data model;
write your value provider and also use it with your data model
Use parameter binding
Moreover you can always use query params which is never will break principles of REST :)
Please see more details about here and here
Hope that helps

Categories

Resources