2009-10-27 11 views
6

Estoy construyendo una biblioteca básica ABM, que preveo uso tanto local (añadir referencia) y WCF (Agregar referencia de servicio) entornos.Biblioteca de diseño CRUD clase, para volver mensajes útiles acerca de la insuficiencia lógica de negocio, que no es excepcional

¿Cuáles son los mejores tipos de retorno para los Crea, uupdate, y eliminar las partes (que tienen reglas de negocio más complejos) de una instalación de ABM?

Quiero poder minimizar el ida y vuelta en el cable, pero también quiero proporcionar a mis clientes información significativa sobre cuándo una operación falla en mi lógica comercial, pero es técnicamente válida (por lo tanto, no es una excepción)

Tomemos por ejemplo la ABM es para una clase de persona, que tiene los siguientes campos: Nombre, MiddleName Apellido y Fecha de Brith. Primero, Último y Fecha de nacimiento son obligatorios, pero Medio no.

¿Cómo debería transmitir las fallas de la lógica de negocio de vuelta al cliente? ES DECIR. "Debe especificar un valor para FirstName".

  1. ¿Es aquí donde debería arrojar excepciones ? (No se siente como un caso excepcional, así que creo que no, pero podría estar equivocado).
  2. ¿Debo usar el vacío y un parámetro de "salida"? Si es así, ¿qué tipo debería ser?
  3. ¿Debo usar un tipo de devolución de objeto y poner datos allí sobre lo que sucede?
  4. Alguna otra opción que me he perdido por completo?

Respuesta

1

1.¿Esto es donde debería arrojar excepciones? (No parece un caso excepcional, así que no, pero podría estar equivocado).

Personalmente, siento que debe devolver un objeto con un resultado, así como cualquier error de validación, y no una excepción para la validación de datos, ya sea debido a la falta de información (validación de formato) o validación lógica de negocio. Sin embargo, sugiero lanzar una excepción para errores que no están relacionados con los datos en sí, es decir: si la confirmación de la base de datos falla con datos válidos, etc.

Mi opinión aquí es que la validación no es un "acontecimiento excepcional" . Personalmente, creo que cualquier cosa que un usuario pueda cometer errores al simplemente no ingresar suficientes datos/correctos/etc. es algo que no es excepcional, es una práctica estándar y debe ser manejada directamente por la API.

Las cosas que no están relacionadas con lo que el usuario está haciendo (es decir, problemas de red, problemas del servidor, etc.) son acontecimientos excepcionales y justifican una excepción.

2. ¿Debo utilizar el vacío y un parámetro de "salida"? Si es así, ¿qué tipo debería ser?

3. ¿Debo usar un tipo de devolución de objeto y poner datos allí sobre lo que sucede?

Personalmente prefiero la tercera opción. los parámetros de "salida" no son muy significativos. Además, querrá devolver más de una información de estado única de esta llamada; querrá devolver suficiente información para marcar las propiedades adecuadas como no válidas, así como también toda la información de toda la operación completa.

Esto probablemente requiera una clase que contenga, como mínimo, un estado de confirmación (éxito/error de formato/lógica de negocio fallida/etc.), una lista de asignaciones de propiedades-> errores (es decir: IDataErrorInfo información de estilo), y potencialmente una lista de errores que no están vinculados a una propiedad específica, sino que tratan con la lógica comercial de la operación como un todo, o la combinación de valores de propiedad sugeridos.

4.Alguna otra opción que me he perdido por completo?

La otra opción, que me gusta bastante, es tener la validación en un ensamblaje separado de la capa de procesamiento empresarial. Esto le permite reutilizar la lógica de validación en el lado del cliente.

Lo bueno de esto es que puede simplificar y reducir el tráfico de la red de manera espectacular. El cliente puede pre validar la información y solo enviar datos a través del cable si es válida.

