Asp.net-Mvc

根據外部因素驗證對象(即數據儲存唯一性)

  • May 4, 2011

描述

我的解決方案有這些項目:

  • DAL = 修改後的實體框架
  • DTO = 能夠自我驗證的數據傳輸對象
  • BL = 業務層服務
  • WEB = 展示 Asp.net MVC 應用程序

DAL、BL 和 WEB 都參考了 DTO,這很棒。

該過程通常以這種方式執行:

  1. 向 WEB 發出 Web 請求
  2. WEB 發布 DTO
  • DTO 通過自定義 ActionFilter 自動驗證
  • 自動收集驗證錯誤
  1. (驗證OK)WEB呼叫BL提供DTO
  2. BL 使用 DTO 呼叫 DAL(可以通過它們或僅使用它們)

DTO驗證問題然後……

我的 DTO 能夠根據自己的狀態(屬性值)驗證自己。但是現在,當情況並非如此時,我遇到了一個問題。我需要他們使用 BL(以及因此 DAL)進行驗證。

我的真實範例:使用者註冊並且 WEB 獲得了一個經過驗證的使用者 DTO。有問題的部分是username驗證。應根據數據儲存檢查其唯一性。

我該怎麼做?

還有其他資訊表明,所有 DTO 都為 IoC 目的和 TDD實現了一個介面(即UserDTO 實現)。IUser兩者都是DTO 項目的一部分。

不可能的嘗試

  1. 我不能在 DTO 中引用 BL,因為我會得到循環引用。

Compilation error 2. 我無法創建一個額外的 DTO.Val 項目來引用部分 DTO 類並在那裡實現它們的驗證(他們會引用 BL + DTO)。

Partial classes can't span assemblies.

可能的嘗試

  1. 創建一個特殊ActionFilter的,可以根據外部條件驗證對象。這將在WEB 項目中創建,因此可以看到將在此處使用的 DTO 和 BL。
  2. 將 DTO 放在 BL 中,並將 DTO 介面保留為其他項目引用的實際 DTO,並重構所有程式碼以使用介面而不是具體類。
  3. 不要處理外部依賴驗證,讓外部依賴拋出異常——這可能是這個問題最糟糕的解決方案

你有什麼建議?

我會建議一個我在過去一周左右才嘗試過的實驗。

基於這個靈感,我正在創建 DTO,其驗證與該DataAnnotations方法的驗證略有不同。範例 DTO:

public class Contact : DomainBase, IModelObject
{
   public int ID { get; set; }
   public string Name { get; set; }
   public LazyList<ContactDetail> Details { get; set; }
   public DateTime Updated { get; set; }


   protected override void ConfigureRules()
   {
       base.AddRule(new ValidationRule()
       {
           Properties = new string[] { "name" },
           Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
           validator = () => this.Name.IsRequired300LenNoSpecial()
       });

       base.AddRule(new ValidationRule()
       {
           Properties = new string[] { "updated" },
           Description = "required",
           validator = () => this.Updated.IsRequired()
       });
   }
}

這可能看起來比DataAnnotations而且還好,這是因為它是,但它並不大。我認為它在課堂上更形象(我現在有一些非常醜陋的帶有DataAnnotations屬性的 DTO 類——你甚至再也看不到屬性了)。而且這個應用程序中匿名代表的力量幾乎是值得一書的(所以我正在發現)。

基類:

public partial class DomainBase : IDataErrorInfo
{
   private IList<ValidationRule> _rules = new List<ValidationRule>();

   public DomainBase()
   {
       // populate the _rules collection
       this.ConfigureRules();
   }

   protected virtual void ConfigureRules()
   {
       // no rules if not overridden
   }

   protected void AddRule(ValidationRule rule)
   {
       this._rules.Add(rule);
   }





   #region IDataErrorInfo Members

   public string Error
   {
       get { return String.Empty; }    // Validation should call the indexer so return "" here
   }                                   // ..we dont need to support this property.

   public string this[string columnName]
   {
       get
       {
           // get all the rules that apply to the property being validated
           var rulesThatApply = this._rules
               .Where(r => r.Properties.Contains(columnName));

           // get a list of error messages from the rules
           StringBuilder errorMessages = new StringBuilder();
           foreach (ValidationRule rule in rulesThatApply)
               if (!rule.validator.Invoke())   // if validator returns false then the rule is broken
                   if (errorMessages.ToString() == String.Empty)
                       errorMessages.Append(rule.Description);
                   else
                       errorMessages.AppendFormat("\r\n{0}", rule.Description);

           return errorMessages.ToString();
       }
   }

   #endregion
}

ValidationRule和我的驗證功能:

public class ValidationRule
{
   public string[] Properties { get; set; }
   public string Description { get; set; }
   public Func<bool> validator { get; set; }
}


/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
   #region IsRequired

   public static bool IsRequired(this String str)
   {
       return !str.IsNullOrTrimEmpty();
   }

   public static bool IsRequired(this int num)
   {
       return num != 0;
   }

   public static bool IsRequired(this long num)
   {
       return num != 0;
   }

   public static bool IsRequired(this double num)
   {
       return num != 0;
   }

   public static bool IsRequired(this Decimal num)
   {
       return num != 0;
   }

   public static bool IsRequired(this DateTime date)
   {
       return date != DateTime.MinValue;
   }

   #endregion


   #region String Lengths

   public static bool IsLengthLessThanOrEqual(this String str, int length)
   {
       return str.Length <= length;
   }

   public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
   {
       return !str.IsNullOrTrimEmpty() && (str.Length <= length);
   }

   public static bool IsRequired300LenNoSpecial(this String str)
   {
       return !str.IsNullOrTrimEmpty() &&
           str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
               RegexOptions.Multiline) == str;
   }

   #endregion

}

如果我的程式碼看起來很亂,那是因為我最近幾天才研究這種驗證方法。我需要這個想法來滿足一些要求:

  • 我需要支持IDataErrorInfo介面,以便我的 MVC 層自動驗證
  • 我需要能夠支持複雜的驗證場景(我猜你問題的重點):我希望能夠針對同一個對像上的多個屬性進行驗證(即 StartDate 和 FinishDate);來自不同/多個/關聯對象的屬性,例如我在對像圖中的屬性;甚至其他我還沒有想到的事情。
  • 我需要支持將錯誤應用於多個屬性的想法
  • 作為我的 TDD 和 DDD 旅程的一部分,我希望我的域對像比我的服務層方法描述更多我的“域”,因此將這些複雜條件放在模型對象(而不是 DTO)中似乎可以實現這一點

我認為這種方法會讓我得到我想要的,也許你也一樣。

我想如果你和我一起加入這件事,我們會很“靠自己”,但這可能是值得的。我正在閱讀MVC 2 中的新驗證功能,但如果沒有自定義修改,它仍然不符合上述願望清單。

希望這可以幫助。

S#arp 架構有一個

$$ DomainSignature $$與類級別驗證器一起使用的方法標識符$$ HasUniqueDomainSignature $$會做的工作。請參閱下面的範常式式碼:

[HasUniqueDomainSignature]
public class User : Entity
{
   public User()
   {
   }

   public User(string login, string email) : this()
   {
       Login = login;
       Email = email;
   }

   [DomainSignature]
   [NotNullNotEmpty]
   public virtual string Login { get; set; }

   [DomainSignature]
   public virtual string Email { get; set; }

}

仔細看看http://www.sharparchitecture.net/

引用自:https://stackoverflow.com/questions/1721327