Il semble que la référence circulaire avait à voir avec le fait que la couche de service dépendait ModelState du contrôleur et le contrôleur en fonction de la couche de service.
je devais réécrire ma couche de validation pour obtenir ce travail. Voici ce que j'ai fait.
Define interface validateur générique comme ci-dessous:
public interface IValidator<TEntity>
{
ValidationState Validate(TEntity entity);
}
Nous voulons être en mesure de retourner une instance de ValidationState qui, évidemment, définit l'état de validation. Notez que nous avons une collection d'erreurs fortement typée que nous devons également définir. La collection va être constituée d'objets ValidationError contenant le nom de la propriété de l'entité que nous validons et le message d'erreur qui lui est associée. Cela suit simplement l'interface ModelState standard.
public class ValidationErrorCollection : Collection<ValidationError>
{
public void Add(string property, string message)
{
Add(new ValidationError(property, message));
}
}
Et voici ce que le ValidationError ressemble:
public class ValidationError
{
private string _property;
private string _message;
public string Property
{
get
{
return _property;
}
private set
{
_property = value;
}
}
public string Message
{
get
{
return _message;
}
private set
{
_message = value;
}
}
public ValidationError(string property, string message)
{
Property = property;
Message = message;
}
}
Le reste de c'est magique StructureMap. Nous devons créer une couche de service de validation qui va localiser les objets de validation et valider notre entité.Je voudrais définir une interface pour cela, puisque je veux que quiconque utilisant le service de validation soit complètement inconscient de la présence de StructureMap. En outre, je pense que Sprinkling ObjectFactory.GetInstance() n'importe où en dehors de la logique du bootstrapper une mauvaise idée. Le garder centralisé est un bon moyen d'assurer une bonne maintenabilité. Quoi qu'il en soit, j'utilise le modèle décorateur ici:
public interface IValidationService
{
ValidationState Validate<TEntity>(TEntity entity);
}
que nous mettons enfin:
public class ValidationService : IValidationService
{
#region IValidationService Members
public IValidator<TEntity> GetValidatorFor<TEntity>(TEntity entity)
{
return ObjectFactory.GetInstance<IValidator<TEntity>>();
}
public ValidationState Validate<TEntity>(TEntity entity)
{
IValidator<TEntity> validator = GetValidatorFor(entity);
if (validator == null)
{
throw new Exception("Cannot locate validator");
}
return validator.Validate(entity);
}
#endregion
}
Je vais être en utilisant le service de validation dans mon contrôleur. Nous pourrions le déplacer vers la couche de service et avoir StructureMap utiliser l'injection de propriété pour injecter une instance de ModelState du contrôleur à la couche de service, mais je ne veux pas que la couche de service soit couplée avec ModelState. Et si nous décidions d'utiliser une autre technique de validation? C'est pourquoi je préfère le mettre dans le contrôleur. Voici ce que mon contrôleur ressemble à:
public class PostController : Controller
{
private IEntityService<Post> _service = null;
private IValidationService _validationService = null;
public PostController(IEntityService<Post> service, IValidationService validationService)
{
_service = service;
_validationService = validationService;
}
}
Ici je suis injectais ma couche de service et les instances de service à l'aide validaton StructureMap. Donc, nous devons enregistrer les deux dans le registre StructureMap:
ForRequestedType<IValidationService>()
.TheDefaultIsConcreteType<ValidationService>();
ForRequestedType<IValidator<Post>>()
.TheDefaultIsConcreteType<PostValidator>();
C'est tout. Je ne montre pas comment j'implémente mon PostValidator, mais c'est simplement l'implémentation de l'interface IValidator et la définition de la logique de validation dans la méthode Validate(). Il ne vous reste plus qu'à appeler votre instance de service de validation pour récupérer le validateur, appeler la méthode validate sur votre entité et écrire les erreurs dans ModelState.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "PostId")] Post post)
{
ValidationState vst = _validationService.Validate<Post>(post);
if (!vst.IsValid)
{
foreach (ValidationError error in vst.Errors)
{
this.ModelState.AddModelError(error.Property, error.Message);
}
return View(post);
}
...
}
espoir j'ai aidé quelqu'un avec cette :)
La coloration syntaxique est un peu hors sur StackOverflow :) TEntity est un type générique , pas un objet de classe. Il est simplement noir dans Visual Studio. J'utilise TEntity pour simplement passer le type d'objet que je veux valider. Je suis d'accord avec vous sur la validation dans le contrôleur. Je n'ai pas trouvé de meilleure façon de valider cela, ce qui me donnerait beaucoup de souplesse. En procédant de cette façon, je peux utiliser n'importe quel cadre de validation que je veux dans mon validateur d'entité. –
Maintenant, je n'y ai pas beaucoup réfléchi, mais ce qui pourrait fonctionner est d'utiliser le modèle d'adaptateur pour écrire un adaptateur pour ModelState. Donc, essentiellement, vous devez valider votre entité, obtenir l'objet ValidationState et utiliser l'adaptateur pour le convertir en ModelState. Si vous l'avez suffisamment abstrait, vous pouvez utiliser StructureMap pour injecter l'adaptateur de votre choix. Je ne suis pas sûr à quel point cela serait lié à votre contrôleur, mais je suis sûr que vous pourriez le découpler assez bien. De cette façon, votre service de validation pourrait probablement être utilisé au sein de vos modèles commerciaux/entités ou de la couche de service. Je vais voir si je peux faire quelque chose avec ça. –
Je regardais à la suite de cela, http://www.asp.net/Learn/mvc/tutorial-38-cs.aspx. A l'origine, je n'avais pas pensé à l'option car je pensais qu'elle couplerait trop ma couche de service à MVC mais à la minute, pour faire avancer mon projet, ça ressemble au meilleur d'un mauvais groupe. Je suis très désireux de trouver une alternative plus lâche cependant. Si vous venez avec quelque chose me déposer un email à lloyd phillips tout un mot à xtra dot co dot nz. Cordialement Lloyd –