将具体类注入泛型c#.net核心
我创建了一个泛型类:将具体类注入泛型c#.net核心,c#,generics,dependency-injection,C#,Generics,Dependency Injection,我创建了一个泛型类: public class GenericCreate<T> : IRequest<Attempt<T>> where T: class { public T Model { get; } public GenericCreate(T model) => Model = model; } public class GenericCreateHandler<T> : IRequestHandler<G
public class GenericCreate<T> : IRequest<Attempt<T>> where T: class
{
public T Model { get; }
public GenericCreate(T model) => Model = model;
}
public class GenericCreateHandler<T> : IRequestHandler<GenericCreate<T>, Attempt<T>> where T : class
{
private readonly NotNullValidator<T> _validator;
private readonly DatabaseContext _databaseContext;
public GenericCreateHandler(NotNullValidator<T> validator, DatabaseContext databaseContext)
{
_validator = validator;
_databaseContext = databaseContext;
}
public async Task<Attempt<T>> Handle(GenericCreate<T> request, CancellationToken cancellationToken)
{
var generic = request.Model;
var validationAttempt = _validator.Validate(generic).ToAttempt();
if (validationAttempt.Failure) return validationAttempt.Error;
_databaseContext.Add(generic);
await _databaseContext.SaveChangesAsync(cancellationToken);
return generic;
}
}
但我想说的是:
public class CategoryValidator: NotNullValidator<Category>
{
public CategoryValidator()
{
RuleFor(m => m.Id).NotEmpty();
RuleFor(m => m.Name).NotEmpty();
}
}
公共类CategoryValidator:NotNullValidator
{
公共类别验证器()
{
RuleFor(m=>m.Id).NotEmpty();
RuleFor(m=>m.Name).NotEmpty();
}
}
可以想象,我为项目中的每个实体都有一个验证器类,因此能够获得正确的验证器非常重要。
有人知道我如何使用.net core做到这一点吗?正如第一个基于约定的示例所承诺的:
//列出名称中包含验证器的所有类,您可能希望在启动时这样做,以避免每次需要创建类时的开销
//您可能还想更改正在搜索的程序集。
var validators=Assembly.getExecutionGassembly().DefinedTypes.Where(t=>t.Name.Contains(“Validator”).ToList();
//您将能够从类型参数T(即typeof(T))中获得它
变量类型=类型(MyClass);
//这是您的约定=>验证程序
var validatorouse=validators.Single(v=>v.Name==$“{type.Name}Validator”);
//实例化验证器(您应该转换为抽象类型以更容易地使用它)
var obj=Activator.CreateInstance(ValidatorUse);
如果需要,可以使用其他方法实例化验证器:只是为了澄清一下,以防其他人有此问题。 我使用响尾蛇回答并创建此扩展方法:
public static class ValidatorExtensions
{
public static NotNullValidator<T> GetValidator<T>()
{
// List all classes containing validators in their name, you might want to do this at startup to avoid the overhead each time you need to create a class
// You might also want to change which assembly you are searching in.
var validators = Assembly.GetExecutingAssembly().DefinedTypes.Where(t => t.Name.Contains("Validator")).ToList();
// You'll be able to get that from your type parameter T (i.e typeof(T))
var type = typeof(T);
//This is your convention => <ClassName>Validator
var validatorToUse = validators.Single(v => v.Name == $"{type.Name}Validator");
//Instantiate your validator (you should cast to your abstract type to use it more easily)
return (NotNullValidator<T>) Activator.CreateInstance(validatorToUse);
}
}
公共静态类验证扩展
{
公共静态NotNullValidator GetValidator()
{
//列出所有名称中包含验证器的类,您可能希望在启动时这样做,以避免每次需要创建类时的开销
//您可能还想更改正在搜索的程序集。
var validators=Assembly.getExecutionGassembly().DefinedTypes.Where(t=>t.Name.Contains(“Validator”).ToList();
//您将能够从类型参数T(即typeof(T))中获得它
var类型=类型(T);
//这是您的约定=>验证程序
var validatorouse=validators.Single(v=>v.Name==$“{type.Name}Validator”);
//实例化验证器(您应该转换为抽象类型以更容易地使用它)
return(NotNullValidator)Activator.CreateInstance(validatorUse);
}
}
然后我可以像这样更新我的泛型类:
public class GenericCreate<T> : IRequest<Attempt<T>> where T: class
{
public T Model { get; }
public GenericCreate(T model) => Model = model;
}
public class GenericCreateHandler<T> : IRequestHandler<GenericCreate<T>, Attempt<T>> where T : class
{
private readonly NotNullValidator<T> _validator;
private readonly DatabaseContext _databaseContext;
public GenericCreateHandler(DatabaseContext databaseContext)
{
_validator = ValidatorExtensions.GetValidator<T>();
_databaseContext = databaseContext;
}
public async Task<Attempt<T>> Handle(GenericCreate<T> request, CancellationToken cancellationToken)
{
var generic = request.Model;
var validationAttempt = _validator.Validate(generic).ToAttempt();
if (validationAttempt.Failure) return validationAttempt.Error;
_databaseContext.Add(generic);
await _databaseContext.SaveChangesAsync(cancellationToken);
return generic;
}
}
公共类GenericCreate:IRequest其中T:class
{
公共T模型{get;}
公共通用创建(T模型)=>model=model;
}
公共类GenericCreateHandler:IRequestHandler其中T:class
{
私有只读NotNullValidator\u validator;
专用只读DatabaseContextu DatabaseContext;
公共GenericCreateHandler(DatabaseContext DatabaseContext)
{
_validator=ValidatorExtensions.GetValidator();
_databaseContext=databaseContext;
}
公共异步任务句柄(GenericCreate请求、CancellationToken CancellationToken)
{
var generic=request.Model;
var validationAttempt=_validator.Validate(通用).ToAttempt();
if(validationAttempt.Failure)返回validationAttempt.Error;
_databaseContext.Add(通用);
wait_databaseContext.saveChangesSync(cancellationToken);
返回泛型;
}
}
注意构造器:
_validator = ValidatorExtensions.GetValidator<T>();
\u validator=ValidatorExtensions.GetValidator();
这让我的测试顺利通过:
[TestFixture]
public class GenericCreateShould
{
[Test]
public async Task ThrowValidationErrorWhenGenericIsInvalid()
{
// Assemble
var services = GenericCreateContext<Venue>.GivenServices();
var handler = services.WhenCreateHandler();
// Act
var response = await handler.Handle(new GenericCreate<Venue>(new Venue()), CancellationToken.None);
// Assert
response.Success.Should().BeFalse();
response.Result.Should().BeNull();
response.Error.Should().BeOfType<ValidationError>();
response.Error.Message.Should().Be("'Name' must not be empty.");
}
[Test]
public async Task ReturnGeneric()
{
// Assemble
var services = GenericCreateContext<Venue>.GivenServices();
var handler = services.WhenCreateHandler();
var model = new GenericCreate<Venue>(new Venue
{
Name = "Main branch"
});
// Act
var response = await handler.Handle(model, CancellationToken.None);
// Assert
response.Success.Should().BeTrue();
response.Error.Should().BeNull();
response.Result.Should().BeOfType<Venue>();
}
}
public class GenericCreateContext<T, TKey> : DatabaseContextContext where T: TClass<TKey>
{
public static GenericCreateContext<T, TKey> GivenServices() => new GenericCreateContext<T, TKey>();
public GenericCreateHandler<T> WhenCreateHandler() => new GenericCreateHandler<T>(DatabaseContext);
}
public class GenericCreateContext<T> : GenericCreateContext<T, int> where T: TClass<int>
{
}
[TestFixture]
公共类GenericCreateShould
{
[测试]
公共异步任务ThrowValidationErrorHengenerisInvalid()
{
//集合
var services=GenericCreateContext.GivenServices();
var handler=services.WhenCreateHandler();
//表演
var response=await handler.Handle(new GenericCreate(new Venue()),CancellationToken.None);
//断言
response.Success.Should().BeFalse();
response.Result.Should().BeNull();
response.Error.Should().BeOfType();
response.Error.Message.Should().Be(“'Name'不能为空”);
}
[测试]
公共异步任务ReturnGeneric()
{
//集合
var services=GenericCreateContext.GivenServices();
var handler=services.WhenCreateHandler();
var模型=新的通用创建(新场地)
{
Name=“主要分支机构”
});
//表演
var response=await handler.Handle(model,CancellationToken.None);
//断言
response.Success.Should().BeTrue();
response.Error.Should().BeNull();
response.Result.Should().BeOfType();
}
}
公共类GenericCreateContext:DatabaseContextContext,其中T:TClass
{
公共静态GenericCreateContext GivenServices()=>new GenericCreateContext();
public GenericCreateHandler WhenCreateHandler()=>新建GenericCreateHandler(DatabaseContext);
}
公共类GenericCreateContext:GenericCreateContext,其中T:TClass
{
}
第一个测试通过了,这就是问题所在,因为
NotNullValidator
不包含名称等规则,但是VenueValidator
包含这些规则;这意味着扩展方法正在工作。我看到了三种方法:属性、配置或约定。对于第一个,定义一个attibute,您将在描述需要注入的验证器的类上声明它。对于第二种情况,您可以有一个配置类,该类将每个类映射到它的验证器(可能变得非常冗长)。或者第三,您需要建立一个约定,例如基于类的名称,并使用反射来查找相应的验证器。我不知道如何执行您提到的任何选项,但是对于第三个,除了第二个选项之外,我所有的验证器都被命名为像validator
,第一个和第三个是基于反射的。今天晚些时候,我会尝试在回答中写下一些例子。我也许会开始
[TestFixture]
public class GenericCreateShould
{
[Test]
public async Task ThrowValidationErrorWhenGenericIsInvalid()
{
// Assemble
var services = GenericCreateContext<Venue>.GivenServices();
var handler = services.WhenCreateHandler();
// Act
var response = await handler.Handle(new GenericCreate<Venue>(new Venue()), CancellationToken.None);
// Assert
response.Success.Should().BeFalse();
response.Result.Should().BeNull();
response.Error.Should().BeOfType<ValidationError>();
response.Error.Message.Should().Be("'Name' must not be empty.");
}
[Test]
public async Task ReturnGeneric()
{
// Assemble
var services = GenericCreateContext<Venue>.GivenServices();
var handler = services.WhenCreateHandler();
var model = new GenericCreate<Venue>(new Venue
{
Name = "Main branch"
});
// Act
var response = await handler.Handle(model, CancellationToken.None);
// Assert
response.Success.Should().BeTrue();
response.Error.Should().BeNull();
response.Result.Should().BeOfType<Venue>();
}
}
public class GenericCreateContext<T, TKey> : DatabaseContextContext where T: TClass<TKey>
{
public static GenericCreateContext<T, TKey> GivenServices() => new GenericCreateContext<T, TKey>();
public GenericCreateHandler<T> WhenCreateHandler() => new GenericCreateHandler<T>(DatabaseContext);
}
public class GenericCreateContext<T> : GenericCreateContext<T, int> where T: TClass<int>
{
}