Nested grouping with Linq? - asp.net

I am trying to create a "simple" timesheet application that is almost doing what I need it to do, I'm not sure it's the most efficient way of doing this, so please give me feedback if you have better ideas... I got help here at SO before (See Group items with Linq and display in table in MVC view where you can also see the database schema). Now I just need to be able to group at one more nested level.
What I get now is a sort of table layout looking sort of like this:
Project Task 1 2 3 (dates with hours in the cells)
ProjA Analysis 8 8 8
ProjB Analysis 8 7 8
(Etc)
Here's the property in the viewmodel that I use to populate the table(s):
public IEnumerable<IGrouping<Project, TimeSegment>> TimeSegmentsByProject
{
get
{
var projectGroups = from timeSegment in ts.TimeSegments
group timeSegment by timeSegment.Project
into projectGroup
select projectGroup;
return projectGroups.ToList();
}
}
And here's the code in the View:
<table>
<tr>
<th>
Projekt
</th>
<th>
Aktivitet
</th>
<th>
Timmar
</th>
</tr>
<% foreach (var item in Model.TimeSegmentsByProject)
{ %>
<tr>
<td>
<%:item.Key.Name %>
</td>
<td>
<table>
<% foreach (var i in item)
{ %>
<tr>
<td>
<%: i.Task.Name %>
</td>
</tr>
<% } %>
</table>
</td>
<td>
<table>
<% foreach (var i in item)
{ %>
<tr>
<% for (int index = 0; index < 30; index++)
{%>
<td width="20px">
<%if (i.Date.Substring(8) == index.ToString())
{ %>
<%: i.Hours %>
<% } %>
</td>
<% } %>
</tr>
<% } %>
</table>
</td>
</tr>
<% } %>
</table>
Now, the problem is that this works fine, but even though Project is grouped and only appears once for each project, task is repeated with a new row for the same task name. I want each task only once, just like for Project. But the linq group expressions and all that has already got me so confused so I have no idea how to get this done. I'm in a bit over my head here, I thought a timesheet application would'nt be such a hard thing (at least a basic one). But I'm fairly close to something that would do, if I could just get this task grouping to work so I don't get it repeated.
Any advice for how to change my code to do this would be appreciated! (And if you think I'm doing it wrong, please tell me how)

Here's a stab at it.
var projectGroups =
from timeSegment in ts.TimeSegments
group timeSegment by new //start off by grouping two properties
{
ProjectName = timeSegment.Project.Name,
TaskName = timeSegment.Task.Name
} into g
select new
{
Key = g.Key,
Values = //and finish off by turning each group into another query.
(
from ts in g
group ts by ts.Date.Substring(8)) into g2
select new {
indexValue = g2.Key,
Hours = g2.Sum(ts2 => ts2.Hours)
}
).ToList()
};
Now, just because I used anonymous types here, doesn't mean you need to. You can declare actual classes and use them to project into. Just be careful with the class that serves as the grouping key (it has ProjectName and TaskName) as you don't want to use reference equality...

Related

multiple table in a view in ASP.NET mvc (with visual basic)

