How to SelectListItem with Foreign Key? - c#

I have 2 tables and I want to display them together using the Foreign Key. For Employees
ID
FirstName
LastName
JobID
1
John
Doe
1
2
Mike
Sully
1
3
Ray
Man
3
and Jobs
JobID
JobTitle
1
Animator
2
3D Artist
3
Illustrator
How can I make it show up in a dropdownlist as
John Doe (Animator)
Mike Sully (Animator)
Ray Man (3D Artist)
My Model
public string Employee { get; set; }
public IEnumerable<SelectListItem> EmployeeList { get; set; }
My Code
public IEnumerable<SelectListItem> GetEmployee()
{
var employee = db.Employees.Select(s => new SelectListItem
{
Value = s.EmployeeID.ToString(),
Text = s.FirstName + " " + s.LastName
}).ToList();
var placeholder = new SelectListItem() { Value = null, Text = "Select Employee..." };
//employee.Insert(0, placeholder);
return new SelectList(employee, "Value", "Text");
}

You may fetch the job title by job ID, like this:
IEnumerable<string> titles = db.Employees.Select(
e => string.Format("{0} {1} ({2})",
e.FirstName,
e.LastName,
db.Jobs.Single(j => j.JobID == e.JobID)
.JobTitle)
);
titles contains the items for your drop-down list.

Related

Transpose datatable id / header / value to another table with those headers

