I have two DbSets:
public DbSet<Reports.Models.Application> Application { get; set; }
public DbSet<Reports.Models.Category> Category { get; set; }
In the controller, I'm creating two List<SelectListItem>s:
var applications = _context.Application
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
var categories = _context.Category
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
I'd like to refactor this into a single, private method:
private List<SelectListItem> SelectList<T>(bool blankListItem = false)
{
var selectListItems = _context.<T> <------ doesn't compile
.Select(listItem => new SelectListItem
{
Value = listItem.ID,
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {{T.ToString}}", Value = "" }));
return selectListItems;
}
And call it twice:
var applications = SelectList<Application>();
var categories = SelectList<Category>();
or
var applications = SelectList<Application>(true); // add "choose"
var categories = SelectList<Category>(true); // add "choose"
What's the right way to define the _context.<T> part? Perhaps this should be an extension method of the DbSet instead?
Maybe you can have your dbsets inherit a base class. which would be representing the generic type T.
Something like;
public class BaseClassForDbSets
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Application : BaseClassForDbSets
{
}
public class Category : BaseClassForDbSets
{
}
and then your private method;
private IEnumerable<SelectListItem> GetSelectList<T>(IEnumerable<T> dataSource, bool blankListItem = false) where T : BaseClassForDbSets
{
var selectListItems = dataSource
.Select(listItem => new SelectListItem
{
Value = listItem.Id.ToString(),
Text = listItem.Name
}
).ToList();
if (blankListItem)
selectListItems.Insert(0, (new SelectListItem { Text = $"Choose {nameof(T)}", Value = "" }));
return selectListItems;
}
Then you would call it like;
var applicationCollection = GetSelectList(_context.Application);
var categoryCollection = GetSelectList(_context.Category);
Do note - not tested
My solution uses a different approach, but same result.
Start with an interface:
public interface IBaseSelectItem
{
int Id { get; set; }
string Name { get; set; }
}
Have your entities (Application and Category) implement the interface:
public partial class Category : IBaseSelectItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Create an extension on DbSet:
public static IList<SelectListItem> AsSelectList<T>(this DbSet<T> dbSet, bool useChooseValueOption) where T : class, IBaseSelectItem
{
var selectList = dbSet
.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.Name })
.ToList();
if (useChooseValueOption)
selectList.Insert(0, new SelectListItem { Value = "0", Text = "-Choose Value-" });
return selectList;
}
Then use like this:
var categoriesSelectList = _dbContext.Categories.AsSelectList();
Related
I have LINQ query
Label = c.Name.Translations.Select(label => new Label
{
Rus = label.Text,
Eng = label.Text,
}),
Translation class
public int Id { get; set; }
public string Text { get; set; }
public virtual ICollection<Translation> Translations { get; set; }
public class Translation
{
public int Id { get; set; }
public string Language { get; set; }
public string Text { get; set; }
}
which return list like this
{
"rus":"Нью-Йорк",
"eng":"Нью-Йорк"
},
{
"rus":"New-York",
"eng":"New-York"
My goal is have one item like this
"rus":"Нью-Йорк",
"eng":"New-York"
How can i filter it ?
This should do the job:
var labels = new Dictionary<string, string>();
foreach(var item in c.Name.Translations)
{
labels.add(item.Language, item.Text);
}
Edit
var labels = c.Name.Translations.ToDictionary(t => t.Language, t => t.Text);
var label = c.Name.Translations.ToDictionary(translation => translation.Language, translation => translation.Text);
This creates a dictionary which can be accessed in the following way
label["eng"] // This returns "New York"
You can achieve this by a LINQ query for each of your Label class property:
var resultLabel = new Label
{
Eng = Translations.FirstOrDefault(trans => trans.Language == "English")?.Text,
Rus = Translations.FirstOrDefault(trans => trans.Language == "Russian")?.Text,
};
You can do something like this:
Dictionary<string, string> langText = new Dictionary<string, string>();
c.Name.Translations.ForEach(x => langText.Add(x.Language, x.Text));
langText will contain your data.
How can i implement simple DropDownList without Id.
public AddItemModel()
{
Types = new SelectList(new []{"Type1", "Type2", "Type3"});
}
public SelectList Types { get; set; }
#Html.DropDownListFor(x => x.AddItem, ???
You will need a property on your view model to hold the selected value and then you may try this:
public AddItemModel()
{
var data = new [] { "Type1", "Type2", "Type3" };
Types = data.Select(x => new SelectListItem
{
Value = x,
Text = x,
});
}
public IEnumerable<SelectListItem> Types { get; set; }
public string SelectedType { get; set; }
and then in your view:
#Html.DropDownListFor(x => x.SelectedType, Model.Types)
my Model is
public class ChildMenu
{
public string Name { get; set; }
public string Comments { get; set; }
public List<UlrikenModel.ulriken_tblChildMenu> FormDetails { get; set; }
public long pkChildMenuID { get; set; }
public long fkSubMenuID { get; set; }
[Required(ErrorMessage = "Requird")]
public string ChildManuName { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string Events { get; set; }
public IList<SelectListItem> Drp_Submenu { get; set; }
}
My Controller action is :
public ActionResult FillDeptName()
{
UlrikenEntities db1 = new UlrikenModel.UlrikenEntities();
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem { Text = "-Please select-", Value = "Selects
items" });
var cat = (from c in db1.ulriken_tblSubMenu where c.fkMainMenuID == 1 &&
c.Status == true select new { c.pkSubMenuID,c.SubManuName }).ToArray();
for (int i = 0; i < cat.Length; i++)
{
list.Add(new SelectListItem
{
Text = cat[i].SubManuName,
Value = cat[i].pkSubMenuID.ToString(),
Selected = (cat[i].pkSubMenuID == 1)
});
}
ViewBag.list = list;
return View("ChildMenuOfSubMenu", ViewBag.list);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult ChildMenuOfSubMenu(ChildMenu obj)
{
UlrikenEntities db = new UlrikenEntities();
ulriken_tblChildMenu objchild = new ulriken_tblChildMenu();
objchild.fkSubMenuID = obj.fkSubMenuID;
objchild.ChildMenuName = obj.ChildManuName;
objchild.cPageBody = obj.Name;
db.ulriken_tblChildMenu.Add(objchild);
db.SaveChanges();
return View("ChildMenuOfSubMenu");
}
and view is
#Html.DropDownListFor(m=>m.fkSubMenuID,
(IEnumerable<SelectListItem>)ViewBag.list,"Select" ,new { id = "ddlSubMenu" })
At start dropdown bind successfully but after saving data to database show an exception in
as "There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key
'fkSubMenuID'"
AnyBody guide me where am i doing wrong.
Move that code to the helper class:
public class ControllerHelper
{
public List<SelectListItem> FetchListItems()
{
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem { Text = "-Please select-", Value = "Selects items" });
var cat = (from c in db1.ulriken_tblSubMenu where c.fkMainMenuID == 1 &&
c.Status == true select new { c.pkSubMenuID,c.SubManuName }).ToArray();
for (int i = 0; i < cat.Length; i++)
{
list.Add(new SelectListItem
{
Text = cat[i].SubManuName,
Value = cat[i].pkSubMenuID.ToString(),
Selected = (cat[i].pkSubMenuID == 1)
});
}
return list;
}
}
And then your controller should looks like:
public ActionResult FillDeptName()
{
UlrikenEntities db1 = new UlrikenModel.UlrikenEntities();
ViewBag.list = new ControllerHelper().FetchListItems();
return View("ChildMenuOfSubMenu", ViewBag.list);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult ChildMenuOfSubMenu(ChildMenu obj)
{
UlrikenEntities db = new UlrikenEntities();
ulriken_tblChildMenu objchild = new ulriken_tblChildMenu();
objchild.fkSubMenuID = obj.fkSubMenuID;
objchild.ChildMenuName = obj.ChildManuName;
objchild.cPageBody = obj.Name;
db.ulriken_tblChildMenu.Add(objchild);
db.SaveChanges();
ViewBag.list = new ControllerHelper().FetchListItems();
return View("ChildMenuOfSubMenu");
}
Of course:
new ControllerHelper().FetchListItems();
should be a field in the controller class, for example:
private ControllerHelper controlerHelper;
You can use Interface instead of concerete implementation, if you use DI.
Regards
I am trying to populate an Objective and ObjectiveDetail objects. Here are the classes I have:
partial class Objective
{
public Objective() {
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail
{
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public string Text { get; set; }
public virtual Objective Objective { get; set; }
}
I'm currently populating the only the Objective object from this call:
var objectiveData = GetContent.GetType5();
var objectives = objectiveData.Select(o => new Objective {
Name = o.Name,
Text = o.Text}
);
The data looks like this:
Name Text
0600 header 1
0601 detail abc
0602 detail def
0603 detail ghi
0700 header 2
0701 detail xyz
Is there a way I could modify my LINQ so that only the data where the name field contents end in "00" goes into the Objective object (as it does now) and when the data where the name field contents end in "01" then it creates a new ObjectiveDetail object with "detail abc" etc going into the text field.
This is a picture of what the end result should look like:
A collection of Objectives
new Objective { name = "header 1",
ObjectiveDetails = A collection of ObjectiveDetails
name = "detail abc"
name = "detail def" etc.
Sure you can do that, using [string.EndsWith] method like:1
.Where(r=> r.Name.EndsWith("00"))
Modify your query as:
var objectives = objectiveData
.Where(r => r.Name.EndsWith("00"))
.Select(o => new Objective {
Name = o.Name,
Text = o.Text}
);
It's somewhat unclear what you are asking, but you can put complex logic inside the Select() if you need to:
var objectives = objectiveData.Select(o =>
{
var result = new Objective
{
Name = o.Name,
Text = o.Text
};
if (o.Name != null && o.Name.EndsWith("01"))
{
result.ObjectiveDetails.Add
(
new ObjectiveDetail
{
ObjectiveDetailId = o.ObjectiveId,
Name = o.Name,
Text = o.Text,
Objective = result
}
);
}
return result;
});
(Note that I'm guessing at what you need; you will need to correct the logic to do what you really want.)
Looks like you want to do some sort of conditional mapping. I like Matthew Watson's answer, but it's a bit unclear why he's always creating an Objective instance every time. Here's some LINQ-less code which I believe is more readable, and maps the way I think you'd want:
public class Mapper
{
public List<Objective> Objectives = new List<Objective>();
public class Objective
{
public int ObjectiveId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
public Objective()
{
ObjectiveDetails = new List<ObjectiveDetail>();
}
}
public class ObjectiveDetail
{
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public string Text { get; set; }
public virtual Objective Objective { get; set; }
}
public void Assign()
{
var objectiveData = new[] // Hard-coded test data. We don't know what the type of each item in this list is, so I use an anonymous type
{
new {Name = "0600", Text = "Header 06"},
new {Name = "0601", Text = "06 Detail 01"},
new {Name = "0602", Text = "06 Detail 02"},
new {Name = "0603", Text = "06 Detail 03"},
new {Name = "0700", Text = "Header 07"},
new {Name = "0701", Text = "07 Detail 01"},
new {Name = "0702", Text = "07 Detail 02"}
};
// Create Objectives first
var id = 1;
foreach (var item in objectiveData.Where(i => i.Name.EndsWith("00")))
{
Objectives.Add(new Objective { ObjectiveId = id, Name = item.Name, Text = item.Text });
id++;
}
// Create ObjectiveDetails
id = 1;
foreach (var item in objectiveData.Where(i => !i.Name.EndsWith("00")))
{
var itemLocal = item;
var matchingObjective = Objectives.FirstOrDefault(o => o.Name.StartsWith(itemLocal.Name.Substring(0, 2)));
var objectiveDetail = new ObjectiveDetail
{
ObjectiveDetailId = id,
Text = item.Text,
ObjectiveId = matchingObjective != null ? matchingObjective.ObjectiveId : 0,
Objective = matchingObjective
};
if (matchingObjective != null)
{
matchingObjective.ObjectiveDetails.Add(objectiveDetail);
}
id++;
}
// At the end of this method you should have a list of Objectives, each with their ObjectiveDetails children
}
}
Output:
Hope this helps.
I have a list of items, i.e, List<SearchFilter>, and this is the SearchFilter object:
public class SearchFilter
{
public int ItemID { get { return ValueInt("ItemID"); } }
public string ItemName { get { return ValueString("ItemName"); } }
public string Type { get { return ValueString("Type"); } }
}
How do I group by the Type, and project the grouped item into a new list of GroupedFilter, i.e:
public class Filter
{
public int ItemID { get; set; }
public string ItemName { get; set; }
}
public class GroupedFilter
{
public int Type { get; set; }
public List<Filter> Filters { get; set; }
}
Thanks.
var result = items.GroupBy(
sf => sf.Type,
sf => new Filter() { ItemID = sf.ItemID, ItemName = sf.ItemName },
(t, f) => new GroupedFilter() { Type = t, Filters = new List<Filter>(f) });
But you need to make sure your GroupedFilter.Type property is a string to match your SearchFilter.Type property.
With Linq query syntax it is longer and more complex but just for reference:
var grpFilters = (from itm in list group itm by itm.Type into grp select
new GroupedFilter
{
Type = grp.Key,
Filters = grp.Select(g => new Filter
{
ItemID = g.ItemID,
ItemName = g.ItemName
}).ToList()
}).ToList();
Somebody may find it more readable because they don't know all the possible parameters to GroupBy().