I made Index view by ADO.NET(model), MVC 5 controller use Entity Framework with views(controller).
It is so easy way. but I got a problem when I try make two table (in a view).
How can I show two tables in one view.
I wanna reference multiple models. But I can refer just one model as below
(in view)
#ModelType IEnumerable(Of Ora_Connect.UsersData)
please advise
This is made by Visual Basic
added comment
Hello again.
could you advice for me.
I transf from C# to VB. So, I made masterview for multi table.
I tried about 3days. but not solved yet
thank you so much.
model
Public Class TestMulti_ViewModel
Public Property Test1 As IEnumerable(Of SCT_AUTH_OUT)
Public Property Test2 As IEnumerable(Of SCT_AUTH_OUT_ADD)
control
Namespace Controllers
Public Class TestMultiController
Inherits Controller
'Private db1 As New TestMulti_Entities1
' GET: TestMulti
Function Index_Test(ByVal searchString As String) As ActionResult
Dim mview = New TestMulti_ViewModel
Return View(mview.Test1.ToList())
End Function
End Class
#Modeltype IEnumerable(Of TestMulti_ViewModel)
View
#Modeltype IEnumerable(Of TestMulti_ViewModel)
<table class="table">
#Code Dim item As SCT_AUTH_OUT End Code
<tr>
<th>
#Html.DisplayNameFor(Function(model As SCT_AUTH_OUT) model.ID)
</th>
<th>
#Html.DisplayNameFor(Function(model As SCT_AUTH_OUT) model.PASS)
</th>
<th>
#Html.DisplayNameFor(Function(model As SCT_AUTH_OUT) model.NAMEK)
</th>
<th></th>
</tr>
#code Dim mview = New TestMulti_ViewModel().Test1 end code
#For Each item In mview
#<tr>
<td>
#Html.DisplayFor(Function(model As SCT_AUTH_OUT) item.ID)
</td>
<td>
#Html.DisplayFor(Function(model As SCT_AUTH_OUT) item.PASS)
</td>
<td>
#Html.DisplayFor(Function(modelItem As SCT_AUTH_OUT) item.NAMEK)
</td>
<td>
</td>
</tr>
Next
</table>

i don't know what #foreach in asp.net mvc 5 do

I'm a newbie and i don't know what #foreach do.
this is code written by my teacher
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.MaSach)
</td>
<td>
<img src="~/HinhAnhSP/#Html.DisplayFor(modelItem => item.AnhBia)" width="100" />
</td>
<td>
#Html.DisplayFor(modelItem => item.TenSach)
</td>
<td>
#Html.DisplayFor(modelItem=> item.GiaBan)
</td>
<td>
#Html.ActionLink("Details", "Details", new { id = item.MaSach }) |
#Html.ActionLink("Add to Cart", "Add", "Cart", new { id = item.MaSach }, new {url ="" })
</td>
</tr>
}
foreach is a construct that loops over the items of a collection (actually an enumeration, but never mind that now) and repeats whatever is inside the brackets ({,}) for each of the items, assigning a variable (named item in this case) on each iteration of the loop.
In your case, Model is a collection of items (of some type you haven't shown us). So on each iteration, it'll write all the HTML code between the brackets, assigning a variable named item on each iteration with the contents of the current item.
So imagine Model was a collection of five items of type MyType, which is defined as:
class MyType {
public string Name;
}
Where Name contains the string Hello1 in the first item, Hello2 in the second item, etc:
So if you do:
#foreach(var item in Model) {
<p>#(item.Name)</p>
}
The result once parsed will be:
<p>Hello1</p>
<p>Hello2</p>
<p>Hello3</p>
<p>Hello4</p>
<p>Hello5</p>

View returning empty model to Controller

