重构类以避免在每个方法中编写相同的模式

时间:2014-08-06 09:37:18

标签: c# entity-framework design-patterns try-catch using

我有一个包含许多静态方法的类来管理数据库操作。所有方法都遵循以下模式:

try
{
   using(var trans = new TransactionScope())
   {
      using(var context = new DBContext())
      {
         . . . // method body
      }
      trans.Complete();
   }
}
catch(Excepcion ex)
{
   Log.Error(ex.Message);
   if (ex.InnerException != null)
      Log.Error(ex.InnerException.Message);
}

如何重构我的代码,以便不需要在每个方法中编写这个结构?

编辑实施Jon的回复。

public static T TransactionalOperation<T>(Func<DBContext, T> databaseAction)
{
   T retVal = default(T);
   try
   {
      using (var trans = new TransactionScope())
      {
         using (var context = new DBContext())
         {
            if (databaseAction != null)
               retVal = databaseAction(context);
         }
         trans.Complete();
   }
   catch (Exception ex)
   {
      Log.Error(ex.ToString());
   }
   return retVal;
}
public static void TransactionalOperation(Action<DBContext> databaseAction)
{
   TransactionalOperation(context =>
      {
         databaseAction(context);
         return string.Empty;
      });
}

并像这样使用:

public static string GetUserLanguage(string owner)
{
   return
      TransactionalOperator(context => context.clients.Single(c => c.Id == owner).Language);
}    

3 个答案:

答案 0 :(得分:7)

听起来你应该把“方法体”作为代表传递出来:

public void GiveMeAProperName(Action<DBContext> databaseAction)
{
    try
    {
       using(var trans = new TransactionScope())
       {
          using(var context = new DBContext())
          {
             . . . // method body
          }
          trans.Complete();
       }
    }
    catch(Exception ex)
    {
       Log.Error(ex.Message);
       if (ex.InnerException != null)
          Log.Error(ex.InnerException.Message);
    }
}

然后你可以用:

来调用它
GiveMeAProperName(context =>
{
    // Do stuff with your context here
});

如评论中所述,您可能希望重载:

public void GiveMeAProperName<T>(Func<DBContext, T> databaseAction)

这样您就可以返回一个值。您可以轻松编写Action重载,以便委派给您:

public void GiveMeAProperName(Action<DBContext> databaseAction)
{
    GiveMeAProperName(context =>
    {
        databaseAction(context);
        return "ignored";
    }
}

我强烈建议不同的异常处理:

  • 记录邮件。为什么你认为堆栈跟踪不重要?只记录整个异常 - 包括嵌套异常等
  • 之后你有效地忽略了失败。在大多数情况下,我个人会完全删除try / catch ...在非常高的级别捕获异常,而不是在进行数据库操作的级别。

答案 1 :(得分:0)

创建静态方法:

public static void UsingDBContext(Action<DBContext> action){
try
    {
       using(var trans = new TransactionScope())
       {
          using(var context = new DBContext())
          {
             action(context);
          }
          trans.Complete();
       }
    }
    catch(Exception ex)
    {
       Log.Error(ex.Message);
       if (ex.InnerException != null)
          Log.Error(ex.InnerException.Message);
    }
}

然后:

UsingDbContext( con => {
  ..\\ write your code here
});

答案 2 :(得分:0)

您可以创建一个包含该包装器的泛型方法并调用您提供的委托:

private static T Call<T>(Func<DbContext, T> func) {
  T result = null;
  try {
    using(var trans = new TransactionScope()) {
      using(var context = new DBContext()) {
         result = func(context);
      }
      trans.Complete();
   }
  } catch(Excepcion ex) {
    Log.Error(ex.Message);
    if (ex.InnerException != null) {
      Log.Error(ex.InnerException.Message);
    }
  }
  return result;
}

用法示例:

List<int> someValues = Call(context => {
  // some code that uses context and returns a list of ints
});

由于返回类型是通用的,您可以让它返回您想要的任何类型。它甚至可以是匿名类型。

但是,您应该考虑在方法中重新抛出异常。现在它只会在出现错误时返回null,重新抛出异常会给调用方法提供有关出错的更多信息。