2009-02-22 24 views
12

Chúng tôi thực hiện phần lớn các quy tắc kinh doanh của chúng tôi trong cơ sở dữ liệu, sử dụng procs được lưu trữ.Làm cách nào để xử lý các vi phạm ràng buộc db trong giao diện người dùng?

Tôi không bao giờ có thể quyết định cách tốt nhất để truyền dữ liệu ràng buộc các lỗi vi phạm từ cơ sở dữ liệu trở lại giao diện người dùng. Những hạn chế mà tôi đang nói đến được gắn liền với quy tắc kinh doanh hơn là tính toàn vẹn dữ liệu.

Ví dụ: lỗi db như "Không thể chèn hàng khóa trùng lặp" giống với quy tắc kinh doanh "bạn không thể có nhiều hơn một Foo có cùng tên". Nhưng chúng tôi đã "triển khai" nó ở vị trí có ý nghĩa phổ biến nhất: là một ràng buộc duy nhất cho phép ngoại lệ khi quy tắc bị vi phạm.

Các quy tắc khác như "Bạn chỉ được phép 100 Foos mỗi ngày" không gây ra lỗi cho mỗi người nói, vì chúng được xử lý một cách duyên dáng bằng mã tùy chỉnh như return empty dataset mà mã ứng dụng kiểm tra và chuyển về lớp ui.

Và trong đó có chà. đang ui của chúng tôi trông như thế này (đây là AJAX.NET webservices mã, nhưng bất kỳ khuôn khổ ajax sẽ làm):

WebService.AddFoo("foo", onComplete, onError); // ajax call to web service 

function onComplete(newFooId) { 
    if(!newFooId) { 
     alert('You reached your max number of Foos for the day') 
     return 
    } 
    // update ui as normal here 
} 

function onError(e) { 
    if(e.get_message().indexOf('duplicate key')) { 
     alert('A Foo with that name already exists'); 
     return; 
    } 
    // REAL error handling code here 
} 

(Như một mặt lưu ý: Tôi nhận thấy đây là những gì stackoverflow làm khi bạn gửi ý kiến ​​quá nhanh: máy chủ tạo ra một phản hồi HTTP 500 và ui bắt được nó.)

Vì vậy, bạn thấy, chúng tôi đang xử lý vi phạm quy tắc kinh doanh ở hai nơi ở đây, một trong số đó (tức là lỗi constaint duy nhất) đang được xử lý như trường hợp đặc biệt mã được cho là xử lý lỗi thực sự (không vi phạm quy tắc kinh doanh), vì .NET truyền bá Ngoại lệ tất cả các cách lên đến trình xử lý onError().

Điều này cảm thấy sai. lựa chọn của tôi, tôi nghĩ là:

  1. bắt các 'chủ chốt vi phạm bản sao' ngoại lệ ở cấp máy chủ ứng dụng và chuyển đổi nó để bất cứ điều gì nó là ui hy vọng là "quy tắc kinh doanh vi phạm" cờ,
  2. preempt lỗi (giả sử, với số "select name from Foo where name = @Name") và trả về bất kỳ máy chủ ứng dụng nào được mong đợi là cờ "quy tắc kinh doanh bị vi phạm",
  3. trong cùng một ballpark như 2): tận dụng ràng buộc duy nhất được xây dựng trong lớp db và mù quáng insert into Foo, bắt bất kỳ ngoại lệ nào và chuyển đổi ngoại lệ thành bất kỳ ứng dụng nào r dự kiến ​​là "quy tắc kinh doanh vi phạm" cờ
  4. mù quáng insert into Foo (như 3) và để cho Ngoại lệ đó lan truyền đến ui, có máy chủ ứng dụng tăng vi phạm quy tắc kinh doanh là Exceptions thực (trái ngược với 1). Bằng cách này, tất cả các lỗi được xử lý trong mã onError() (hoặc tương tự) của lớp ui.

Điều tôi thích về 2) và 3) là vi phạm quy tắc kinh doanh được "ném" nơi chúng được triển khai: trong proc được lưu trữ. Những gì tôi không thích về 1) và 3) là tôi nghĩ rằng chúng liên quan đến kiểm tra ngu ngốc như "if error.IndexOf('duplicate key')", giống như những gì đang có trong lớp ui hiện tại.

