【问题标题】:Any way I can make this code cleaner/more efficient? (C#/LINQ-to-SQL)有什么办法可以让这段代码更干净/更高效? (C#/LINQ-to-SQL)
【发布时间】:2011-03-08 01:11:51
【问题描述】:

我一直在为我正在开发的程序整合一个审计解决方案,我在其中使用 LINQ 进行更新/插入操作。我想出了以下解决方案(这是用于插入的 sn-p)(注意 Tables 变量包含已修改的所有表的列表 - 我手动将它们添加到列表中并调用此方法):

 BindingFlags b = BindingFlags.Instance | BindingFlags.Public;
        LINQDataContext dc = new LINQDataContext();
        foreach (object Table in Tables)
        {
            string TableName = Table.ToString().Replace("Project.", "");

            switch (TableName)
            {
                case "Job":
                    string NewJobString = null;
                    Job JobDetails = (Job)Table;
                    var prpsJob = typeof(Job).GetProperties(b);
                    foreach (var p in prpsJob)
                    {
                        object x = p.GetGetMethod().Invoke(JobDetails, null);
                        x = StripDate(x);
                        NewJobString += p.Name + ": " + x + Environment.NewLine;
                    }
                    Audit(JobID, NewJobString, "New Job created", SourceID, "", JobDetails.JobID);
                    break;

                case "Estimation":
                    string NewEstimationsString = null;
                    Estimation EstimationDetails = (Estimation)Table;
                    var prpsEstimations = typeof(Estimation).GetProperties(b);
                    foreach (var p in prpsEstimations)
                    {
                        object x = p.GetGetMethod().Invoke(EstimationDetails, null);
                        x = StripDate(x);
                        NewEstimationsString += p.Name + ": " + x + Environment.NewLine;
                    }
                    Audit(JobID, NewEstimationsString, "New Estimation created", SourceID, "", EstimationDetails.EstimationID);
                    break;

对于每个可能的表名,代码都会继续。代码运行良好,但似乎效率很低——每种情况都有一个几乎相同的块。有没有更有效的方法?

【问题讨论】:

标签: c# asp.net linq-to-sql


【解决方案1】:

您应该能够使用 Lambda 来覆盖重复代码的特定类型部分。这是我一起破解的一些几乎是伪代码....

void TableIsJob(Job j, BindingFlags b) {
   HandleTable("Job", j.JobID, typeof(Job).GetProperties(b),
               p=>p.GetGetMethod().Invoke(j, null));
}


void TableIsEstimation(Estimation e, BindingFlags b) {
   HandleTable("Estimation", e.EstimationID, typeof(Estimation).GetProperties(b),
       p => p.GetGetMethod().Invoke(e, null));
}

void HandleTable(string nm, int ID, PropertyInfo [] props, Func<PropertyInf, Object> i) {
       string desc = string.Join(Environment.NewLine, props.Select(p=>{
                       return string.Format("{0}: {1}", p.Name,
                                    StripDate(i(p)));
               }).ToArray());
       Audit(JobID, desc, string.Format("New {0} created", nm),
             SourceID, "", id);
}

然后你可以用...替换你巨大的 for 循环和开关盒

Tables.Select(t =>
{
   switch (t.ToString().Replace("Project.", ""))
   {
       case "Job":
           TableIsJob((Job)t, b);
           break;
       case "Estimation":
           TableIsEstimation((Estimation)t, b);
           break;
   }
});

这都是假设“高效”是指代码量,而不是执行时间。

【讨论】:

    【解决方案2】:

    你当然可以将下面的代码提取到一个方法中:

     string NewJobString = null;
        Job JobDetails = (Job)Table;
        var prpsJob = typeof(Job).GetProperties(b);
        foreach (var p in prpsJob)
        {
               object x = p.GetGetMethod().Invoke(Table, null);
               x = StripDate(x);
               NewJobString += p.Name + ": " + x + Environment.NewLine;
        }
    
    public string GetProperties(Type t, BindingFlags b)
    {
         StringBuilder sb = new StringBuilder();
         var prpsJob = typeof(t).GetProperties(b);
         foreach (var p in prpsJob)
         {
              object x = p.GetMethod().Invoke(Table, null);
              x = StripDate(x);
              sb.Append(p.Name + ": " + x + Environment.NewLine);
         }
         return sb.ToString()
    }
    

    还有类似的东西

     case "Job":
    
       Audit(JobID, GetProperties(typeof(Job),b), "New Job created", SourceID, "", JobDetails.JobID);
       break;
    

    【讨论】:

      【解决方案3】:

      据我所知,大部分重复的代码都是将对象的值转储到字符串中。

      您可以使用辅助方法来执行此操作:

      public static string DumpObject<T>(T obj)
      {
          StringBuilder sb = new StringBuilder();
      
          var properties = typeof(T).GetProperties(
                           BindingFlags.Instance | BindingFlags.Public);
      
          foreach (var p in properties)
          {
              object x = p.GetGetMethod().Invoke(obj, null);
              x = StripDate(x);
              sb.Append(p.Name).Append(": ").Append(x).AppendLine();
          }
      
          return sb.ToString();
      }
      

      就执行时间而言,这仍然不是非常有效的代码,但它会减少代码重复。我还将附加的字符串更改为使用 StringBuilder。在这种情况下,这只是一种很好的做法。

      顺便说一句,对于局部变量和参数,习惯使用驼峰式大小写:

      switch (tableName)
      {
          case "Job":
              Job jobDetails = (Job)table;
              Audit(jobID, DumpObject(jobDetails), "New Job created",
                    sourceID, "", jobDetails.JobID);
              break;
          // ...
       }
      

      【讨论】:

        【解决方案4】:

        你真的应该使用这个答案from SO quesetion

        如果您不想序列化所有属性,我强烈建议您创建自定义属性并根据它过滤列表(当然之前装饰属性)。一开始就弄乱 IL 很脏,但它是最高效的方法。放下手。

        卢克

        【讨论】:

          【解决方案5】:

          我建议您使用 T4 模板通过 LINQ to SQL templates for T4 生成您的 LINQ to SQL 类。然后在有特殊需求的表上添加接口。

          public interface IHaveEstimation {
             DateTime GetEstimationDate();
          }
          

          在您的 LINQDataContext.tt 文件中,您可以添加一些额外的 T4 生成代码来检测表并将接口添加到该对象:

          <#  
            string markerInterface = String.Empty;
          
            if (class1.Name == "Estimation")
            {
               markerInterface = "IHaveEstimation"; 
            }
          #>
          
          <#=code.Format(class1.TypeAttributes)#>partial class <#=class1.Name#>
          <#=String.IsNullOrEmpty(markerInterface) ? String.Empty : String.Format(" : {0}", markerInterface) #>
          { ... }
          

          在您的 LINQDataContext.cs 文件中,您可以执行以下操作:

          /// <summary>
          /// When the database context is submitted.
          /// </summary>
          /// <param name="failureMode">
          /// the submit failure mode
          /// </param>
          public override void SubmitChanges(ConflictMode failureMode)
          {
            foreach (var insert in changeSet.Inserts.OfType<IHaveEstimation>())
            {
              var estimtation = insert.GetEstimationDate();
              // handle auditing, etc.
            }
          
            // do same for update and delete change sets
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-03-05
            • 2012-10-18
            • 1970-01-01
            • 2020-08-22
            • 2011-06-28
            • 2022-09-30
            • 2015-08-31
            • 1970-01-01
            相关资源
            最近更新 更多