2011-01-02 10 views
1

私はLinqでこの問題を解決する方法についてすべて混乱しています。私は実用的なソリューションを持っていますが、それを行うためのコードは、私が考えてあまりにも複雑で、円形である:LinqとMVC2アプリケーションでオブジェクトのネストされたチェーンを取得する方法は?

私は、次の表(簡体字)を持つデータベースを照会したいMVC 2のタイムシートアプリケーションを持っている:

プロジェクトは、多くのタスクを持つことができ、タスクが多くtimesegmentsを持つことができます。

プロジェクト タスク TimeSegment

関係は次のとおりです。

これをさまざまな方法で照会できる必要があります。例は次のとおりです。ビューは、テーブル内のプロジェクトのリストを表示するレポートです。各プロジェクトのタスクがリストされ、そのタスクで作業された時間数の合計が続きます。 timeegmentオブジェクトは時間を保持するものです。ここで

ビューです:私が述べたように、この作品、

再び
public List<TimeSegment> GetTimeSegments(int customerId, string startdate, string enddate) 
    { 
     var timeSegments = _repository.TimeSegments 
      .Where(timeSegment => timeSegment.Customer.CustomerId == customerId) 
      .Where(timeSegment => timeSegment.DateObject.Date >= DateTime.Parse(startdate) && 
         timeSegment.DateObject.Date <= DateTime.Parse(enddate)); 

     return timeSegments.ToList(); 
    } 

    public List<Project> GetProjects(List<TimeSegment> timeSegments) 
    { 
     var projectGroups = from timeSegment in timeSegments 
         group timeSegment by timeSegment.Task 
          into g 
          group g by g.Key.Project 
           into pg 
           select new 
           { 
            Project = pg.Key, 
            Tasks = pg.Key.Tasks 
           }; 


     List<Project> projectList = new List<Project>(); 
     foreach (var group in projectGroups) 
     { 
      Project p = group.Project; 
      foreach (var task in p.Tasks) 
      { 
       task.CurrentTimeSegments = timeSegments.Where(ts => ts.TaskId == task.TaskId).ToList(); 
       p.CurrentTasks.Add(task); 
      } 
      projectList.Add(p); 
     } 
     return projectList; 
    } 

、:

[HttpPost] 
    public ActionResult MonthlyReports(FormCollection collection) 
    { 
     MonthlyReportViewModel vm = new MonthlyReportViewModel(); 

     vm.StartDate = collection["StartDate"]; 
     vm.EndDate = collection["EndDate"]; 
     int customerId = Int32.Parse(collection["Customers"]); 


      List<TimeSegment> allTimeSegments = GetTimeSegments(customerId, vm.StartDate, vm.EndDate); 
      vm.Projects = GetProjects(allTimeSegments); 
      vm.Employee = "Alla"; 


     vm.Customer = _repository.GetCustomer(customerId); 

     vm.TotalCost = vm.Projects.SelectMany(project => project.CurrentTasks).Sum(task => task.Cost); //Corresponds to above foreach 
     vm.TotalHours = vm.Projects.SelectMany(project => project.CurrentTasks).Sum(task => task.TaskHours); 
     vm.TotalCostAndVAT = vm.TotalCost * 1.25; 
     vm.VAT = vm.TotalCost * 0.25; 
     return View("MonthlyReport", vm); 
    } 

