【问题标题】:How to use the same generic method for validation of different types如何使用相同的泛型方法来验证不同类型
【发布时间】:2018-08-16 06:10:25
【问题描述】:

我有一个接口 IValidator,它有一个通用方法 Validate,旨在验证应用程序中的不同对象。现在我正在验证用户的个人资料和登录凭据。问题是,当我尝试验证配置文件或登录类时,即使我尝试验证登录详细信息,它也总是调用配置文件的方法。代码如下:

验证器

 public interface IValidator
{
    List<Error> Validate<T>(T request);
}

用户配置文件验证器

 public class UserProfileValidator : IValidator 
{
    public List<Error> Validate<T>(T request)
    {
       //Code for profile validation
    }
}

用户登录验证器

 public class UserLoginValidator: IValidator 
{
    public List<Error> Validate<T>(T request)
    {
       //Code for login validation
    }
}

用户服务

   private readonly IValidator _validator; 

   public UserService(IValidator validator)
    {
        _validator = validator;
    }


 public async Task<Response> Login(UserDetail userDetail)
 {
     //this is supposed to validate login details, but validates profile instead
        var errors = _validator.Validate(userDetail); 
 }

public async Task<Response> UpdateProfile(Profile profile)
 {
        var errors = _validator.Validate(profile);
 }

请指导。谢谢

【问题讨论】:

  • 这取决于你如何创建用户服务对象。你能告诉你如何创建用户服务对象吗?
  • 很可能您将UserProfileValidator 传递给您的UserService 因此行为。
  • 我只是将 UserService 注入到 UserController 的构造函数中,然后调用 UpdateProfile 或 Login 方法
  • 你在使用 DI 吗?你如何注册你的UserService,因为它的构造函数需要IValidator?
  • 是的,我在 autofac 的帮助下使用 DI。这里我们的代码--- builder.RegisterType().As();

标签: c# asp.net asp.net-mvc asp.net-web-api


【解决方案1】:

虽然它可能不是解决 OP 问题的方法,但我发布了这个。

根据你所说的你正在使用 AutoFac。

服务:

builder.RegisterType<UserService>().As<IUserService>();

验证器:

builder.RegisterAssemblyTypes(typeof(DependencyBuilder).Assembly).Where(t => t.Name.EndsWith("Validator")).AsImplementedInterfaces().InstancePerLifetimeScope(); 

看看你如何注册你的UserService,你没有向它的构造函数提供一个特定的IValidator参数,我认为这就是AutoFac继续使用UserProfileValidator的原因。从逻辑上讲,UserService 如何知道IValidator 在运行时使用什么实现?

我经常使用以下来为我的构造函数指定参数。

builder
    .Register(r => new UserService(r.Resolve<IValidator>()))
    .As<IUserService>()
    .InstancePerRequest();

但是这个问题是如果 DI 将解决 IValidator 它仍然不具体。

我建议重新设计您的代码,因为我认为您当前的设计不允许UserService 同时使用UserProfileValidator 和UserLoginValidator。

可能为每个视图模型分配IValidator 可能是一种更好的方法,这样每个视图模型都有自己的IValidator 实现,然后您可以执行var errors = viewModel.Validate(); 之类的操作。

【讨论】:

  • 好的,那我是注入单个对象进行验证(IValidator)还是在用户服务中注入单独的对象? (IValidator 和 IValidator)
【解决方案2】:

您应该将界面更改为:

public interface IValidator<T>
{
    List<Error> Validate(T request);
}

【讨论】:

  • 为了?请为您的答案提供解释。
  • 我试过了,但它无法向 autofac 注册,当我注册两个验证器类时,它显示的行为与我在问题中提到的相同。
【解决方案3】:

您必须在默认 DI 容器或 Autofac 中使用某些依赖注入注册。请详细说明您是如何这样做的。

您的接口不是通用的,这意味着您的依赖注入容器正在为同一个接口注册多个实现,并且当您注入它时,它会采用最后一个实现。

界面改为

public interface IValidator<T>
{
    List<Error> Validate(T request);
}

并注入一个服务IValidator&lt;UserDetail&gt; 和另一个IValidator&lt;Profile&gt;,因此本质上存在使用相同接口的不同验证器。

当实现是泛型时使用泛型是可以的,但在你的情况下,每个验证器都是不同的,因此 DI 需要确切知道要使用哪个。

在 autofac 中你应该有类似的东西

builder.RegisterType<UserLoginValidator>().As<IValidator<UserDetails>>();
builder.RegisterType<ProfileValidator>().As<IValidator<Profile>>();

【讨论】:

  • 这里是所有在类末尾包含'Validator'关键字的类的注册代码。以这种方式注册所有验证器类:=> builder.RegisterAssemblyTypes(typeof(DependencyBuilder).Assembly).Where(t => t.Name.EndsWith("Validator")).AsImplementedInterfaces().InstancePerLifetimeScope();
  • 好的,那我是注入单个对象进行验证(IValidator)还是在用户服务中注入单独的对象? (IValidator 和 IValidator)
【解决方案4】:

感谢大家的帮助,它确实帮助我解决了这个问题。我使用 Autofac 的键控服务将两个验证器都注册为 IValidator,然后根据需要通过索引解析。

看到这个: https://autofaccn.readthedocs.io/en/latest/advanced/keyed-services.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多