El servidor puede recibir los datos correctos, y volver a validarlos, y devolver nada más que un único resultado de confirmación. Creo que esto debería tener al menos tres respuestas: éxito, error debido a la lógica comercial o error debido al formateo. Esto proporciona seguridad (no es necesario que confíe en el cliente) y le brinda al cliente información sobre lo que no se maneja correctamente, pero evita pasar tanto información incorrecta del cliente-> servidor, como información de validación del servidor-> cliente, por lo que puede reducir drásticamente el tráfico.

La capa de validación puede enviar (con seguridad) la información a la capa CRUD para enviarla.

+0

Gracias por tomarse el tiempo para escribir una respuesta tan detallada. ¿Cuál es, en su opinión, la mejor opción para la mayoría de los casos? Parece que el # 3 es muy popular entre las otras respuestas, ¿estarías de acuerdo en que es la mejor solución para "atrapar a todos"? ¿Especulación de barrado o casos de bordes impares? – Nate

+1

Si le preocupa el tráfico, mi opción 4 es una variación de 3 que proporciona una alternativa "mejor", IMO. También hace que la validación del lado del cliente sea muy sencilla. De lo contrario, la opción 3 es mi favorita. –

1

Puede que esta publicación del blog de Rob Bagby sea interesante; él describe cómo implementar un repositorio para manejar las operaciones CRUD, y, a su punto, específicamente cómo implementar la validación, devolviendo una colección de "RuleViolation" al cliente en caso de que haya un problema.

http://www.robbagby.com/silverlight/patterns-based-silverlight-development-part-ii-repository-and-validation/

[editar] Para mí, es un caso para lanzar una excepción: si un usuario hace necesario crear un nombre y no se proporciona el nombre, la persona que llama no ha utilizado los argumentos adecuados, y no está usando el método de la manera prevista. InvalidArgumentException suena adecuado.

+1

Entonces, ¿su método se vería así? 'CreateUser (String firts, String middle, String Last, DateTime dob)' porque me siento más inclinado a usar 'CreateUser (User user)' ¿es una mala idea de diseño? En la versión que no es WCF, también puedo agregar sobrecargas, pero usaría la firma que acepta un objeto en WCF. – Nate

+0

Cuando lanza excepciones, ¿cómo sabe a qué área apunta al usuario en la interfaz de usuario? Me gusta cuando olvida ingresar un campo, y presiona enviar, la aplicación no solo le da un mensaje, sino que también enfoca el cursor en ese campo. – Nate

+0

El nombre del método CreateUser implica que creará un objeto User; si ya tiene un objeto Usuario para pasar al método, ¿por qué crearía uno? –

1

Debería consultar la implementación de las Reglas de validación de Rocky Lhotka en su CSLA framework.

NOTA: hice no decir a usar su marco en masa, ya que tiene algunos problemas de acoplamiento que no se rompen algunos esfuerzos de SRP en las últimas tendencias de desarrollo .NET.

Pero, su marco hace uso de la notificación "automática" hasta la capa de IU y la integración con los mensajes de error de validación con soporte para controles Web/Winforms.

1

Editar: Debo señalar, que normalmente tendría la materia de validación abstraída en una capa empresarial, que manejaría la validación y llamaría a los métodos CRUD después de que la validación fuera exitosa.

Una forma de hacerlo sería devolver una clase de "respuesta" que contenga toda la información que necesitaría un consumidor de su biblioteca para evaluar qué sucedió y qué hacer a continuación.Un ejemplo muy básico de una clase que podría utilizar sería algo como esto:

public class Response<T> where T:BusinessObject 
{ 
    public Response(T oldOriginalValue, T newValue) 
    { 
    } 

    /// <summary> 
    /// List of Validation Messages 
    /// </summary> 
    public List<ValidationMessage> ValidationMessages { get; set; } 

    /// <summary> 
    /// Object passed into the CRUD method 
    /// </summary> 
    public T OldValue { get; private set; } 