I have data currently parsed in to a .Net System.Data.DataTable in this format:
ID | Header | Value
1 Name Jim
1 Age 34
1 Gender M
2 Name Gibby
2 Age 32
2 Gender F
3 Name Bob
3 Age 100
3 Gender U
and I need to get it into another DataTable that has those fields:
ID | Name | Age | Gender
1 Jim 34 M
2 Gill 32 F
3 Bob 100 U
This is achievable in SQL using:
select id,
max(case when header = 'Name' then Value end) as name,
max(case when header = 'Age' then Value end) as Age,
max(case when header = 'Gender' then Value end) as Gender
from pivottest
group by id;
but this means I'd need to update a sql table with the values and then run this query, where I'd prefer to do it in a c# method before pushing it to the DB.
Is this possible to do in C#, probably using Linq? Where would I even start?!
Assuming you don't mind that it's not returned in a DataTable, you could actually create a list of persons like this:
Group the data in the data table by Id:
var groups = <yourDataTable>.AsEnumerable().GroupBy(x => x.Field<int>("Id"));
Create a class called Person and Enum for Gender:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
}
public enum Gender
{
M,
F,
U
}
Create a list of persons called "people" and loop through the groupings and map them to a Person like this:
var people = new List<Person>();
foreach (var group in groups)
{
people.Add(new Person
{
Id = group.Key,
Name = group.FirstOrDefault(x => x.Field<string>("Header") == "Name").Field<string>("Value"),
Age = Convert.ToInt32(group.FirstOrDefault(x => x.Field<string>("Header") == "Age").Field<string>("Value")),
Gender = (Gender)Enum.Parse(typeof(Gender), group.FirstOrDefault(x => x.Field<string>("Header") == "Gender").Field<string>("Value"))
});
}
I put this into a console app to demonstrate:
class Program
{
static void Main(string[] args)
{
var data = new DataTable();
data.Columns.Add("Id", typeof(int));
data.Columns.Add("Header", typeof(string));
data.Columns.Add("Value", typeof(string));
data.Rows.Add(1, "Name", "Jim");
data.Rows.Add(1, "Age", "34");
data.Rows.Add(1, "Gender", "M");
data.Rows.Add(2, "Name", "Gibby");
data.Rows.Add(2, "Age", "32");
data.Rows.Add(2, "Gender", "F");
data.Rows.Add(3, "Name", "Bob");
data.Rows.Add(3, "Age", "100");
data.Rows.Add(3, "Gender", "U");
Console.WriteLine("Current data:");
foreach (DataRow row in data.Rows)
{
Console.WriteLine("id: {0}, header: {1}, value: {2}", row[0], row[1], row[2]);
}
var groups = data.AsEnumerable().GroupBy(x => x.Field<int>("Id"));
var people = new List<Person>();
foreach (var group in groups)
{
people.Add(new Person
{
Id = group.Key,
Name = group.FirstOrDefault(x => x.Field<string>("Header") == "Name").Field<string>("Value"),
Age = Convert.ToInt32(group.FirstOrDefault(x => x.Field<string>("Header") == "Age").Field<string>("Value")),
Gender = (Gender)Enum.Parse(typeof(Gender), group.FirstOrDefault(x => x.Field<string>("Header") == "Gender").Field<string>("Value"))
});
}
Console.WriteLine("People list data:");
foreach (var person in people)
{
Console.WriteLine("Id: {0}, Name: {1}, Age: {2}, Gender: {3}", person.Id, person.Name, person.Age, person.Gender.ToString());
}
Console.ReadLine();
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Gender Gender { get; set; }
}
public enum Gender
{
M,
F,
U
}
Results:

Add rows with same ID together after SELECT using LINQ

Hi all I am using LINQ to query a table like this:
AlbumID | Spotify | iTunes
--------------------------
5 | 12 | 4
5 | 18 | 6
6 | 10 | 8
I would like to add together rows with the same unique AlbumID, I am aware of GroupBy but that groups together all similar IDs so I used Where that uses albumID (an input arg in this example it could be 5)
var platformArray = Context.Analytics.Where(x => x.AlbumID == albumId).Select(s => new
{
Id = s.AlbumID,
Spotify = s.SpotifyClicks,
Itunes = s.ItunesClicks
});
But when I print out to debug console, it does not combine the unique ID's
foreach (var item in platformArray)
{
Debug.WriteLine(item.Id);
Debug.WriteLine(item.Spotify);
Debug.WriteLine(item.Itunes);
}
I see (added arrows for clarity):
5 <-ID
12 <-Spotify
4 <-ITunes
5 <-ID
18 <-Spotify
6 <-ITunes
How can I reformat my LINQ code to print out something like:
5 <-ID
30 <-Spotify
10 <-Itunes
Thanks!
EDIT: adding my Analytic Class
public class Analytic
{
private object p1;
private object p2;
private object p3;
public Analytic(object p1, object p2, object p3)
{
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
[Key]
public int Id { get; set; }
public int AlbumID { get; set; }
public int SpotifyClicks { get; set; }
public int ItunesClicks { get; set; }
public virtual Album Album { get; set; }
}
Try below code:
var List = from element in prod
group element by new { element.Id } into g
select new yourClass
(
Id = g.First().ID,
Spotify = g.Sum(s => s.Spotify)
Itunes = g.Sum(s => s.Itunes)
);
So I managed to figure this out from the help of Hasan's comment on my original post and some additional fixes:
var query = Context.Analytics
.Where(x => x.AlbumID == albumId)
.GroupBy(c => c.AlbumID)
.Select(
g =>
new
{
Id = g.Key,
Spotify = g.Sum(s => s.SpotifyClicks),
ITunes = g.Sum(s => s.ItunesClicks),
}
);
This only queries for my specific albumID and sums together all rows with that specific ID!
Thanks everyone
So after you have grouped your Albums into groups of Albums with the same AlbumId, you want to transform all Albums in the group into one object, containing the common AlbumId, the total of all Spotify of all Albums in your group, and the total of all iTunes of all Albums in your group.
var result = albums.GroupBy(album => album.AlbumId)
// from every group, make one element with the sum of Spotify and sum of itunes:
.Select(group => new
{
AlbumId = group.Key,
Spotify = group.Select(groupElement => groupElement.Spotify).Sum(),
ITunes = group.Select(groupElement => groupElement.Itunes).Sum(),
});
Simple comme bonjour!

ASP MVC 5 C# Recursive Trees To Group and Indent A DropDownList

I have a Model called Category which has a recursive (parent child) relationship as follows:
public class Category: ITreeNode<Category>
{
public byte Id { get; set; }
public byte? ParentId { get; set; }
public string Name { get; set; }
public Category Parent { get; set; }
public IList<Category> Children { get; set; }
}
I want to generate a dropdownlist that is heirachaly grouped based on the parent child relations where I can also select the parent group as follows - basically I want to indent the children from the parent:
<select name="Category" id="Category">
<option value="0">All</option>
<option value="1">Cars</option>
<option value="2">--Toyota</option>
<option value="3">--Nissan</option>
<option value="4">Fruits</option>
<option value="5">--Apples</option>
<option value="6">--Oranges</option>
</select>
My Table data is as follows:
Id | ParentId | Name
----------------------
1 | null | Cars
2 | 1 | Toyota
3 | 1 | Nissan
4 | null | Fruits
5 | 4 | Apples
6 | 4 | Oranges
Currently I have the following LINQ query but it simply generates a normal dropdown ordered by id.
public IEnumerable<SelectListItem> GetCategoriesSelectList()
{
var categories = new List<SelectListItem>
{
new SelectListItem() {Value = "0", Text = "All" }
}.Concat(_context.Category.Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
}).OrderBy(x => x.Value).ToList());
return categories
}
How can I amend the LINQ query to get them correctly grouped and indented for when it's rendered using a Html.DropDownListFor.
I've tried to amend my original select list to achieve some kind of tree as follows but I'm stuck on the EnumerateNodes method. The original below is for printing out a ul list which I pulled from the following site http://www.codeproject.com/Articles/23949/Building-Trees-from-Lists-in-NET. How do I iterate through it and return each item, if its a child then append -- to the name and add to my select list?
public IEnumerable<SelectListItem> GetCategoriesSelectList()
{
IList<Category> listOfNodes = GetListOfNodes();
IList<Category> topLevelCategories = TreeHelper.ConvertTOForest(listOfNodes);
var cats = new List<SelectListItem>
{
new SelectListItem { Value = "0", Text = "All"}
};
foreach(var category in topLevelCategories) {
var catName = EnumerateNodes(category);
cats.Add(new SelectListItem { Value = category.Id.ToString(), Text = catName });
}
return cats;
}
private List<Category> GetListOfNodes()
{
List<Category> sourceCategories = _context.Category.ToList();
List<Category> categories = new List<Category>();
foreach (Category sourceCategory in sourceCategories)
{
Category s = new Category();
s.Id = sourceCategory.Id;
s.Name = sourceCategory.Name;
if (sourceCategory.ParentId != null)
{
s.Parent = new Category();
s.Parent.Id = (int)sourceCategory.ParentId;
}
categories.Add(s);
}
return categories;
}
private static string EnumerateNodes(Category parent)
{
if (category.Children.Count > 0) {
Response.Write("<ul>");
foreach(Category child in category.Children) {
EnumerateNodes(child);
}
Response.Write("</ul>");
}
Response.Write("</li>");
}
You can first select the parent categories (where ParentId is null) and then for each parent category, select its child categories.
public IEnumerable<SelectListItem> GetCategoriesSelectList()
{
var categories = _context.Category.ToList();
// Initialise list and add first "All" item
List<SelectListItem> options = new List<SelectListItem>
{
new SelectListItem(){ Value = "0", Text = "All" }
};
// Get the top level parents
var parents = categories.Where(x => x.ParentId == null);
foreach (var parent in parents)
{
// Add SelectListItem for the parent
options.Add(new SelectListItem()
{
Value = parent.Id.ToString(),
Text = parent.Name
});
// Get the child items associated with the parent
var children = categories.Where(x => x.ParentId == parent.Id);
// Add SelectListItem for each child
foreach (var child in children)
{
options.Add(new SelectListItem()
{
Value = child.Id.ToString(),
Text = string.Format("--{0}",child.Name)
});
}
}
return options;
}
You'll need to call the categories recursively so that you can get infinite level nesting if you want it. The above code mentioned only works for 2 levels of nesting, so you will only have 2 levels here: Parents and Children.
Create the function in such a way that it takes the categoryId as a parameter and the recursive function takes the categoryId as the parentId so you can go deeper into your tree by another level, but eventually you can have that new category be a parent to another category.