そして "ヘルパー" 方法:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Report.Master" Inherits="System.Web.Mvc.ViewPage<Tidrapportering.ViewModels.MonthlyReportViewModel>" %> 

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> 
    Månadsrapport 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 
    <h1> 
     Månadsrapport</h1> 
    <div style="margin-top: 20px;"> 
     <span style="font-weight: bold">Kund: </span> 
     <%: Model.Customer.CustomerName %> 
    </div> 
    <div style="margin-bottom: 20px"> 
     <span style="font-weight: bold">Period: </span> 
     <%: Model.StartDate %> - <%: Model.EndDate %> 
    </div> 
     <div style="margin-bottom: 20px"> 
     <span style="font-weight: bold">Underlag för: </span> 
     <%: Model.Employee %> 
    </div> 

    <table class="mainTable"> 
     <tr> 
      <th style="width: 25%"> 
       Projekt 
      </th> 
      <th> 
       Specifikation 
      </th> 
     </tr> 
     <% foreach (var project in Model.Projects) 
      { 
     %> 
     <tr> 
      <td style="vertical-align: top; padding-top: 10pt; width: 25%"> 
       <%:project.ProjectName %> 
      </td> 
      <td> 
       <table class="detailsTable"> 
        <tr> 
         <th> 
          Aktivitet 
         </th> 
         <th> 
          Timmar 
         </th> 
         <th> 
          Ex moms 
         </th> 
        </tr> 
        <% foreach (var task in project.CurrentTasks) 
         {%> 
        <tr class="taskrow"> 
         <td class="task" style="width: 40%"> 
          <%: task.TaskName %> 
         </td> 
         <td style="width: 30%"> 
          <%: task.TaskHours.ToString()%> 
         </td> 
         <td style="width: 30%"> 
          <%: String.Format("{0:C}", task.Cost)%> 
         </td> 
        </tr> 
        <% } %> 
       </table> 
      </td> 
     </tr> 
     <% } %> 
    </table> 
    <table class="summaryTable"> 
     <tr> 
      <td style="width: 25%"> 
      </td> 
      <td> 
       <table style="width: 100%"> 
        <tr> 
         <td style="width: 40%"> 
          Totalt: 
         </td> 
         <td style="width: 30%"> 
          <%: Model.TotalHours.ToString() %> 
         </td> 
         <td style="width: 30%"> 
          <%: String.Format("{0:C}", Model.TotalCost)%> 
         </td> 
        </tr> 
       </table> 
      </td> 
     </tr> 
    </table> 
    <div class="price"> 
     <table> 
      <tr> 
      <td>Moms: </td> 
       <td style="padding-left: 15px;"> 

         <%: String.Format("{0:C}", Model.VAT)%> 

       </td> 
      </tr> 
      <tr> 
      <td>Att betala: </td> 
       <td style="padding-left: 15px;"> 

         <%: String.Format("{0:C}", Model.TotalCostAndVAT)%> 

       </td> 
      </tr> 
     </table> 
    </div> 
</asp:Content> 

は、ここでアクションメソッドですもちろん、実際には複雑で、私がコーディングしていても、それを見て自分自身を混乱させてしまいます。私は、私が望むものを達成するためのもっと簡単な方法がなければならないと感じています。基本的に私が達成したいものを見ることができます:

私はプロジェクトのコレクションを取得したいと思います。各プロジェクトには関連するタスクの集まりがあるはずです。また、各タスクには、指定された期間の関連するタイムセグメントのコレクションが含まれている必要があります。選択したプロジェクトとタスクは、この期間のタイムセグメントを持つプロジェクトとタスクでなければならないことに注意してください。私はこの期間内にタイムセグメントを持たないプロジェクトやタスクをすべて望んでいません。

GetProjects()メソッドのようなLinqクエリのグループがこれを達成しているようですが(これは日付などの条件が適用されるように拡張されている場合)、これを返してビューに渡すことはできません。それは匿名のオブジェクトです。私もそのようなクエリで特定の型を作成しようとしましたが、その周りに私の頭をラップすることができませんでした...

私は行方不明とこれを達成するためのいくつかのより簡単な方法があります。最終的にもいくつかの異なるクエリを実行できる必要があります。

「CurrentTimeSegments」プロパティなどで解決した方法も本当に好きではありません。これらのプロパティは実際には最初のモデルオブジェクトには存在しません。ネストされたオブジェクトチェーンの各部分に対してフィルタリングされた結果を配置するために部分クラスに追加しました...

アイデア?

UPDATE:How do I create a nested group-by dictionary using LINQ?

私はこれらの線に沿って何かを探していますだと思い。それがこのようなものであれば、私は試したが失敗したので、私の問題にそれを翻訳する助けに本当に感謝しています。しかし、私はこの戦略についてはオフになっているかもしれません(正直言って、私はこのような質問が予想されるより複雑です)、もしそうなら、私に教えてください!

更新2:Jon Skeet(文字通り、私の問題に翻訳されていない)の例を試してみると、Dictionaryタイプは得られないので、上記のリンクに関しては、まったく指定する...コンパイラは変換できないと言っています。そして、この結果はViewModelとしてViewに渡されなければならないので、返す特定の型を持つ必要があります。