I'm current working on making a sort of wiki of plants. The first thing we started doing is the managment of the users in the site. For that we made this view that shows the results of a search from the database. The search works fine and the ViewData["datos"] returns what it should, and in this table we show the users (From the Model Usuario). In the end, for each user it should show a button that sends the Model for that user to another Controller. The problem is that the Controller receives an empty model.
Here's the code for the View. The input with the value Editar is the one that should send the model.
#model AtlasPlantasMedicinales.Models.Usuario
#{
ViewBag.Title = "Tabla";
}
#if (ViewData["datos"]!=null){
<table>
#foreach (var elemento in (List<AtlasPlantasMedicinales.Models.Usuario>)(ViewData["datos"]))
{
<tr>
<td>
#elemento.Nombre
</td>
<td>
#elemento.Carrera
</td>
<td>
#elemento.Institucion
</td>
<td>
#elemento.usuario
</td>
<td>
#elemento.correo
</td>
#using (Html.BeginForm("FormularioEditar", "Usuario", FormMethod.Post))
{
<td>#Html.HiddenFor(m => elemento) <input type="submit" value="Editar"></td>
}
</tr>
}
</table>
}
How can we send the model in elemento to the controller?
We have also tried making a Html.HiddenFor for each attribute in the model, but it still doesn't work.
Please keep in mind that me (and my teammmates) are completely new at ASP.NET MVC 4
This is a model binding problem. If you are willing to use the default model binder, use for loop instead of foreach. Your code will go something as as follows (Since you didn't post the Model/ViewModel, I will use ModelId as the property, and usuario as the value):
#for (int i = 0; i < (List<AtlasPlantasMedicinales.Models.Usuario>)(ViewData["datos"])).Count(); i++)
{
var elemento = (List<AtlasPlantasMedicinales.Models.Usuario>)(ViewData["datos"])).ElementAt(i);
<tr>
<td>
#elemento.Nombre
</td>
<td>
#elemento.Carrera
</td>
<td>
#elemento.Institucion
</td>
<td>
#elemento.usuario
</td>
<td>
#elemento.correo
</td>
#using (Html.BeginForm("FormularioEditar", "Usuario", FormMethod.Post))
{
<td>#Html.HiddenFor(m => m.ModelId, new { #Value = elemento.usuario }) <input type="submit" value="Editar"></td>
}
</tr>
}
Now, in the controller you will receive Usuario model with only the ModelId property initialized.
For more information about the model binding, I recommend you to this read this blog post by Scott Hanslman

ASP.Net MVC EditorFor not working

I am trying to render a table, using EditorFor, and a partialview, I think.
I have a model with a List<> property defined like this:
public List<TransactionSplitLine> TransactionSplitLines { get; set; }
The idea is that a user selects a few drop downs and enters a value into an edit box, and clicks a button. The model goes back to the controller, and the controller adds the entered values to the List<>
[HttpPost]
public ActionResult AccountTransaction(AccountTransactionView model)
{
var reply = CreateModel(model);
if (model.CategoryIds != null)
{
foreach (var c in model.CategoryIds)
{
reply.TransactionSplitLines.Add(new TransactionSplitLine { Amount = "100", Category = "Test Category", SubCategory = "Test More", CategoryId = int.Parse(c) });
}
}
reply.TransactionSplitLines.Add(new TransactionSplitLine { Amount = "100", Category = "Test Category", SubCategory = "Test More", CategoryId = 1 });
return View("AccountTransaction", reply);
}
Ignore the CreateModel. It simply sets up some data. Also, I am hardcoding data. This will eventually come from some form values.
The model is then returned to the same screen, allowing the user to ender more data. Any items in the List<> are read and a Table is rendered. I also have to store the current listen item values in hidden fields, so that they can be submitted back, along with the new data entered, so that the list can grow each time the user adds data.
The view is defined like this:
<table width="600">
<thead>
<tr class="headerRow">
<td>
Category
</td>
<td>
Sub Category
</td>
<td>
Amount
</td>
</tr>
</thead>
<tbody>
<%=Html.EditorFor(m=>m.TransactionSplitLines) %>
</tbody>
</table>
This is my first attempt with EditorFor...
My View is in a folder 'Views/BankAccount/AccountTransaction.aspx
I have created a ascx in Views/Shared/TransactionSplitLines.ascx
The code for the ascx is like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BudgieMoneySite.Models.TransactionSplitLine>" %>
<tr>
<td>
<%=Model.Category %>
<%=Html.HiddenFor(x => x.CategoryId)%>
</td>
<td>
<%=Model.SubCategory %>
<%=Html.HiddenFor(x => x.SubCategoryId)%>
</td>
<td>
<%=Model.Amount %>
<%=Html.HiddenFor(x => x.AmountValue)%>
</td>
</tr>
This is data
The 'This is data' is just test stuff, which is never rendered.
When I run this, all that happens is that my output is rendered as:
<table width="600">
<thead>
<tr class="headerRow">
<td>
Category
</td>
<td>
Sub Category
</td>
<td>
Amount
</td>
</tr>
</thead>
<tbody>
Test Category
</tbody>
</table>
It seems like the ascx isn't being used? I'd expect to see the 'This is data' text. But, nothing. Hopefully you can see an obvious fault?
Your editor template should be either:
~/Views/Shared/EditorTemplates/TransactionSplitLine.ascx
or:
~/Views/BankAccount/EditorTemplates/TransactionSplitLine.ascx
The name of the ascx is always the type name of the collection item (TransactionSplitLine and not TransactionSplitLines) and it should be situated at ~/Views/Shared/EditorTemplates or ~Views/ControllerName/EditorTemplates.
Or if you want to use a custom editor template name:
<%= Html.EditorFor(m=>m.TransactionSplitLines, "~/Views/foo.ascx") %>
Or use UIHintAttribute on your model.