Sửa: Tôi thích 4), nhưng hầu hết mọi người nói để sử dụng Exception s chỉ trong đặc biệt hoàn cảnh.

Vì vậy, làm cách nào để mọi người xử lý việc vi phạm quy tắc kinh doanh đến thanh lịch?

Trả lời

1

Quy trình được lưu trữ có thể sử dụng câu lệnh RAISERROR để trả về thông tin lỗi cho người gọi. Điều này có thể được sử dụng theo cách cho phép giao diện người dùng quyết định cách lỗi sẽ xuất hiện, đồng thời cho phép quy trình được lưu trữ cung cấp chi tiết về lỗi.

RAISERROR có thể được gọi với msg_id, mức độ nghiêm trọng và trạng thái và với một bộ đối số lỗi. Khi được sử dụng theo cách này, một thông báo với msg_id nhất định phải được nhập vào cơ sở dữ liệu bằng quy trình lưu trữ hệ thống sp_addmessage. Điều này msg_id có thể được truy xuất dưới dạng thuộc tính ErrorNumber trong SqlException sẽ được nêu trong mã .NET gọi thủ tục được lưu trữ. Giao diện người dùng sau đó có thể quyết định loại thông điệp hoặc chỉ báo khác để hiển thị.

Đối số lỗi được thay thế thành thông báo lỗi tương tự như cách câu lệnh printf hoạt động trong C. Tuy nhiên, nếu bạn chỉ muốn chuyển đối số trở lại giao diện người dùng để giao diện người dùng có thể quyết định cách sử dụng chúng , chỉ cần làm cho các thông báo lỗi không có văn bản, chỉ cần giữ chỗ cho các đối số. Một thông báo có thể là '"% s" |% d' để trả về một đối số chuỗi (trong dấu ngoặc kép) và một đối số dạng số. Mã .NET có thể tách chúng ra và sử dụng chúng trong giao diện người dùng theo ý bạn.

RAISERROR cũng có thể được sử dụng trong khối TRY CATCH trong quy trình được lưu trữ. Điều đó sẽ cho phép bạn nắm bắt lỗi khóa trùng lặp và thay thế bằng số lỗi của riêng bạn có nghĩa là "khóa trùng lặp khi chèn" vào mã của bạn và có thể bao gồm (các) giá trị khóa thực tế. Giao diện người dùng của bạn có thể sử dụng điều này để hiển thị "Số đơn đặt hàng đã tồn tại", trong đó "x" là giá trị khóa được cung cấp.

+0

Tôi muốn người đã bỏ phiếu này và http://stackoverflow.com/questions/581994/-net-coding-standards-and-framework-for-a-web-service/582429#582429 sẽ đưa ra lý do. Hai câu trả lời rất khác nhau, cùng một kết quả, trong vòng một phút. –

+1

Mã mà cuối cùng gọi là "RAISERROR' sẽ vẫn cần thực hiện" kiểm tra ngu ngốc "như' CHARINDEX ('khóa trùng lặp', @errorMessage)> 0' và tôi tìm thấy câu hỏi này vì kịch bản ví dụ của tôi liên quan đến * hai * khóa duy nhất tiềm năng hạn chế vi phạm và tôi đã tò mò liệu có một số cách để xác định khóa nào đã bị vi phạm mà không tìm kiếm tên của các khóa hoặc tên của các cột có liên quan. –

1

Tôi đã thấy rất nhiều ứng dụng dựa trên Ajax thực hiện kiểm tra thời gian thực trên các trường như tên người dùng (để xem nếu nó đã tồn tại) ngay khi người dùng rời khỏi hộp chỉnh sửa. Dường như với tôi một cách tiếp cận tốt hơn là rời khỏi cơ sở dữ liệu để đưa ra một ngoại lệ dựa trên ràng buộc db - nó chủ động hơn vì bạn có một quy trình thực: lấy giá trị, kiểm tra xem nó có hợp lệ hay không, hiển thị lỗi nếu không, cho phép tiếp tục nếu không có lỗi. Vì vậy, có vẻ như tùy chọn 2 là một lựa chọn tốt.

