Domanda

Ho un grosso problema e non riesco a trovare nessun altro su Internet che abbia il mio problema. Spero davvero che StackOverflow mi possa aiutare ...

Sto scrivendo un'applicazione ASP.NET MVC e sto usando il concetto di repository con Linq To Sql come archivio dati. Tutto funziona alla grande per quanto riguarda la selezione di righe dalle viste. E intrappolando vincoli di regole aziendali molto basilari. Tuttavia, mi trovo di fronte a un problema nelle mappature delle procedure memorizzate per eliminazioni, inserimenti e aggiornamenti. Lasciami spiegare:

Il nostro DBA ha lavorato molto per mettere la logica di business in tutte le nostre procedure memorizzate in modo da non dovermi preoccupare da parte mia. Certo, eseguo la convalida di base, ma gestisce l'integrità dei dati e vincoli di date contrastanti, ecc ... Il problema che sto affrontando è che tutte le procedure memorizzate (e intendo tutte) hanno 5 parametri aggiuntivi (6 per inserti ) che mi forniscono informazioni. L'idea è che quando qualcosa si rompe, posso chiedere all'utente le informazioni appropriate dal nostro database.

Ad esempio:

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)

Dalla procedura memorizzata sopra, i primi 3 parametri sono gli unici parametri utilizzati per " Crea " il record di categoria. I restanti parametri vengono semplicemente utilizzati per dirmi cosa è successo all'interno del metodo. Se una regola aziendale viene violata all'interno della procedura memorizzata, NON utilizza la parola chiave "RAISEERROR" SQL quando le regole aziendali vengono violate. Invece, mi fornisce informazioni sull'errore usando i parametri OUTPUT. Lo fa per ogni singola procedura memorizzata nel nostro database, anche per gli aggiornamenti e le eliminazioni. Tutte le chiamate "Ricevi" vengono eseguite utilizzando viste personalizzate. Sono stati tutti testati e l'idea era di semplificare il mio lavoro poiché non devo aggiungere la logica aziendale per intercettare tutti i vari scenari per garantire la qualità dei dati.

Come ho detto, sto usando Linq To Sql e ora sto affrontando un problema. Il problema è che la mia " Categoria " l'oggetto modello ha semplicemente 4 proprietà su di esso: IDCategoria, NomeCategoria, ID utente e IsActive. Quando ho aperto il designer per iniziare a mappare le mie proprietà per l'inserto, mi sono reso conto che non esiste davvero un modo (facile) per tenere conto dei parametri aggiuntivi se non li aggiungo al mio oggetto Model.

Teoricamente quello che mi piacerebbe fare è questo:

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

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

E poi dalla mia categoria CategoryController farei semplicemente quanto segue:

[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);
    }
}

Qual è il modo migliore per gestirlo usando Linq to Sql? Non (personalmente) non ha senso aggiungere tutte queste proprietà aggiuntive a ciascun oggetto modello ... Ad esempio, "Get" non dovrebbe MAI contenere errori e non voglio che i miei metodi di repository ne restituiscano uno tipo di oggetto per Ricevi chiamate, ma accetta un altro tipo di oggetto per chiamate CUD.

Aggiornamento: la mia soluzione! (1 dicembre 2009)

Ecco cosa ho fatto per risolvere il mio problema. Mi sono sbarazzato del mio metodo "Save ()" su tutti i miei repository. Invece, ho aggiunto un metodo "Update ()" a ciascun repository e in realtà eseguivo il commit dei dati nel database su ogni chiamata CUD (es. Crea / Aggiorna / Elimina).

Sapevo che ogni stored procedure aveva gli stessi parametri, quindi ho creato una classe per conservarli:

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; }
    }

}

Ho anche creato un MySprocException che accetta MySprocArgs nel suo costruttore:

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; }
    }
}

Ora qui è dove tutto si unisce ... Usando l'esempio con cui ho iniziato nella mia richiesta iniziale, ecco come potrebbe apparire il metodo '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);
} 

Ora dal mio controller, faccio semplicemente quanto segue:

[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");

    } 
}

Il trucco per gestire il nuovo MySprocException è semplicemente di intercettarlo utilizzando l'attributo HandleError e reindirizzare l'utente a una pagina che comprende MySprocException.

Spero che questo aiuti qualcuno. :)

È stato utile?

Soluzione

Non credo che tu possa aggiungere i parametri di output a nessuna delle tue classi LINQ perché i parametri non persistono in nessuna tabella del tuo database.

Ma puoi gestire i parametri di output in LINQ nel modo seguente.

Aggiungi le procedure memorizzate che desideri chiamare sul tuo .dbml usando il designer.

Chiama la procedura memorizzata nel tuo codice

 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);
 }

Altri suggerimenti

Non l'ho ancora provato, ma puoi leggere questo articolo, in cui parla di stored procedure che restituiscono parametri di output.

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

Fondamentalmente trascina la procedura memorizzata nel tuo designer LINQ to SQL, quindi dovrebbe fare il lavoro per te.

Il dbContext.SubmitChanges (); funzionerà solo per ENTITY FRAMEWORK. Suggerisco di salvare, aggiornare ed eliminare funzionerà utilizzando una singola procedura memorizzata o 3 diverse procedure.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top