I need to use groupby in my code to show ActionId and group based on the controllerId field.
The problem is, I except I can show ActionId with a select like this :
var group = conList.GroupBy(x => x.ControllerId).Select(x=>x.ActionId).ToList();
But when I use the select it just shows me Key and Count.
this my code:
var group = conList.GroupBy(x => x.ControllerId).Select(x=>x.Key).ToList(););
How would I be able to solve this problem?
Based on you question I did this:
Let's say that you have the following class:
public class Controller
{
public int ControllerId { get; set; }
public int ActionId { get; set; }
}
And the following data:
var list = new List<Controller>()
{
new Controller() { ControllerId = 1, ActionId = 1 },
new Controller() { ControllerId = 1, ActionId = 2 },
new Controller() { ControllerId = 1, ActionId = 3 },
new Controller() { ControllerId = 2, ActionId = 4 },
new Controller() { ControllerId = 2, ActionId = 5 },
new Controller() { ControllerId = 2, ActionId = 6 }
};
What this command says is. Group my object of type Controller by ControllerId (the Key) and for each grouped item (g) I will have a list of type Controller.
var groupedList = list.GroupBy(x => x.ControllerId).Select(g => g);
foreach(var g in groupedList)
{
//g --> is your grouped object a list of type Controller by unique ControllerId.
//g.Key --> is the value of each unique ControllerId.
Console.WriteLine("Group: " + g.Key + " Actions: " + string.Join(";", g.Select(actions => actions.ActionId)));
}
Expected result
Group: 1 Actions: 1;2;3
Group: 2 Actions: 4;5;6
You can test the code above here.
I hope that explanation can help you out with your problem. Good luck!
Here you can find some samples and more explanation about GroupBy here.
Related
CREATE TABLE [dbo].[Topic] (
[Id] SMALLINT NOT NULL,
[ParentId] SMALLINT NULL
);
I have a simple table (above) with a parent/child hierarchy. I'm using Entity Framework to extract the data. The number of rows is less than 100.
I want to get a list of descendant ID, consisting of the children, grandchildren and so on (possibly with the option of including the original root parent). As the table only contains a small number of rows, it's probably easier to extract all the data to a List<Topic> first and work on that, but I stand to be corrected.
The preferred output would be: Dictionary<int, List<int>>
Where the key would be the ID and the list would contain child/grandchild ID's
I've looked at tens and tens of solutions online but I can't find a solution that meets my needs. Can anyone help?
You could populate a dictionary with the ParentId->Id relations and use that to resolve sub-topics:
// prepare dictionary
var dictionary = new Dictionary<short, List<Topic>>();
// in real life this would get replaced by your database query
var topics = new List<Topic>
{
new Topic { Id = 1 },
new Topic { Id = 2, ParentId = 1 },
new Topic { Id = 3, ParentId = 1 },
new Topic { Id = 4, ParentId = 1 },
new Topic { Id = 5, ParentId = 1 },
new Topic { Id = 6, ParentId = 2 },
new Topic { Id = 7, ParentId = 2 },
new Topic { Id = 8, ParentId = 3 },
new Topic { Id = 9, ParentId = 4 },
new Topic { Id = 10, ParentId = 4 },
new Topic { Id = 11, ParentId = 8 },
new Topic { Id = 12, ParentId = 8 }
};
// populate dictionary with relations from DB
foreach(var topic in topics)
{
var key = topic.ParentId ?? -1;
if(!dictionary.ContainsKey(key))
{
dictionary.Add(key, new List<Topic>());
}
dictionary[key].Add(topic);
}
Now that you have the mappings available, you can write a simple recursive iterator method to resolve the descendants of a given id:
IEnumerable<short> GetDescendantIDs(short from)
{
if(dictionary.ContainsKey(from))
{
foreach(var topic in dictionary[from])
{
yield return topic.Id;
foreach(var child in GetDescendants(topic.Id))
yield return child;
}
}
}
// resolves to [1, 2, 6, 7, 3, 8, 11, 12, 4, 9, 10, 5]
var descendantsOfRoot = GetDescendantIDs(-1);
// resolves to [8, 11, 12]
var descendantsOfThree = GetDescendantIDs(3);
The Topic class used in the example above is just:
class Topic
{
internal short Id { get; set; }
internal short? ParentId { get; set; }
}
Consider that result has to be stored in tree:
public class TopicModel
{
public int Id { get; set; }
public int? ParentId{ get; set; }
public List<TopicModel> Children { get; set; };
}
Building tree:
// retrieveing from database
var plainResult = context.Topic
.Select(t => new TopicModel
{
Id = x.Id
ParentId = x.ParentId
})
.ToList();
var lookup = plainResult.Where(x => x.ParentId != null).ToLookup(x => x.ParentId);
foreach (c in plainResult)
{
if (lookup.Contains(c.Id))
{
c.Children = lookup[c.Id].ToList();
}
}
// here we have all root items with children intialized
var rootItems = plainResult.Where(x => x.ParentId == null).ToList();
And searching for children:
public static IEnumerable<TopicModel> GetAllChildren(TopicModel model)
{
if (model.Children != null)
{
foreach (var c in model.Children)
{
yield return c;
foreach (sc in GetAllChildren(c))
yield return sc;
}
}
}
Hi I have two entity models that has data in tables as:
Table Group
id name
1 NW
2 SW
3 NE
Second Table is
Location
id groupId count
1 1 34
2 2 5
3 2 90
4 1 33
5 1 23
I want to write a linq query or using context in EF so that i can group by each groupId so that result is
groupdId 1
{count 34, 33, 23}
groupId 2
{count 90,5}
I have tried following
from e in DbContext.Set<Group>()
join a in DbContext.Set<locatin>() on e.Id equals a.groupId
Select new Test
{
groupId= e.groupdId
tables = new List<tbl>()
{
count= a.count;
}
}
public class Test
{
public int groupId {get; set;}
public IEnumerable<gbl> Tables {get; set;}
}
public class tbl
{
public int count {get; set;}
}
In writing the query when I get to count=a.count, I get intellisense error can not resolve. Please let me know how to correct my query so that I get heading of groupId then another array with count numbers only. Thanks.
Following will work in this case:
var result =
groups.Join(locations,g=>g.id,l=>l.groupId,(g,l)=>new {l})
.GroupBy(x =>x.l.groupId,x=>x.l.count)
.Select(y=>new Test
{
groupId= y.Key,
tables = y.Select(n => new tbl{count = n})
});
Following are the steps:
Join two tables groups and locations using the id and groupid
GroupBy the result and project the count
Select and result should be IEnumerable<Test>
Following is my complete code, that I have used to create the solution (using LinqPad):
void Main()
{
var groups = Group.CreateList();
var locations = Location.CreateList();
var result =
groups.Join(locations,g=>g.id,l=>l.groupId,(g,l)=>new {l})
.GroupBy(x =>x.l.groupId,x=>x.l.count)
.Select(y=>new Test
{
groupId= y.Key,
tables = y.Select(n => new tbl{count = n})
});
result.Dump();
}
public class Group
{
public int id;
public string name;
public static List<Group> CreateList()
{
return new List<Group>
{
new Group
{
id = 1,
name = "NW"
},
new Group
{
id = 2,
name = "SW"
},
new Group
{
id = 3,
name = "NE"
}
};
}
}
public class Location
{
public int id;
public int groupId;
public int count;
public static List<Location> CreateList()
{
return new List<Location>
{
new Location
{
id = 1,
groupId = 1,
count = 34
},
new Location
{
id = 2,
groupId = 2,
count = 5
},
new Location
{
id = 3,
groupId = 2,
count = 90
},
new Location
{
id = 4,
groupId = 1,
count = 33
},
new Location
{
id = 5,
groupId = 1,
count = 23
}
};
}
}
public class Test
{
public int groupId {get; set;}
public IEnumerable<tbl> tables {get; set;}
}
public class tbl
{
public int count {get; set;}
}
I have a controller which orders Orders by there Category.Sequence property as follows:
public ActionResult Index()
{
// Return orders of this Date
var currentDate = DateTime.Now.Date;
var orders = db.Orders.Include(o => o.Product)
.Where(o => o.Date == currentDate && o.IsConfirmed == false)
.OrderBy(o => o.Product.Category.Sequence == null)
.ThenBy(o => o.Product.Category.Sequence)
.ToList();
return View("Index" ,new ShoppingListViewModel(orders, db.Locks.Any(l => l.Date == DateTime.Today)));
}
Where the ViewModel looks like this:
public class ShoppingListViewModel
{
public ICollection<Order> Orders { get; private set; }
public ICollection<Category> Categories { get; private set; }
(...)
}
Now I want to unittest if this works. To do this I have the following unittest:
[TestMethod]
public void TestShoppingListControllerOrderBySequenceNumber()
{
// Arrange
var db = this.getTestDBContext();
var controller = new ShoppingListController(db);
// Invoke
var viewResult = controller.Index() as ViewResult;
var ordersFromView = (ViewModels.ShoppingListViewModel)viewResult.Model;
var orderList = ordersFromView.Orders;
// Assert
var expected = 2;
var actual = orderList.First().Product.Category.Sequence;
Assert.AreEqual(expected, actual,
string.Format("The first order in the list should have" +
" Sequence {0}, but had Sequence {1}", expected, actual));
}
Where GetTestDBContext does the following:
private IContext getTestDBContext()
{
// Arrange
var memoryItems = new FakeDbSet<Order>
{
// Categorie 1 and unconfirmed
new Order { OrderId = 1,
UnitPrice = 1.00M,
Quantity = 2,
Date = DateTime.Today,
IsConfirmed = false,
ProductId = 1,
User = new User { Name = "Piet", Email = "piet#paulusma.nl", IsAvailable = true },
Product = new Product {
Category = new Category {CategoryId = 1, Name = "Categorie1", Sequence = 3},
Name = "Testproduct",
Price = 1.00M,
Visible = true
}
},
// Categorie 2 and unconfirmed
new Order { OrderId = 2,
UnitPrice = 2.00M,
Quantity = 1,
Date = DateTime.Today,
IsConfirmed = false,
ProductId = 2,
User = new User { Name = "Kees", Email = "Kees#DeHond.nl", IsAvailable = true },
Product = new Product {
Category = new Category {CategoryId = 2, Name = "Categorie2", Sequence = 2},
Name = "Testproduct2",
Price = 2.00M,
Visible = true
}
},
// Categorie 2 and confirmed
new Order { OrderId = 2,
UnitPrice = 1.00M,
Quantity = 1,
Date = DateTime.Today,
IsConfirmed = true,
ProductId = 3,
User = new User { Name = "Jan", Email = "Jan#DeBouvrier.de", IsAvailable = true },
Product = new Product {
Category = new Category {CategoryId = 2, Name = "Categorie2", Sequence = 2},
Name = "Testproduct2",
Price = 3.10M,
Visible = true
}
},
};
So Category named "Categorie2" With CategoryId2 has Sequence 2 and another Category named Categorie1 has Sequence is 3.
The unit test returns:
Test Name: TestShoppingListControllerOrderBySequenceNumber Test
Outcome: Failed Test Duration: 0:00:00.0141778
Result Message: Assert.AreEqual failed. Expected:<2>. Actual:<3>. The
first order in the list should have Sequence 2, but had sequence 3
Since expected was the Sequence 2 and I got the Category.Sequence 3 value, apparently the TestMethod does not return an ordered list by Category.Sequence as defined in the controller.
How can I accomplish that the ordersFromView property in the TestMethod to actually return the ordered sequence as it comes from the controller?
Consider these two tables:
ClassID Name
1 C1
2 C2
ClassID List<CourseSession>
1 [Object that has value "A"], [Object that has value "B"]
2 [Object that has value "B"], [Object that has value "C"]
When I join these two tables in Linq, I get:
ID Name List
1 C1 [A, B]
2 C2 [A, B]
Wheras I need to expand them:
ID Name List
1 C1 A
1 C1 B
2 C2 A
2 C2 B
Linq code:
var classes = from row in t.AsEnumerable()
select new
{
ClassID = row.Field<Guid>("ClassID"),
ClassName = row.Field<string>("Name"),
};
var classCourses = from row in classes.AsEnumerable()
select new
{
ID = row.ID,
CourseSessionList = GetAllCoursesByID(row.ID).AsEnumerable()
};
//Attempt to join
var expandedClassCourse = from classRow in classes
join ccRow in classCourses
on classRow.ID equals ccRow.ID
into filteredExpandedClasses
select filteredExpandedClasses;
I'm not sure how to achieve this. Any ideas?
Something like (not sure what your model looks like):
context.CouseSessions.Where(cs => /* condition goes here */)
.Select(cs =>
new
{
Name = cs.Name,
Class = cs.Class.Name
});
or
context.Classes.Where(c => /* condition goes here */)
.SelectMany(c => c.Courses)
.Select(cs =>
new
{
Name = cs.Name,
Class = cs.Class.Name
});
I created two models based on assumption. I hope this helps.
class Info
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> List { get; set; }
}
class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public string s { get; set; }
}
static void Main(string[] args)
{
var infos = new List<Info> { new Info { Id = 1, Name = "c1", List = new List<string> { "A", "B" } }, new Info { Id = 2, Name = "c2", List = new List<string> { "A", "B" } } };
var myClasses = new List<MyClass>();
foreach (var info in infos)
{
myClasses.AddRange(info.List.Select(a => new MyClass { Id = info.Id, Name = info.Name, s = a }));
}
}
(from c in classList
join s in sessionList on c.ClassID equals s.ClassID
select new
{
ID = c.ClassID,
Name = c.Name,
SessionList = s.SessionList
})
.SelectMany(e => e.SessionList.Select(s => new
{
ID = e.ClassID,
Name = e.Name,
Session = s
}))
I have an action method returning a JsonResult in my controller:
public JsonResult GetDetails()
{
var rows = //Linq-To-SQL
//Linq-To-Entities
var lifts = (from r in rows
group r by new { r.LiftID, r.LiftDate } into g
select new
{
ID = g.Key.LiftID,
Date = g.Key.LiftDate.ToShortDateString(),
Driver = g.Where(x => x.IsDriver)
.Select(x => x.p).Single().Name,
Passengers = g.Where(x => !x.IsDriver)
.Select(x => x.p.Name)
.Aggregate((x, y) => x + ", " + y)
}).ToList();
return Json(lifts);
}
I use the result in a jQuery script to write out a table.
The data looks like:
ID | Date | Driver | Passengers
1 | 20/06/2010 | David Neale | John Smith, Paul Jones
etc...
I would like the names to be hyperlinks to the route Person\{id} I.e. David Neale. The p property corresponds to a Person object containing both Name and ID.
I don't want to construct the URL manually. How would I construct the object to contain the names as hyperlinks using the MVC routing engine?
It's fairly easy.
Just use Url.Action("actionName", "controllerName", params)
It will create a string using the routing engine, so if you change the routes your code will keep workin just fine
First, I think you have a mistake in your model or in your Linq-To-SQL or Linq-To-Entities querys. Because you don't have an ID for your Persons (Drivers and Passagers), and you will definitly need it if you want a link with an Id of the Person. I think you need to split your Lifts from your Persons and have 2 separate entities (linked by its Id of course).
Second, you need to pass the ID of the Persons from the Controller to the View.
class Lift
{
public int LiftID { get; set; }
public DateTime LiftDate { get; set; }
public IEnumerable<Person> p { get; set; }
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDriver { get; set; }
}
public JsonResult GetDetails()
{
var rows = new Lift[] { //Linq-To-SQL and Linq-To-Entities replaced by an example
new Lift{LiftID = 1, LiftDate= DateTime.Now, p = new Person[] {
new Person{IsDriver = true, Id = 1, Name = "David Neale"},
new Person{IsDriver = false, Id = 2, Name = "John Smith"},
new Person{IsDriver = false, Id = 3, Name = "Paul Jones"}
}},
new Lift{LiftID = 2, LiftDate= DateTime.Now, p = p = new Person[] {
new Person{IsDriver = true, Id = 4, Name = "Daniel Faraday"},
new Person{IsDriver = false, Id = 2, Name = "John Smith"}
}}
};
var lifts = (from r in rows
select new
{
ID = r.LiftID,
Date = r.LiftDate.ToShortDateString(),
Driver = r.p.Where(x => x.IsDriver)
.Select(x => x.Name).Single(),
Passengers = r.p.Where(x => !x.IsDriver)
.Select(x => x.Name)
.Aggregate((x, y) => x + ", " + y)
}).ToList();
return Json(lifts);
}
Then, once you have the ID on the View you use it to make the link using Html.ActionLink:
<%= Html.ActionLink("Person", "Index", new { id = p.Id })%>
If you know the action and controller beforehand, you can do something like:
var baseURL = '<%=Url.Action("MyAction","MyController") %>';
and then manually build your links from jQuery, with href set to something like baseUrl+"/"+personId