Pregunta

Tengo un problema muy grande y parece que no puedo encontrar a nadie más en Internet que tenga mi problema. Espero que StackOverflow pueda ayudarme ...

Estoy escribiendo una aplicación ASP.NET MVC y estoy usando el concepto Repository con Linq To Sql como mi almacén de datos. Todo funciona muy bien en lo que respecta a seleccionar filas de las vistas. Y atrapar restricciones de reglas comerciales muy básicas. Sin embargo, me encuentro con un problema en mis asignaciones de procedimientos almacenados para eliminaciones, inserciones y actualizaciones. Déjame explicarte:

Nuestro DBA ha trabajado mucho para poner la lógica de negocios en todos nuestros procedimientos almacenados para que no tenga que preocuparme de mi parte. Claro, hago una validación básica, pero él maneja la integridad de los datos y las restricciones de fechas en conflicto, etc. El problema que enfrento es que todos los procedimientos almacenados (y quiero decir todos) tienen 5 parámetros adicionales (6 para las inserciones). ) que me devuelven información. La idea es que cuando algo se rompe, puedo solicitar al usuario la información adecuada de nuestra base de datos.

Por ejemplo:

sp_AddCategory(
    @userID INT,
    @categoryName NVARCHAR(100),
    @isActive BIT,
    @errNumber INT OUTPUT,
    @errMessage NVARCHAR(1000) OUTPUT,
    @errDetailLogID INT OUTPUT,
    @sqlErrNumber INT OUTPUT,
    @sqlErrMessage NVARCHAR(1000) OUTPUT,
    @newRowID INT OUTPUT)

Del procedimiento almacenado anterior, los primeros 3 parámetros son los únicos parámetros que se utilizan para " Crear " El registro de categoría. Los parámetros restantes simplemente se usan para decirme qué sucedió dentro del método. Si una regla de negocio se rompe dentro del procedimiento almacenado, NO utiliza la palabra clave 'RAISEERROR' de SQL cuando se rompen las reglas de negocio. En su lugar, me proporciona información sobre el error utilizando los parámetros OUTPUT. Lo hace para cada procedimiento almacenado en nuestra base de datos, incluso las actualizaciones y eliminaciones. Todas las llamadas 'Obtener' se realizan mediante vistas personalizadas. Todos han sido probados y la idea era facilitar mi trabajo, ya que no tengo que agregar la lógica de negocios para atrapar todos los diversos escenarios para garantizar la calidad de los datos.

Como dije, estoy usando Linq To Sql, y ahora me enfrento a un problema. El problema es que mi " Categoría " El objeto modelo simplemente tiene 4 propiedades: Id. de categoría, Nombre de categoría, Id. de usuario e IsActive. Cuando abrí el diseñador para comenzar a mapear mis propiedades para el inserto, me di cuenta de que realmente no hay una forma (fácil) de dar cuenta de los parámetros adicionales a menos que los agregue a mi objeto Modelo.

Teóricamente, me gustaría hacer esto:

// note: Repository Methods
public void AddCategory(Category category)
{
    _dbContext.Categories.InsertOnSubmit(category);
}

public void Save()
{
    _dbContext.SubmitChanges();
}

Y luego, desde mi clase CategoryController, simplemente haría lo siguiente:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
    var category = new Category();
    try 
    {
        UpdateModel(category); // simple validation here...

        _repository.AddCategory(category);
        _repository.Save(); // should get error here!!

        return RedirectToAction("Index");
    }
    catch
    {
        // manage friendly messages here somehow... (??)
        // ...

        return View(category);
    }
}

¿Cuál es la mejor manera de administrar esto usando Linq to Sql? Yo (personalmente) no creo que tenga sentido agregar todas estas propiedades adicionales a cada objeto modelo ... Por ejemplo, 'Get' NUNCA debería tener errores y no quiero que mis métodos de repositorio devuelvan uno tipo de objeto para obtener llamadas, pero acepta otro tipo de objeto para llamadas CUD.

Actualización: ¡Mi solución! (1 de diciembre de 2009)

Esto es lo que hice para solucionar mi problema. Me deshice de mi método 'Save ()' en todos mis repositorios. En cambio, agregué un método 'Update ()' a cada repositorio y realmente confirmo los datos a la base de datos en cada llamada CUD (es decir, Crear / Actualizar / Eliminar).

