[英]FluentValidation using validator on wrong viewmodel
我是第一次使用 FluentValidation。 我有一些基本的驗證工作,但后來我意識到我需要為一些更復雜的驗證做一些數據庫檢索。 這需要進行依賴注入,以便我可以使用數據庫服務,這使我進入了當前狀態:卡住了。 我不能讓它工作。
為簡化起見,我假設我的應用程序正在處理體育聯盟和球隊,因為我認為這比合同、發票、資金來源、供應商和分包商更容易理解。 :-)
所以,假設我有一個體育聯盟的視圖模型。 在該視圖模型中,有該聯盟中球隊的一組視圖模型。
我有一個屏幕可以編輯聯賽。 同一個屏幕允許更改有關該聯盟中球隊的一些信息。
聯賽的視圖模型包含球隊的視圖模型列表。
[FluentValidation.Attributes.Validator(typeof(LeagueValidator))]
public class LeagueViewModel
{
public string LeagueName { get; set; }
public DateTime SeasonBeginDate { get; set; }
public DateTime SeasonEndDate { get; set; }
public List<TeamViewModel> TeamViewModels { get; set; }
}
我為 LeagueViewModel 創建了一個驗證器。 不幸的是,當我編輯聯賽並單擊提交按鈕時,我收到以下錯誤消息:
InvalidCastException:無法將“TeamViewModel”類型的對象轉換為“LeagueViewModel”類型。 在 FluentValidation.ValidationContext.ToGenericT
顯然它正在嘗試使用 LeagueValidator 驗證 TeamViewModel。
我經歷了許多變化,試圖弄清楚如何讓它發揮作用。 這是我目前所擁有的。
public class LeagueValidator : AbstractValidator<LeagueViewModel>
{
private readonly ILeagueService _leagueService;
public LeagueValidator(ILeagueService leagueService)
{
_leagueService = leagueService;
RuleFor(x => x.SeasonEndDate)
.NotNull()
.GreaterThan(x => x.SeasonBeginDate)
.WithMessage("Season End Date must be later than Season Begin Date.");
}
}
(LeagueService 位在那里,因為在實際代碼中它需要檢查一些數據庫值,它使用服務來檢索。)
請注意,LeagueValidator 沒有針對 TeamViewModel 列表中的任何字段的任何驗證規則。
public class LeagueValidatorFactory : ValidatorFactoryBase
{
private readonly Container _container;
public LeagueValidatorFactory(Container container)
{
_container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return _container.GetInstance<LeagueValidator>();
}
}
我們使用 SimpleInjector 進行 DI。 作為現有設置的一部分,它正在調用注冊服務的方法。 在該方法中,我添加了對此的調用:
private static void RegisterValidators(Container container)
{
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
var leagueValidatorProvider =
new FluentValidationModelValidatorProvider(new LeagueValidatorFactory(container));
leagueValidatorProvider.AddImplicitRequiredValidator = false;
ModelValidatorProviders.Providers.Add(leagueValidatorProvider);
container.Register<LeagueValidator>();
}
我想我一定是誤解了一些基本的東西。
下面史蒂文的回答讓我指出了正確的方向! 在我進行了他建議的更改后,我遇到了另一個錯誤。 一旦我解決了這個問題,它就起作用了! 以下是我為使上述代碼正常工作所做的更改。
我刪除了這一行,因為它不是必需的。
[FluentValidation.Attributes.Validator(typeof(LeagueValidator))]
我將其重命名為“ValidatorFactory”,因為無論我創建了多少個驗證器,結果證明只有一個驗證器工廠。 然后我將 CreateInstance 方法更改為:
public override IValidator CreateInstance(Type validatorType)
{
if (_container.GetRegistration(validatorType) == null)
{
return null;
}
return (IValidator)_container.GetInstance(validatorType);
}
這不再明確指定要獲取的驗證器類型(這就是為什么只需要一個工廠的原因)。 為了確定給定類型的驗證器是否可用,它調用 GetRegistration,如果沒有找到則返回 null。
這很重要! 對於每個視圖模型,它都會嘗試找到一個驗證器。 如果沒有此 null 檢查,則會拋出 InvalidCastException。
按照史蒂文的建議,我用這個替換了 container.Register 行:
container.Register(typeof(IValidator<>), new[] { typeof(SimpleInjectorInitializer).Assembly });
這避免了每次添加新驗證器時都需要明確列出每個驗證器。
現在一切正常! 非常感謝您的幫助,史蒂文!
我不熟悉 FluentValidation,但考慮到它提供了要驗證的類型,因此您的LeagueValidatorFactory
似乎從容器中請求了錯誤的類型。
因此,我希望您的驗證工廠看起來像這樣:
public class LeagueValidatorFactory : ValidatorFactoryBase
{
private readonly Container _container;
public LeagueValidatorFactory(Container container) =>
_container = container;
public override IValidator CreateInstance(Type validatorType) =>
(IValidator)_container.GetInstance(validatorType);
}
我從 FluentValidator 源代碼中可以看到, validatorType
是IValidator<T>
類型的封閉泛型版本, T
是被驗證的實際類型。 這意味着,您必須通過它們的IValidator<T>
接口注冊驗證器。 例如:
container.Register<IValidator<LeagueViewModel>, LeagueValidator>();
這種配置即代碼(或顯式注冊)模型,您使用一行代碼顯式注冊每個驗證器,如果您只有幾個驗證器,可能會正常工作,但這通常會導致必須經常更新的組合根.
因此,更好的模型是使用Auto-Registration ,您可以在其中使用反射注冊所有IValidator<T>
實現。 幸運的是,您不必自己實現; Simple Injector 支持您:
var validatorAssemblies = new[] { typeof(LeagueValidator).Assembly };
container.Register(typeof(IValidator<>), validatorAssemblies);
這可確保您在添加新驗證器(在該特定程序集中)時永遠不必更改組合根。
有了這個設置,我看不出為什么你應該用FluentValidation.Attributes.ValidatorAttribute
標記你的視圖模型。 如果可以,請刪除它,因為它只會導致視圖模型和驗證器之間的不緊密耦合。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.