我有Unity Ioc注入在全局過濾器以及Action和Controller過濾器中工作。現在我需要讓IoC在驗證屬性中工作。如何在MVC驗證屬性中使用Unity IoC?
在MVC5中實現這一點的最佳方法是什麼?
乾杯邁克
我有Unity Ioc注入在全局過濾器以及Action和Controller過濾器中工作。現在我需要讓IoC在驗證屬性中工作。如何在MVC驗證屬性中使用Unity IoC?
在MVC5中實現這一點的最佳方法是什麼?
乾杯邁克
好的,我已經適應了上面建議的Unity DI容器。
下面是新模型驗證
public class UnityModelValidator : DataAnnotationsModelValidator
{
private readonly IUnityContainer unityContainer;
public UnityModelValidator(ModelMetadata metadata,
ControllerContext context,
ValidationAttribute attribute)
: base(metadata, context, attribute)
{
this.unityContainer = DependencyResolver.Current.GetService<IUnityContainer>();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
try
{
unityContainer.BuildUp(Attribute.GetType(), Attribute);
}
catch (ResolutionFailedException ex)
{
//Don't understand why it sometimes tries to use Unity to create an attribute rather than just build up an existing object. If this happens it can fail but we want to ignore it.
}
ValidationContext context = CreateValidationContext(container);
ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context);
if (result != ValidationResult.Success)
{
yield return new ModelValidationResult
{
Message = result.ErrorMessage
};
}
}
protected virtual ValidationContext CreateValidationContext(object container)
{
var context = new ValidationContext(container ?? Metadata.Model, new UnityServiceLocator(unityContainer), null);
context.DisplayName = Metadata.GetDisplayName();
return context;
}
}
以及執行所述屬性堆積它也增加了容器作爲的ServiceProvider到上下文,因此它可以直接在該屬性中使用。我知道這是一種反模式,我不想自己使用它,但爲了完整起見,這裏包括了它。
這裏是統一配置代碼
container.RegisterType<ModelValidator, UnityModelValidator>(new TransientLifetimeManager());
DataAnnotationsModelValidatorProvider.RegisterDefaultAdapterFactory(
(metadata, context, attribute) => container.Resolve<ModelValidator>(new ParameterOverrides() { { "metadata", metadata }, { "context", context }, { "attribute", attribute } }));
最後,這意味着我們可以建立我們的驗證屬性,這樣
public class InitialsMustBeAvailableAttribute : ValidationAttribute
{
[Dependency]
public IUserService UserService { get; set; }
public override bool IsValid(object value)
{
return UserService.AreInitialsAvailable((string)value);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var userService = (IUserService)validationContext.GetService(typeof(IUserService));
if (userService != null)
{
if (userService.AreInitialsAvailable((string) value))
{
return null;
}
}
return new ValidationResult("Initals must be unique!");
}
}
請注意,您只會實際使用這些方法之一。第一個使用已注入的UserService,第二個使用上下文中的ServiceLocator來抓取它自己。
我更喜歡前者。
乾杯邁克
我已經稍微更改了代碼。在某些情況下,它試圖從框架構建屬性時失敗。它會因錯誤而失敗,'找到1個參數的多個構造函數不能消除歧義'。不知道爲什麼它需要訪問構造函數來執行構建,但我們可以放心地忽略該錯誤。 –
有沒有好方法注入依賴關係驗證屬性。但是,有兩種解決方法(http://forloop.co.uk/blog/resolving-ioc-container-services-for-validation-attributes-in-asp.net-mvc):
Validator.ValidateObject
不反對你的建議,但有些人認爲'服務定位器'是[反模式](http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) – 2013-12-18 19:44:23
@Veleous - I知道,但在某些情況下(像這樣),沒有更好的辦法。我認爲如果你故意這樣做,使用反模式沒有任何問題。 –
有沒有人有使用Unity容器而不是Castle Windsor容器的自定義ModelValidator的示例? –
看看[這個答案在CodePlex上(https://simpleinjector.codeplex.com/discussions/472187#post1137461)。它討論從屬性中提取邏輯並將其放入服務中。雖然是關於Web API和Simple Injector,但我認爲你會發現它很有用,因爲Unity的解決方案几乎是一樣的。 – Steven