2013-09-27 16 views
5

Ich brauche eine Möglichkeit, zwischen SQL-Ausnahmen mit Entity Framework LINQ zu unterscheiden, zum Beispiel wie Unterscheidung zwischen Foreing Key Constraint Verletzung oder Unique Constraint Verletzung, wenn alles, was ich bekomme von der DbUpdateException ist eine Tonne von verschachtelten inneren Ausnahmen und nutzlose lange Fehlermeldungen? Gibt es Ausnahmen auf niedrigerer Ebene, wo ich etwas wie "Catch FKException" machen kann; Fang "uniqueException" oder so ähnlich.Handle Ausnahmen in Entity Framework 4

+0

Oft enthalten die inneren Ausnahmen numerische Fehlercodes, haben Sie versucht, diese nachzuschlagen? Veröffentlichen Sie ein Beispiel für eine Ausnahme in Ihrer Frage, achten Sie darauf, den Text zu markieren, und klicken Sie auf die Schaltfläche {} ', um das Format zu formatieren. –

Antwort

5

Mit SQL-Fehlercodes ...

catch (DbUpdateException ex) 
        { 
         var sqlex = ex.InnerException.InnerException as SqlException; 

         if (sqlex != null) 
         { 
          switch (sqlex.Number) 
          { 
           case 547: throw new ExNoExisteUsuario("No existe usuario destino."); //FK exception 
           case 2627: 
           case 2601: 
            throw new ExYaExisteConexion("Ya existe la conexion."); //primary key exception 

           default: throw sqlex; //otra excepcion que no controlo. 


          } 
         } 

         throw ex; 
        } 
11
  try 
      { 
       //code 
      } 
      catch (System.Data.Entity.Validation.DbEntityValidationException e) 
      { 
       string rs = ""; 
       foreach (var eve in e.EntityValidationErrors) 
       { 
        rs = string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State); 
        Console.WriteLine(rs); 

        foreach (var ve in eve.ValidationErrors) 
        { 
         rs += "<br />" + string.Format("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage); 
        } 
       } 
       throw new Exception(rs); 
      } 
+4

Wenn du wirklich nett sein wolltest, dann sollte die letzte Zeile "neue Exception (rs, e)" werfen, damit die nächste Person in der Kette die innere Exception überprüfen und den StackTrace und was nicht sehen kann. (Auch eine benutzerdefinierte Ausnahme anstelle einer generischen 'Exception' zu werfen würde auch nett sein) –

+0

Aber ich muss tatsächlich zwischen ihnen unterscheiden, ich muss verschiedene Ausnahmen werfen, je nachdem, welche Art von Datenbankfehler es war. – user1777914

1

I paar nützliche Methoden für die dies schrieb:

public static class DbUtils 
{ 
    /// <summary> 
    ///  Takes a code block that updates database, runs it and catches db exceptions. If the caught 
    ///  exception is one of those that are ok to ignore (okToIgnoreChecks) then no 
    ///  exception is raised and result is returned. Otherwise an exception is rethrown. 
    /// 
    ///  This function is intended to be run within an explicit transaction, i.e.: 
    ///  using (var transaction = db.Database.BeginTransaction()), which should be committed/rolledback afterwards. 
    ///  Otherwise, if you don't use a transaction discard the db context or in other words make this operation 
    ///  the only one that you run within implicit transaction. 
    /// 
    ///  This function can wrap a single DB statement, but it's more efficient to wrap multiple statements 
    ///  so that locks are held for shorter period of time. 
    ///  If an exception occurs within a transaction and is caught by this function, all other changes 
    ///  will be still saved to DB on commit if transaction is used. 
    /// </summary> 
    /// <typeparam name="T">Any result returned by the code block</typeparam> 
    /// <param name="context">Database connection</param> 
    /// <param name="dbCodeBlock"> 
    ///  Code block to execute that updates DB. It's expected, but not critical that 
    ///  this code does not throw any other exceptions. Do not call SaveChanges() from the code block itself. Let this 
    ///  function do it for you. 
    /// </param> 
    /// <param name="okToIgnoreChecks"> 
    ///  List of functions that will check if an exception can be ignored. 
    /// </param> 
    /// <returns>Returns number of rows affected in DB and result produced by the code block</returns> 
    public static Tuple<int, T> IgnoreErrors<T>(DbContext context, 
     Func<T> dbCodeBlock, params Func<DbUpdateException, bool>[] okToIgnoreChecks) 
    { 
     var result = dbCodeBlock(); 
     try 
     { 
      var rowsAffected = context.SaveChanges(); 
      return Tuple.Create(rowsAffected, result); 
     } 
     catch (DbUpdateException e) 
     { 
      if (okToIgnoreChecks.Any(check => check(e))) 
       return Tuple.Create(0, result); 
      throw; 
     } 
    } 

    public static bool IsDuplicateInsertError(DbUpdateException e) 
    { 
     return GetErrorCode(e) == 2601; 
    } 

    public static bool IsForeignKeyError(DbUpdateException e) 
    { 
     return GetErrorCode(e) == 547; 
    } 

    public static T UpdateEntity<T>(DbContext context, T entity, Action<T> entityModifications) 
     where T : class 
    { 
     return EntityCrud(context, entity, (db, e) => 
     { 
      db.Attach(e); 
      entityModifications(e); 
      return e; 
     }); 
    } 

    public static T DeleteEntity<T>(DbContext context, T entity) 
     where T : class 
    { 
     return EntityCrud(context, entity, (db, e) => db.Remove(e)); 
    } 

    public static T InsertEntity<T>(DbContext context, T entity) 
     where T : class 
    { 
     return EntityCrud(context, entity, (db, e) => db.Add(e)); 
    } 

    public static T EntityCrud<T>(DbContext context, T entity, Func<DbSet<T>, T, T> crudAction) 
     where T : class 
    { 
     return crudAction(context.Set<T>(), entity); 
    } 
} 

Hier ist, wie Sie es verwenden können. Beispiel für das Einfügen einer potenziell doppelten Zeile:

DbUtils.IgnoreErrors(_db,() => DbUtils.InsertEntity(_db, someEntity), 
    DbUtils.IsDuplicateInsertError); 

Keine Ausnahme wird ausgelöst.

Ähnlich wie in früheren Beispiel, aber behandeln Ausnahme FK Verletzung ausdrücklich:

 try 
     { 
      var numInserted = DbUtils.IgnoreErrors(_db,() => DbUtils.InsertEntity(_db, someEntity), DbUtils.IsDuplicateInsertError).Item1; 
      // no FK exception, but maybe unique index violation, safe 
      // to keep going with transaction 
     } 
     catch (DbUpdateException e) 
     { 
      if (DbUtils.IsForeignKeyError(e)) 
      { 
       // you know what to do 
      } 
      throw; // rethrow other db errors 
     } 

Schließlich können Sie rufen Sie die Transaktion begehen, wenn Sie eine explizite Transaktion haben, sonst speichern bereits Kontext aufgerufen wurde.

Verwandte Themen