+0

ちょうど側の注意点一般的なコードの品質に...代わりにIListをまたはIEnumerableをを使用し、パブリックメソッドやプロパティにリストを返すことはありません:) –

答えて

1

回答は圧倒的ではありません:-)私は自分自身で答えを試みます。 ViewModelの値を保持するための中間ヘルパークラスを作成する必要があるので、私の意見ではまだそれほど満足のいくものではありません。誰かが私にそれをするよりよい方法を示すことができるなら、私は別の答えを受け入れることを嬉しく思うでしょう。

public class MonthlyReportViewModel 
    { 
     public List<CurrentProject> Projects { get; set; } 
     public string Employee { get; set; } 
     public Customer Customer { get; set; } 
     public int TotalHours { get; set; } 
     public int TotalCost { get; set; } 
     public double TotalCostAndVAT { get; set; } 
     public double VAT { get; set; } 

     public string Month { get; set; } 
     public string StartDate { get; set; } 
     public string EndDate { get; set; } 
    } 

    public class CurrentProject 
    { 
     public string Name { get; set; } 
     public List<CurrentTask> CurrentTasks { get; set; } 
    } 

    public class CurrentTask 
    { 
     public string Name { get; set; } 
     public List<TimeSegment> CurrentTimeSegments { get; set; } 
     public int Cost { get; set; } 
     public int Hours { get; set; } 
     public int Fee { get; set; } 
    } 

そして私は、フィルタリングTimeSegments取得する前に、同様の方法があります:

とにかく、私がやったことは、私はフィルタオブジェクトを保持するためにいくつかのクラスでのViewModelを作成しています

public List<TimeSegment> GetTimeSegments(int customerId, string startdate, string enddate) 
    { 
     var timeSegments = _repository.TimeSegments 
      .Where(timeSegment => timeSegment.Customer.CustomerId == customerId) 
      .Where(timeSegment => timeSegment.DateObject.Date >= DateTime.Parse(startdate) && 
         timeSegment.DateObject.Date <= DateTime.Parse(enddate)); 

     return timeSegments.ToList(); 
    } 

そして最後に、私はグループに新しいGetProjects()メソッドを作成し、ろ過timesegmentsに基づいてフィルタリング事業を返す:

public List<CurrentProject> GetProjects(List<TimeSegment> timeSegments) 
    { 
     IEnumerable<CurrentProject> currentProjects = from timeSegment in timeSegments 
         group timeSegment by timeSegment.Task.Project 
          into projectTimeSegments 
          select new CurrentProject() 
             { 
              Name = projectTimeSegments.Key.ProjectName, 
              CurrentTasks = (from projectTimeSegment in projectTimeSegments 
                  group projectTimeSegment by projectTimeSegment.Task 
                   into taskTimeSegments 
                   let fee = taskTimeSegments.Key.Fee 
                   let hours = taskTimeSegments.Sum(t=>t.Hours) 
                   select new CurrentTask 
                      { 
                       Name = taskTimeSegments.Key.TaskName, 
                       Fee = fee, 
                       Cost = hours*fee, 
                       Hours = hours, 
                       CurrentTimeSegments = 
                        taskTimeSegments.ToList() 
                      }).ToList() 
             }; 
     return currentProjects.ToList(); 
    } 

これを達成するにはこれが最善の方法ですか、それともまだ複雑になっていますか?

+0

他の提案はありません...私は自分の答えを受け入れます! – Anders

0

私はこれを解決する最善の方法は、プロジェクトタスクTimeSegmentなどの必要なすべてのデータを返すストアプロシージャを作成することだと思います。このようなもの:

また、LoadWithの機会を使用することができます。

+2

いいえ、ポイントはSQL、私はLINQの中でこれを行うにはしたくないが、私はそれがLinqでうまくいっていることを知っている、私はちょうどどのように把握することができません... – Anders

+0

LoadWithを使用してくださいhttp://msdn.microsoft.com/en-us/library/bb548760.aspx –

+0

申し訳ありませんが、私は持っているどのようにこれが私の質問に当てはまるか分からず、リンクからも理解できませんでした。これがどうやって私の問題を解決するのですか? – Anders

関連する問題