How can I handle a dynamically created form submit?

How to handle dynamically generated form submit in asp.net mvc?
Form is dynamically created (number, order and type of elements are always different) and i have to handle it (store the data in the database) in the Controller of asp.net mvc (there is no viewstate). Type of input can be everything; hidden fields, radio buttons, check boxes, text inputs etc..
<% using (Html.BeginForm("AddAnswer","Research")){ %>
<%= Html.Hidden("page", ViewData["curentPage"]) %>
<% foreach (var item in Model){ %>
<span><%= Html.Encode(item.Text) %></span>
<%= Html.ActionLink("Edit", "Edit", new {id=item.QuestionID}) %>
|
<%= Html.ActionLink("Details", "Details", new { id=item.QuestionID })%>
<%switch (item.QuestionTipe.QuestionTipeID){
case 4:%>
<table>
<%foreach (var offeredAnswer in item.OfferedAnswer) {%>
<tr>
<td><%= Html.CheckBox("q" + item.QuestionID, false, new{ value = offeredAnswer.Number})%></td>
<td><%= offeredAnswer.Text%></td>
</tr>
<%}%>
</table>
<% break;
case 1:%>
<table>
<% foreach (var offeredAnswer in item.OfferedAnswer) {%>
<tr>
<td><%= Html.RadioButton("q" + item.QuestionID, false, new{ value = offeredAnswer.Number})%></td>
<td><%= offeredAnswer.Text%></td>
</tr>
<%}%>
</table>
<% break;
case 2:%>
<div style="width:220px; height:20px; padding-top:10px; padding-left:8px;">
<%= Html.TextBox("q" + item.QuestionID, null, new { style = "width:200px;"})%>
</div>
<% break;
case 3:%>
<div style="width:220px;height:20px; padding-top:10px;padding-left:8px;">
<div id="q<%= item.QuestionID %>" style="width:200px;" class="slider">
</div>
<%= Html.Hidden("q" + item.QuestionID, 0)%>
</div>
<% break;
}%>
<%}%>
<p>
<input type="submit" value="Sljedeća strana" />
</p>
<%}%>
In your action method, you can access FormCollection parameter, from there, you can access all your passed in values from your submit action.
public ActionResult YourActionMethod(FormCollection form)
{
}
In order to best help you decide how to process the form, it may be helpful to have some additional information.
Something is making the decision to generate this form, what is doing that? What is it basing its rendering on?
Are there known variations of the form that can be accounted for, or are the elements truly independent of each other?
Are each of the elements themselves known? If so, is it possible to give them a consistent id/name so that they may be recognized on the server-side?
When you speak of "handling" the submission, what is the end goal that you'd like to achieve? For example, are you parsing the form to store in a database?
foreach (var key in form.AllKeys) {
var answers = form.GetValues(key);
if (answers.Count() > 1){
foreach (var value in answers)
{
...
}
}
else
{
...
}
}
This is very simple. I'm checking if there is multiple values for some of the answers in the form.

Resources