Sabía que cada procedimiento almacenado tenía los mismos parámetros, así que creé una clase para contenerlos:

public class MySprocArgs 
{
    private readonly string _methodName;
    public int? Number;
    public string Message;
    public int? ErrorLogId;
    public int? SqlErrorNumber;
    public string SqlErrorMessage;
    public int? NewRowId;

    public MySprocArgs(string methodName)
    {
        if (string.IsNullOrEmpty(methodName))
            throw new ArgumentNullException("methodName");

        _methodName = methodName;
    }

    public string MethodName
    {
        get { return _methodName; }
    }

}

También creé una MySprocException que acepta MySprocArgs en su constructor:

public class MySprocException : ApplicationException
{

    private readonly MySprocArgs _args;
    public MySprocException(MySprocArgs args) : base(args.Message)
    {
       _args = args;
    }

    public int? ErrorNumber
    {
        get { return _args.Number; }
    }

    public string ErrorMessage
    {
        get { return _args.Message; }
    }

    public int? ErrorLogId
    {
        get { return _args.ErrorLogId; }
    }

    public int? SqlErrorNumber
    {
        get { return _args.SqlErrorNumber; }
    }

    public string SqlErrorMessage
    {
        get { return _args.SqlErrorMessage; }
    }
}

Ahora aquí es donde todo se une ... Usando el ejemplo con el que comencé en mi consulta inicial, así es como se vería el método 'AddCategory ()':

public void AddCategory(Category category)
{
   var args = new MySprocArgs("AddCategory");
   var result = _dbContext.AddWidgetSproc(
                    category.CreatedByUserId,
                    category.Name,
                    category.IsActive,
                    ref args.Number, // <-- Notice use of 'args'
                    ref args.Message,
                    ref args.ErrorLogId,
                    ref args.SqlErrorNumber,
                    ref args.SqlErrorMessage,
                    ref args.NewRowId);

   if (result == -1)
      throw new MySprocException(args);
} 

Ahora desde mi controlador, simplemente hago lo siguiente:

[HandleError(ExceptionType = typeof(MySprocException), View = "SprocError")]
public class MyController : Controller
{
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(Category category)
    {
        if (!ModelState.IsValid)
        { 
            // manage friendly messages        
            return View(category);
        }

        _repository.AddCategory(category);
        return RedirectToAction("Index");

    } 
}

El truco para administrar el nuevo MySprocException es simplemente atraparlo utilizando el atributo HandleError y redirigir al usuario a una página que comprenda MySprocException.

Espero que esto ayude a alguien. :)

¿Fue útil?

Solución

No creo que pueda agregar los parámetros de salida a ninguna de sus clases LINQ porque los parámetros no persisten en ninguna tabla de su base de datos.

Pero puede manejar los parámetros de salida en LINQ de la siguiente manera.

Agregue los procedimientos almacenados que desea llamar a su .dbml utilizando el diseñador.

Llame a su procedimiento almacenado en su código

 using (YourDataContext context = new YourDataContext())
 {
    Nullable<int> errNumber = null;
    String errMessage = null;
    Nullable<int> errDetailLogID = null;
    Nullable<int> sqlErrNumber = null;
    String sqlErrMessage = null;
    Nullable<int> newRowID = null;
    Nullable<int> userID = 23;
    Nullable<bool> isActive=true;

    context.YourAddStoredProcedure(userID, "New Category", isActive, ref errNumber, ref errMessage, ref errDetailLogID, ref sqlErrNumber, ref sqlErrMessage, ref newRowID);
 }

Otros consejos

Todavía no lo he probado, pero puede consultar este artículo, donde habla sobre los procedimientos almacenados que devuelven los parámetros de salida.

http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx

Básicamente arrastre el procedimiento almacenado en su diseñador LINQ to SQL, entonces debería hacer el trabajo por usted.

El dbContext.SubmitChanges (); funcionará solo para ENTITY FRAMEWORK. Sugiero que Guardar, Actualizar y eliminar funcionará utilizando un procedimiento de Almacenamiento único o utilizando 3 procedimientos diferentes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top