Linq to Entities grouping on multiple fields with an Or operator

I have a list of orders and need to create groups of similar orders in an efficient manner (preferably without pulling back all orders and comparing manually).
Each order has a UserId and Email Address with additional fields of Postcode and Country.
So I need to create groups on the following rules:
If an order has the same (UserId or Email Address) and (postcode and country) place in a group
Given the following entity and data
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
}
Example data
OrderId UserId Email PostCode Country
1 1 blah1 111 au
2 1 blah2 111 au
3 2 blah1 111 au
4 2 blah2 111 au
5 3 blah3 111 nz
6 3 blah3 111 nz
Example Results
Group 1
1 1 blah1 111 au
2 1 blah2 111 au
3 2 blah1 111 au
4 2 blah2 111 au
Group 2
5 3 blah3 111 nz
6 3 blah3 111 nz
The only way I can seem to think of doing this through manual iteration in memory
Is this possible to do with Linq to entities cleanly?
Update
After researching this for a while, I think I've come to the conclusion the only way to achieve what I want is to do two groupbys and manually combine them in memory.
Update 2
Thinking about this logically there is seemingly no elegant solution for this problem in linq and may need to be done with SQL and CTEs or some other recursive solution.
Here I have done Group by on more than 1 column,
OrderViewModel OrderList =from ord in order
group ord by new
{
ord.PostCode,
ord.Country,
ord.UserID,
ord.Email,
ord.OrderID
} into gr
select new OrderViewModel
{
OrderID = gr.Key.OrderID,
UserID = gr.Key.UserID,
Email = gr.Key.Email,
PostCode=gr.key.PostCode,
Country=gr.key.Country,
OrderList= gr.ToList()
};
where OrderViewModel is something ::
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
public List<Order> OrderList { get; set; }
}
Where you should have to decide Priority of Grouping data,
&Data which you don't want to Group will be taken as a list.
Try this:-
var query = from ord in orders
group ord by new { ord.Country, ord.PostCode } into g
select new
{
Country = g.Key.Country,
PostCode = g.Key.PostCode,
Orders = g.GroupBy(x => x.OrderId)
.GroupBy(i => i.Count() > 1 ?
new { OrderID = i.Key, Email = default(string) }
: new { OrderID = default(int), i.First().Email })
.Select(o => o.Count() == 1 ?
new { o.Key, Orders = o.First().ToList() }
: new { o.Key, Orders = o.Select(z => z.First()).ToList() })
};
Here is the complete Working Fiddle.
Assuming your Order Class will be like below:
public class Order
{
public int UserId { get; set; }
public int OrderId { get; set; }
public string Email { get; set; }
public int PostCode { get; set; }
public string Country { get; set; }
}
Assuming and Grouping the List of Orders Like below:
public List<List<Order>> grouping()
{
// List of Orders to Group
List<Order> orderList = new List<Order>();
orderList.Add(new Order { UserId = 1, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 2, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 3, OrderId = 2007, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 4, OrderId = 2008, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 5, OrderId = 2008, Email = "blah1#test.com", PostCode = 111, Country = "India" });
orderList.Add(new Order { UserId = 6, OrderId = 2001, Email = "blah1#test.com", PostCode = 111, Country = "India" });
// Grouping
var groupedOrderList = orderList
.GroupBy(u => u.OrderId)
.Select(grp => grp.ToList()).ToList(); // Groping the Records based on the OrderId
return groupedOrderList; // The Result will be List<List<Order>>
}
Your group statement will group by OrderId. The Result will be come as you expected like above you showed.
Update:
You can Add additional fields as well to be grouped based on the multiple fields.
Like below :
.GroupBy(u => u.OrderId & u.UserId)

