Pertanyaan Validasi Logika Bisnis di Asp.net MVC Menggunakan Pengecualian


Saya memiliki pertanyaan mengenai metode yang saya gunakan untuk melakukan Validasi Aturan Bisnis di asp.net MVC.

Saat ini saya memiliki kelas pengecualian yang terlihat seperti ini

public class ValidationException : Exception 
{
    private ModelStateDictionary State { get; set; }
    public ValidationException(ModelStateDictionary state)
    {
        State = state;
    }
    public void MergeModelStates(ModelStateDictionary state)
    {
        state.Merge(this.State);
    }
}

dan validator yang terlihat seperti ini

public void Validate(IEntity entity)
{
    ModelStateDictionary state = new ModelStateDictionary();
    if (entity.Contact != null && _service.GetBy(entity.Contact.Id) == null)
        state.AddModelError("Contact", "Invalid Contact.");
    if (entity.Title.Length > 8)
        state.AddModelError("title", "Title is too long...");
    ... etc
    if (!state.IsValid)
        throw new ValidationException(state);
}

dan Pengontrol yang melakukan hal seperti ini

public ActionResult Add()
{
    var entity = new InputModel;
    try
    {
        TryUpdateMode(inputModel);
        ..... Send input to a Repository (Repository calls Validate(entity);
    }
    catch (ValidationException validationException)
    {
       validationException.MergeModelStates(this.ModelState);
       TryUpdateModel(inputModel);
       return View("Add",inputModel);
    }
    return View("List");
}

Apakah salah jika menggunakan pengecualian untuk melakukan sesuatu seperti ini? Apakah ada contoh metode yang lebih baik? Saya tidak ingin menambahkan validasi ke Entitas Model itu sendiri. Satu-satunya cara lain yang telah saya lihat adalah menyuntikkan Controllers ModelState ke lapisan Repositori, tetapi itu tampak ceroboh bagi saya.

Terima kasih atas bantuannya


5
2017-10-28 14:13


asal


Jawaban:


Pengecualian umumnya harus untuk kasus luar biasa, dan tidak menangani sesuatu yang dapat secara rutin terjadi selama pelaksanaan normal program Anda. Ada banyak alasan bagus untuk ini - inilah beberapa yang saya alami cukup teratur:

  1. Masalah kinerja - Pengecualian umumnya merupakan operasi yang cukup mahal - jika mereka dilempar secara teratur, kinerja Anda mungkin akan terganggu.
  2. Berurusan dengan pengecualian validasi yang tidak tertangkap - Jika Anda kebetulan menggunakan kode Anda tanpa berurusan dengan pengecualian, Anda akan memunculkan kesalahan validasi Anda sejauh "layar kuning" atau pengendali tabrakan - mungkin bukan pengalaman pengguna terbaik.
  3. Pengecualian tidak terstruktur dengan cara yang memungkinkan untuk informasi pengguna yang baik. Lihatlah kelas pengecualian - tidak banyak yang disiapkan dengan cara yang membuat informasi yang baik untuk pengguna, sesuatu yang Anda perlukan untuk menyampaikan kembali informasi kepada pengguna. Setiap kali saya mencoba untuk menggunakan pengecualian dengan cara ini saya telah berakhir dengan seluruh host subclass dengan properti yang ditempelkan pada yang tidak benar-benar membuat banyak rasa milik pengecualian.

Satu pendekatan yang biasanya saya suka lakukan adalah menyediakan metode Validate publik yang mengembalikan daftar kesalahan (tetapi tidak pernah melempar pengecualian itu sendiri), dan kemudian metode Simpan yang memanggil Validasi () dan melempar pengecualian jika ada kesalahan. Anda mengubah perilaku dari "membuang jika model tidak valid" untuk "membuang jika kode mencoba untuk menyimpan sementara model dalam keadaan tidak valid".

Untuk mengatasi komentar di bawah ini mengenai kinerja lemparan di Validasi vs Simpan - lempar di Simpan () akan memiliki penalti kinerja yang sama persis seperti lemparan di Validasi (). Namun, perbedaan utamanya adalah hal ini tidak boleh terjadi - Anda melindungi pengembang dengan menggunakan kelas Anda secara tidak benar daripada menggunakan pengecualian sebagai metode validasi. Ditulis dengan benar, kode yang memanggil metode simpan akan terlihat seperti ini:

ValidationResult result = obj.Validate();
if (result.IsValid) {
   obj.Save();
} else {
   // display errors to the user
}

Pengecualian hanya akan terlempar jika pengembang lupa memeriksa status validasi sebelum menyimpan. Ini memiliki manfaat dari keduanya memungkinkan validasi terjadi tanpa menggunakan pengecualian, serta melindungi database Anda dengan tidak pernah mengizinkan entitas yang tidak valid untuk disimpan. Idealnya Anda tidak akan menangkap pengecualian di controller sama sekali dan membiarkan rutin penanganan kesalahan umum mengatasinya, karena masalahnya tidak lagi dengan input pengguna tetapi kesalahan dari pengembang.


13
2017-10-28 14:23



Idealnya Anda akan menggunakan fitur anotasi data di ASP.NET MVC 2: -

http://stephenwalther.com/blog/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx

http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx

Sebagai contoh:-

public class Person
{
  [Required(ErrorMessage="Please enter a name.")]
  public String Name { get; set; }
}

Jika Anda tidak siap untuk meningkatkan, masih ada solusi:

Jika Anda melewatkan referensi ke kamus Controller's ModelState (membungkusnya di dalam antarmuka jika Anda khawatir tentang pemisahan kekhawatiran) ke Validator Anda, panggil AddModelError jika Anda menemukan kesalahan, kemudian di Pengontrol Anda, Anda dapat menghubungi jika (ModelState.IsValid ) dan mengambil tindakan yang sesuai: -

var entity = new InputModel();

TryUpdateModel(entity);
MyValidator.Validate(entity, ModelState);

if(ModelState.IsValid) { ...

dan Validator Anda terlihat seperti: -

public void Validate(IEntity entity, ModelStateDictionary state)
{
  if (entity.Contact != null && _service.ValidId(entity.Contact.Id) == null)
    state.AddModelError("Contact", "Invalid Contact.");
  // etc
}

2
2017-10-28 14:24