Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Notifications
Mark all as read
Q&A

Working with a generic class that uses a type that should be of generic type

+0
−0

I have followed Nick Chapsas' tutorial to avoid throwing a ValidationException to treat validation errors and instead rely on LanguageExt.Common.Result<> from LanguageExt library.

I have managed to develop a working solution that relies on MediatR (which uses queries, commands and validators for those), but I feel I am violating the DRY principle.

The relevant code is below:

A sample command

public class CreateDummyModelCommand : IRequest<Result<int>>
{
	public string? Code { get; set; }
	public string? Name { get; set; }
}

/// <summary>
/// this relies on Nick Chapsas idea of not using ValidationExceptions, but return Values:
/// https://www.youtube.com/watch?v=a1ye9eGTB98
/// </summary>
internal class CreateDummyModelCommandHandler : IRequestHandler<CreateDummyModelCommand, Result<int>>
{
	private readonly IValidationUtils<CreateDummyModelCommand, int> _validationUtils;

	public CreateDummyModelCommandHandler(IValidationUtils<CreateDummyModelCommand, int> validationUtils)
	{
		_validationUtils = validationUtils;
	}

	public async Task<Result<int>> Handle(CreateDummyModelCommand request, CancellationToken token)
	{
		var validationResult = await _validationUtils.IsValid(request, token);
		if (validationResult.IsFaulted)
			return validationResult;

		var r = new Random();
		return await Task.FromResult(r.Next(1000));
	}
}

Result<A> provides an implicit cast operator that allow to return a Result<A> and the function to return an A.

I will add ValidationUtils class for completeness, but I think it is less relevant in this case:

internal class ValidationUtils<TReq, TRes> : IValidationUtils<TReq, TRes> 
	where TReq : class, IBaseRequest
{
	private readonly IValidator<TReq> _validator;

	public ValidationUtils(IValidator<TReq> validator)
	{
		_validator = validator;
	}

	public async Task<Result<TRes>> IsValid(TReq request, CancellationToken token = default)
	{
		var validationResult = await _validator.ValidateAsync(request, token);
		if (!validationResult.IsValid)
		{
			var validationException = new ValidationException(validationResult.Errors);
			return new Result<TRes>(validationException);
		}

		var dummyObj = Activator.CreateInstance<TRes>();
		return dummyObj;
	}
}

What I do not like is that virtually any command must include that validation logic at the beginning and I feel that it can be put in a MediatR pipeline behaviour that would execute for each command and trigger the validation, if one is defined. My non-working code is currently the following:

internal class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
	where TRequest : class, IRequest<TResponse>, IBaseRequest
	where TResponse: struct
{
	private readonly IValidationUtils<TRequest, TResponse> _validationUtils;

	///
	public ValidationBehaviour(IValidationUtils<TRequest, TResponse> validationUtils)
	{
		_validationUtils = validationUtils;
	}

	public async Task<TResponse> Handle(TRequest request, CancellationToken token,
		RequestHandlerDelegate<TResponse> next)
	{
		bool isResult = typeof(TResponse).GetGenericTypeDefinition() == typeof(Result<>);
		if (!isResult)
			return await next();

		var validationResult = await _validationUtils.IsValid(request, token);
		if (validationResult.IsFaulted)
			return validationResult as dynamic;

		return await next();
	}
}

This compiles, but it does not work because since TResponse is actually a Result<>, validationResult is a Result<Result<>>.

One way would have been to work with another generic type to indicate the underlying type of the Result<>, but I cannot change the behavior's signature. While using Result<> usage is convenient, I could break this dependency and use my own class, but I feel I am reinventing the wheel here.

Any idea about how to make this work?

Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

0 answers

Sign up to answer this question »

This community is part of the Codidact network. We have other communities too — take a look!

You can also join us in chat!

Want to advertise this community? Use our templates!

Like what we're doing? Support us! Donate