Category and Sub Category in single Dropdownlistfor - MVC Razor

I have a table with Category Id and Parent Category Id.
Category Id Category Name Parent Category Id
1 Desktop 0
2 PC 1
3 Mac 1
How do I return it in a dropdownlist in the below format:
hi buddy i looked for this much and i couldn't find it but finally i coded it myself.
i will try to tell you how i solved this problem and works great just like ur screenshot. here is my secreenshot
here is my category model it is almost same with ur model
public class Category
{
public int ID { get; set; }
public string CategoryName { get; set; }
public int? ParentID { get; set; }
[ForeignKey("ParentID")]
public virtual ICollection<Category> SubCategories { get; set; }
}
then i created a View category model like this
public class ViewModelCategory
{
public Category Category { get; set; }
public List<SelectListItem> Categories { get; set; }
public List<SelectListItem> ChildCategories { get; set; }
public List<SelectListItem> AllCategories
{
get
{
List<SelectListItem> sli = new List<SelectListItem>();
foreach (SelectListItem item in Categories)
{
sli.Add(item);
}
foreach (SelectListItem item in ChildCategories)
{
int i = item.Text.IndexOf(">");
string text = item.Text.Substring(0, i-1);
foreach (SelectListItem listItem in ChildCategories)
{
int k = listItem.Text.LastIndexOf(">");
string text2 = listItem.Text.Substring(k+1);
if (text2.Contains(text))
{
item.Text = listItem.Text + " > " + item.Text.Substring(i+2);
}
}
sli.Add(item);
}
return sli;
}
}
}
my codes can look like little complicate but is is not actually.
there are something important in my ViewModelCategory model when u look at the model u will see 3 selectlistitem properties. i wanna talk about these first.
i created one of them called as "Categories" for main categories that has no any parent id value.
second of them called as "ChildCategories" contains only subcategories that has parent id value
when u look at he controller codes u will see already.
and the last one of called as "AllCategories". this one is the important one couse i merged all categories in this property to send categories to dropdownlisfor in controller's view. but it is not just for this i also did something important in this property while i merged them when u look at the loops u will see it. i used loops to place categories to their parents
then used my ViewModelCategory model in controller like this
ViewModelCategory vmc = new ViewModelCategory
{
Category = category,
Categories = _businessCategories.GetItems().Where(x => x.ParentID == null).Select(x => new SelectListItem
{
Text = x.CategoryName,
Value = x.ID.ToString()
}).ToList(),
ChildCategories = _businessCategories.GetItems().Where(x=>x.ParentID != null).Select(x => new SelectListItem
{
Text = _businessCategories.GetItems().Where(a => a.ID == x.ParentID).FirstOrDefault().CategoryName + " > " + x.CategoryName,
Value = x.ID.ToString()
}).ToList(),
};
and finally i welcomed my datas from view like this
<div class="form-group">
<label class="col-sm-4 control-label">Sub Category</label>
<div class="col-sm-8">
#Html.DropDownListFor(model => model.Category.ParentID, Model.AllCategories.OrderBy(x=>x.Text), "Select Category", new {#class = "form-control"})
#Html.ValidationMessageFor(model => model.Category.ParentID)
</div>
</div>
hope u will like this have a nice day
The best thing I can think of would be to create some class that hold the Name and Value for a drop down list and use it to create a SelectList in your Action.
public class SelectListValue{
public string Text {get;set;}
public string Value {get;set;}
}
public ActionResult SomeActionMethod()
{
var categories = GetCategories() //Get your list of categories
var selectListItems = new List<SelectListValue>();
foreach(var item in categories)
{
var text = GetCategoryDisplay() //You will need to figure out how to generate the text, like Components > Web Cameras. This doesn't need to be a method. It could be a property on the Category object.
selectListItems.Add(new SelectListValue {Text = text, Value = item.CategoryId.ToString()});
}
ViewData["CategoryList"] = new SelectList(selectListItems, "Value", "Text");
}
You will need to have a way somewhere, probably in your model, to take generate the name string. I am guessing you are already doing that. Then in your view you would do something like
#Html.DropDownListFor(x => x.CategoryId, ViewData["CategoryList"] as SelectList)
This should work, or at least something like it.

Categories