+0

Điều đó thật tuyệt vời cho UX, nhưng điều kiện cuộc đua lớn ở đây là ui vẫn phải xử lý việc gửi dữ liệu. –

+0

Chúng tôi nhận được xung quanh điều kiện chủng tộc này bằng cách thực hiện xác thực đầy đủ phía máy chủ khi gửi. Xác thực mỗi trường chỉ là sự tiện lợi cho người dùng –

+0

@Mark: chắc chắn.Tôi chỉ tự hỏi làm thế nào mọi người xử lý "đầy đủ phía máy chủ xác nhận" trong ui (giả sử ajax để gửi dữ liệu) khi một vi phạm * không * xảy ra - mà bạn trả lời trong câu trả lời khác của bạn, cảm ơn bạn. –

3

Vấn đề thực sự là một hạn chế trong kiến ​​trúc hệ thống của bạn. Bằng cách đẩy tất cả logic vào cơ sở dữ liệu, bạn cần xử lý nó ở hai nơi (trái ngược với việc xây dựng một lớp logic nghiệp vụ liên kết giao diện người dùng với cơ sở dữ liệu. Sau đó, một lần nữa, bạn có một lớp logic nghiệp vụ mà bạn mất tất cả lợi ích của việc có logic trong procs được lưu trữ. không ủng hộ cái này hay cái khác. cả hai hút về nhau. hoặc không hút. Tùy thuộc vào cách bạn nhìn vào nó.

đâu là tôi? Đúng vậy.

Tôi nghĩ rằng sự kết hợp giữa 2 và 3 có lẽ là cách để đi.

Bằng cách làm trống trước lỗi, bạn có thể tạo một tập hợp các thủ tục có thể được gọi từ mã giao diện người dùng. phản hồi cụ thể ation cho người dùng. Bạn không nhất thiết phải làm điều này với ajax trên cơ sở từng lĩnh vực, nhưng bạn có thể.

Ràng buộc duy nhất và các quy tắc khác trong cơ sở dữ liệu sau đó trở thành kiểm tra độ chính xác cuối cùng cho tất cả dữ liệu và có thể giả định rằng dữ liệu là tốt trước khi được gửi và ném ngoại lệ như một vấn đề tất nhiên (tiền đề là các thủ tục này phải luôn được gọi với dữ liệu hợp lệ và dữ liệu không hợp lệ là trường hợp ngoại lệ).

+0

Đó thực sự là một sự kết hợp của 2 và 4, với các tinh chỉnh đến 4 mà thay vì nâng cao lỗi tại máy chủ ứng dụng chúng được nâng lên trong proc được lưu trữ. Vì vậy, các proc lưu trữ ném ngoại lệ cho tất cả các vi phạm quy tắc kinh doanh (một số nó bắt gặp chính nó, một số db bị bắt cho nó). Tôi thích. –

+0

Bạn đang phải ... –

2

Để bảo vệ # 4, SQL Server có thứ bậc khá trật tự về mức độ nghiêm trọng của lỗi được xác định trước. Vì bạn chỉ ra rằng nó cũng xử lý các lỗi trong đó logic là, tôi muốn được xử lý điều này theo quy ước giữa SP và trừu tượng giao diện người dùng, thay vì thêm một loạt các khớp nối thêm. Đặc biệt là vì bạn có thể tăng lỗi với cả giá trị và chuỗi.

5

Chúng tôi không thực hiện logic nghiệp vụ trong cơ sở dữ liệu nhưng chúng tôi có tất cả các máy chủ xác thực của chúng tôi, với các hoạt động DB CRUD cấp thấp được tách biệt với logic nghiệp vụ cấp cao hơn và mã điều khiển.

Điều chúng tôi cố gắng thực hiện trong nội bộ là truyền xung quanh đối tượng xác thực với các chức năng như Validation.addError(message,[fieldname]). Các lớp ứng dụng khác nhau gắn kết quả xác nhận của họ về đối tượng này và sau đó chúng ta gọi là Validation.toJson() để tạo ra một kết quả trông như thế này:

{ 
    success:false, 
    general_message:"You have reached your max number of Foos for the day", 
    errors:{ 
     last_name:"This field is required", 
     mrn:"Either SSN or MRN must be entered", 
     zipcode:"996852 is not in Bernalillo county. Only Bernalillo residents are eligible" 
    } 
} 

này phía khách hàng có thể dễ dàng được xử lý để hiển thị các thông điệp liên quan đến các lĩnh vực cá nhân cũng như chung tin nhắn.

Về vi phạm ràng buộc, chúng tôi sử dụng # 2, nghĩa là chúng tôi kiểm tra các vi phạm tiềm ẩn trước khi chèn/cập nhật và thêm lỗi vào đối tượng xác thực.

+0

Tôi nhận thấy đây là những gì SO làm cho các hoạt động viết như bỏ phiếu. Câu trả lời có các trường "Thành công" và "Tin nhắn". Nhưng nếu bạn gửi bình luận quá nhanh, họ sẽ ném một ngoại lệ máy chủ 500 và ui sẽ bắt được nó (ví dụ như ví dụ mã của tôi, cái mà tôi không thích). –

+0

Tôi thích cách tiếp cận này mặc dù, và nó sẽ không thể làm ngay cả với kiểm tra quy tắc kinh doanh tất cả các xử lý trong db. Máy chủ ứng dụng chỉ phải dịch tất cả các câu trả lời thành tiêu chuẩn mà ui có thể xử lý. Mà không phải là một điều xấu để làm bất kể. –

1

Đây là cách tôi làm điều gì đó, mặc dù nó có thể không tốt nhất cho bạn:

tôi thường đi cho mô hình chặn trước, mặc dù nó phụ thuộc rất nhiều vào kiến ​​trúc ứng dụng của bạn.

Đối với tôi (trong môi trường của tôi), bạn nên kiểm tra hầu hết các lỗi ở tầng giữa (đối tượng kinh doanh). Đây là nơi mà tất cả các logic kinh doanh cụ thể khác diễn ra, vì vậy tôi cố gắng giữ phần còn lại của logic của tôi ở đây quá. Tôi nghĩ về cơ sở dữ liệu như một nơi nào đó để tồn tại các đối tượng của tôi.

Khi nói đến xác thực, các lỗi dễ nhất có thể bị mắc kẹt trong javascript (định dạng, độ dài trường, v.v.), mặc dù tất nhiên bạn không bao giờ giả định rằng các kiểm tra lỗi đó đã xảy ra. Những lỗi đó cũng được kiểm tra trong thế giới an toàn hơn, được kiểm soát nhiều hơn về mã phía máy chủ.

Quy tắc kinh doanh (chẳng hạn như "bạn chỉ có thể có quá nhiều foos mỗi ngày") được kiểm tra trong mã phía máy chủ, trong lớp đối tượng nghiệp vụ.

Chỉ các quy tắc dữ liệu mới được kiểm tra trong cơ sở dữ liệu (tính toàn vẹn tham chiếu, ràng buộc trường duy nhất, v.v.). Chúng tôi trước khi empt kiểm tra tất cả những điều này trong tầng giữa quá, để tránh nhấn cơ sở dữ liệu không cần thiết.

Do đó, cơ sở dữ liệu của tôi chỉ tự bảo vệ khỏi các quy tắc đơn giản, tập trung vào dữ liệu mà nó được trang bị tốt để xử lý; các quy tắc hướng kinh doanh, biến đổi hơn sống trong vùng đất của các đối tượng, chứ không phải là vùng đất của các hồ sơ.

+0

Rất đẹp. Tò mò mặc dù: bạn làm gì về điều kiện chủng tộc nơi một kiểm tra ở tầng giữa đi qua, nhưng không thành công ở tầng dữ liệu (do nói, một chủ đề khác đánh nó vào cú đấm)? –

+0

Vâng, đó hoàn toàn là điểm yếu trong cách tiếp cận này. Trong môi trường đặc biệt của tôi, đây là một trường hợp hiếm hoi mà chúng ta không cần phải lo lắng về nó. Nó sẽ dẫn đến một ngoại lệ khá xấu xí, đúng, nhưng cơ sở dữ liệu sẽ vẫn còn nguyên vẹn. – teedyay

Các vấn đề liên quan