C# 处理实体框架中的异常 4

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19042107/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:57:58  来源:igfitidea点击:

Handle exceptions in entity framework 4

c#sqlentity-framework

提问by Cristiano Coelho

I need a way to distinguish between SQL exceptions using entity framework LINQ, for example how to distinguish foreing key constraint violation, or unique constraint violation when all i get from the DbUpdateException is a ton of nested inner exceptions and useless long error messages? Are there any lower level exceptions where i can do something like "Catch FKException"; catch "uniqueException" or something like that.

我需要一种使用实体框架 LINQ 来区分 SQL 异常的方法,例如,当我从 DbUpdateException 获得的只是大量嵌套的内部异常和无用的长错误消息时,如何区分前键约束违规或唯一约束违规?是否有任何较低级别的异常,我可以执行诸如“Catch FKException”之类的操作;捕获“uniqueException”或类似的东西。

采纳答案by Cristiano Coelho

Using sql error codes...

使用sql错误代码...

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

回答by Mr.LamYahoo

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

回答by y?s??la

I wrote couple utility methods for this:

我为此编写了几个实用方法:

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

Here is how you can use it. Example of inserting a potentially duplicate row:

这是您如何使用它。插入潜在重复行的示例:

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

No exception will be thrown.

不会抛出任何异常。

Similar to previous example, but handle FK violation exception explicitly:

与前面的示例类似,但显式处理 FK 违规异常:

        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
        }

Eventually you can call commit transaction if you have an explicit transaction, otherwise save has been called on context already.

最终,如果您有显式事务,则可以调用 commit 事务,否则已经在上下文中调用了 save。