    /// <summary> 
    /// Object passed back from the CRUD method, with values of identity fields, etc populated 
    /// </summary> 
    public T NewValue { get; private set; } 

    /// <summary> 
    /// Was the operation successful 
    /// </summary> 
    public bool Success { get; set; } 
} 

public class ValidationMessage 
{ 
    /// <summary> 
    /// Property causing validation message (i.e. FirstName) 
    /// </summary> 
    string Property { get; set; } 

    /// <summary> 
    /// Validation Message text 
    /// </summary> 
    string Message { get; set; } 
} 
+0

Supongo que debería mencionar que mi "CRUD" real está usando LinqToSQL ... Estoy exponiendo la lógica de negocios de la misma forma que CRUD. ¿Es tan malo? – Nate

+0

Por ejemplo, lo que estoy llamando CRUD arriba, realmente valida la entrada y luego llama a LinqToSQL para actualizar la base de datos o falla, y la parte de falla es de lo que no estoy seguro cómo hacer, pero me gusta su idea. – Nate

+0

No, eso está bien. Sus métodos CRUD son realmente su capa empresarial y LinqToSql es su capa de datos y lo que está llamando sus métodos CRUD devolverá el objeto de respuesta para indicar el éxito o el fracaso, así como proporcionar una lista de mensajes para describir los motivos de una falla. Cualquier error que provenga de su LinqToSql capturado de su LinqToSql también podría ir a su colección ValidationMessage (que podría llamarse mejor Mensajes para que pueda contener validación y mensajes de error de LinqToSql sin confundir a nadie) –

1

El debate entre el retorno de una estructura de resultados con detalles de la falla frente a lanzar una excepción se puede generalizar para "¿Puedo con éxito realizar la solicitaron ¿acción?" Está diseñando una biblioteca y la forma en que la biblioteca comunica los modos de falla es parte de eso.

Si se le pide a la biblioteca que cree un objeto User pero no puede porque el nombre de usuario falló la validación, puede pasar un Usuario vacío junto con los mensajes de falla de validación y esperar que el código del cliente pruebe el valor devuelto. Mi experiencia (DCOM pre-flashbacks ATL) con tener que probar los valores de retorno para los códigos de error es que es fácil obtener complaciente (o vago) y omitirlo.

Según lo que ha descrito, no se descarta el uso de excepciones. Defina una excepción principal para todos los tipos de excepción que su biblioteca puede lanzar. De esta forma, los clientes no tienen que incluir una gran lista de excepciones secundarias, a menos que realmente lo deseen. Un ejemplo de esto es SqlException.

3

Por motivos de rendimiento, la validación de entrada inicial debe realizarse en su nivel de cliente. (es decir: no se moleste en enviar datos incorrectos por el cable). En el nivel del cliente, una falla en la validación de la entrada sería, de hecho, un problema muy esperado que no arrojaría excepciones. Sin embargo, si coloca esta validación "temprana" en el nivel del cliente, una falla de validación de datos encontrada en cualquier nivel más profundo podría considerarse un problema inesperado, por lo que arrojar excepciones para errores de validación de datos en esos niveles no sería inapropiado.

+3

Por seguridad, la validación de datos DEBE hacerse en el servidor, ya que el cliente podría verse comprometido. ¿Me equivoco al tratar siempre al cliente como hostil? – Nate

+3

Tiene toda la razón al tratar al cliente como hostil. La validación del lado del cliente no reemplaza la validación del lado del servicio, sino que la complementa. Todo lo que sugiero es que si aplica la validación tanto en el cliente como en la interfaz de servicio, la mayoría de los datos no válidos nunca deberían llegar al servicio, por lo que sería aceptable que el servicio trate los problemas de validación de datos como excepcionales. –

+0

Me he duplicado a esto después de un poco de reflexión, y de alguna manera no puedo aceptar una respuesta, posiblemente porque esta es una vieja pregunta. He llegado a la misma conclusión. – Nate

Cuestiones relacionadas