Любой способ сделать этот код чище / эффективнее? (С # / LINQ к SQL) - PullRequest
1 голос
/ 07 марта 2011

Я собираю решение для аудита для разрабатываемой программы, где я использую LINQ для операций обновления / вставки. Я нашел следующее решение (это фрагмент для вставки) (обратите внимание, что переменная 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;

И код продолжается для каждого возможного имени таблицы. Код работает нормально, но кажется довольно неэффективным, имея почти идентичный блок для каждого случая. Есть ли более эффективный способ?

Ответы [ 5 ]

2 голосов
/ 07 марта 2011

Вы должны иметь возможность использовать Lambdas для покрытия специфичных для типа частей повторяющегося кода.Это какой-то почти псевдокод, который я взломал вместе ...

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 и switch на ...

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

Этовсе предполагая, что «эффективный» означает с точки зрения объема кода, а не во время выполнения.

0 голосов
/ 07 марта 2011

Вы действительно должны использовать эти ответы из SO-запроса

И в случае, если вы не хотите сериализовать все свойства, я НАСТОЯТЕЛЬНО советую создать собственный атрибут и отфильтровать список на его основе (конечно украшая свойства раньше).Вначале грязное общение с IL было грязным, но это самый эффективный способ сделать это.Рука опущена.

Люк

0 голосов
/ 07 марта 2011

Из того, что я могу извлечь, большая часть дублированного кода сбрасывает значение объектов в строку.

Вы можете использовать вспомогательный метод для этого:

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;
    // ...
 }
0 голосов
/ 07 марта 2011

Я предлагаю вам использовать шаблоны T4 для генерации классов LINQ to SQL, используя Шаблоны LINQ to SQL для T4 .Затем добавьте интерфейс к таблицам с особыми потребностями.

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
}
0 голосов
/ 07 марта 2011

Ну, конечно, вы можете извлечь приведенный ниже код в метод:

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