How to dynamically pass .Include & .Select - asp.net-mvc

I am working on an asp.net mvc5 web application , with EF-6. I am trying to dynamically pass .Include & .Select as follow:-
var query = context.SecurityRoles.AsQueryable();
foreach (var include in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(include);
}
return await query
.SingleOrDefaultAsync(a2 => a2.SecurityRoleID == id);
}
which i will be calling as follow:-
var securityrole = await uniteofwork.SecurityRoleRepository
.FindSecurityRole(id.Value, "SecurityRoleModulePermissions.Select(a2=>a2.Module),SecurityRoleModulePermissions.Select(a2=>a2.PermissionLevel)),Staffs");
But the i am getting the following exception:-
A specified Include path is not valid. The EntityType
'SkillManagementModel.SecurityRoleModulePermission' does not declare a
navigation property with the name 'Select(a2=>a2'

You can include your navigation properties dynamically like this:
public async Task<SecurityRole> FindSecurityRole(Expression<Func<SecurityRole, bool>> predicate, params Expression<Func<SecurityRole, object>>[] includeProperties)
{
var query = context.SecurityRoles.AsQueryable();
if (includeProperties != null)
query = includeProperties.Aggregate(query, (current, include) => current.Include(include));
return await query.SingleOrDefaultAsync(predicate);
}
var securityrole = await uniteofwork.SecurityRoleRepository.FindSecurityRole
(sr => sr.Id == id.Value, sr => sr.Module, sr =>sr.PermissionLevel)

Related

Orderby clause not working with drop down list in mvc

I am populating a drop down list using Linq and the orderby clause doesn't seem to work.
public List<Hello> getManagers()
{
var que = (from man in db.Table1
where man.Role == "Manager"
orderby man.Name
select new Hello
{
Managers = man.Name
}).Distinct().ToList();
return que;
}
Controller Class:
public ActionResult Index()
{
rp = new RequestProcess();
ViewBag.ID = fillSelectedList("", "ID", rp);
ViewBag.Managers = fillSelectedList("", "Managers", rp);
return View(""); //View 1
}
public static List<SelectListItem> fillSelectedList(string selValue, string type, RequestProcess rp )
{
List<SelectListItem> list = new List<SelectListItem>();
SelectListItem obj = new SelectListItem();
if (type == "Managers") {
var tempList= rp.getManagers();
tempList.ForEach(x =>
{
obj = new SelectListItem();
obj.Text = x.Managers;
obj.Value = x.Managers;
obj.Selected = x.Managers == selValue ? true : false;
list.Add(obj);
});
}
return list;
}
I am still receiving an un-ordered list. Any fixes?
The result is not ordered, because method Distinct does not return ordered results. What you need to do instead is to first call Disctinct, and only then OrderBy:
var que = (from man in db.Table1
where man.Role == "Manager"
select new Hello
{
Managers = man.Name
}).Distinct() // <- First distinct ...
.OrderBy(x => x.Managers) // <- ... then order by
.ToList();
As mentioned in the answer above, you need to sort the result after Distinct().
Also note that you are mixing Lambda expression and LINQ to Entities Queries... you may want to consider choosing one of them for consistency (though there is no syntax error if you mix them). This is the same query using lambda expression:
var que = _context.Table1
.Where(m => m.Role == "Manager")
.Select(h => new Hello { Managers = h.Name })
.Distinct()
.OrderBy(o => o.Managers)
.ToList();

how to return selected columns based on user choice

I'm trying to limit which fields are returned by an API based on a parameter called fields which I accept multiple strings doing this
private readonly string[] fields;
public string[] SelectiveSerializer(string fields)
{
string[] _fields;
var fieldColl = fields.Split(',');
_fields = fieldColl
.Select(f => f.ToLower().Trim())
.ToArray();
return _fields;
}
I want to be able to choose what I return based on whatever the user gives me in _fields. Normal way to do it:
var linq = (from entity in db.users
where entity.ID== id
&& entity.ON== false
select( new {
ID = entity.ID,
FirstName = entity.FirstName,
LastName =entity.LastName,
FotherName = entity.FotherName
}).ToList();
but here I have to specify the fields in Select (ID, FirstName ..etc), which I want it to be dynamic based on what fields[] has. Is there a way to do this?
sort of this (which is wrong):
var linq = (from entity in db.users
where entity.ID== id
&& entity.ON== false
select( new {
foreach (string s in _fields)
{
entity.s;
}
}).ToList();
Use a ternary expression for each assignment
var user = entityContext.Users.Where(u => u.ID == id)
.Select(u => new {
ID = _fields.Contains['id'] ? u.ID : 0,
FirstName = _fields.Contains['firstname'] ? u.FirstName : null,
LastName = _fields.Contains['lastname'] ? u.LastName : null,
otherName = _fields.Contains['othername'] ? u.otherName : null
})
.ToList();
I also would put the field names in a HashSet<string> for a better performance.
var _fields = new HashSet<string>(fields.Split(',').Select(f => f.ToLower().Trim()));
This solution keeps all the properties but sets the unwanted ones to null. If you want to dynamically add properties, see this other SO question: How to dynamically create a class in C#?. But note that this only useful in scenarios where objects of this type are processed dynamically as well.
I was finally able to do this with minimal work.
assuming the filter is a string list. string array.
so to avoid reflection and all that jazz, I iterate over each record and see if the variable is in the filter list, then create a dic entry with (var,val) assuming that no duplicate var in the same record, which can be catch if you want but I don't have this issue.
Then at the end add that dic to a list.
the method accept anonymous type list and a filter list.
public static List<Dictionary<string, object>> filteredList(IEnumerable source, string[] filter)
{
var filteredList = new List<Dictionary<string, object>>();
foreach (var single in source)
{
var type = single.GetType();
var props = type.GetProperties();
var singleRecord = new Dictionary<string, object>();
foreach (var v in props)
{
if (filter.Contains(v.Name))
{
var tempValue = type.GetProperty(v.Name).GetValue(single, null);
singleRecord.Add(v.Name, tempValue);
}
}
filteredList.Add(singleRecord);
}
return filteredList;
}

LINQ Select Query with Where Condition Which returns single record

public ActionResult CreateArea(int? cityid)
{
if (cityid == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Name = db.Cities.Where(c => c.CityID == cityid)
.Select(x => x.CityName);
ViewBag.message = Name;
Area city = db.Areas.Find(cityid);
return View(city);
}
This my ActionMethod in this i want cityname which i m passing to view through viewbag but my LINQ query is wrong it not displaying cityname instead of this query is printing to my view.plz help me to correct my LINQ query.
You're missing the "execution" of the "expression" like .ToList(), .First(), .FirstOrDefault(), .Single() .....
var name = db.Cities.Where(c => c.CityID == cityid).Select(x => x.CityName).FirstOrDefault();
var Name = db.Cities.Select(x => x.CityName).Where(c => c.CityID == cityid).FirstOrDefault()
ViewBag.message = Name;
Area city = db.Areas.Find(cityid);
return View(city);
try something like this
You need to return the single result from the query and then access the name property:
var Name = db.Cities.Single(c => c.CityID == cityid).CityName;
This may help you!
var model = model.Where(i => i.ParamKey == Key).First();

Dynamically apply filters on Entity Framework's entity using lambda expression

I need to have a method like this, where I can apply Where(x =>x. ...) and Include(x => x.RelatedEntity) and OrderBy(x=>x. ...) on a given entity.
Something like this:
public List<TEntity> ApplyFilter<TEntity>(TEntity entity,
List<filters> filters /* List of filters: 'filters' */)
where TEntity : BaseEntity
{
using (var db = new MyDbContext()){
var query = db.Set<TEntity>().AsQueryable;
//apply filters to 'query'
query.include(/*multiple related entities*/);
query.applyfilters(/*filters*/);
return query.ToList();
}
}
And I need to pass what I need to be filtered/included as lambda expressions.
NOTE: I searched a lot about how I can do it but I really wasn't able to find anything. I'm new to this part of C# / Entity Framework and I really didn't even know what keywords to search for.
Thank you for the help
You'll want to use a LINQ expression
public List<TEntity> ApplyFilter<TEntity>(
Expression<Func<TEntity, bool>> filter,
Expression<Func<TEntity, object>> orderBy,
params Expression<Func<TEntity, object>>[] includes) where TEntity : BaseEntity
{
using (var db = new MyDbContext())
{
var query = db.Set<TEntity>().AsQueryable();
query = query.Where(filter);
query = query.OrderBy(orderBy);
if (includes != null)
{
foreach (var include in includes)
{
query = query.Include(include);
}
}
return query.ToList();
}
}
To use the method:
ApplyFilter<TestObject>(
x => x.Prop1 == "foo",
x => x.Prop2,
x => x.Prop3, x => x.Prop4);
Like this?
var result = Repository.PurchaseProposalItem.GetDbSet();
if (filters.FilterByBrand) result = result.Where(p => p.GS_Product.GS_ProductBrand.PBr_Id == filters.BrandId);
if (filters.FilterByFamily) result = result.Where(p => p.GS_Product.GS_ProductFamily.PFa_Id == filters.FamilyId);
if (filters.FilterBySubFamily) result = result.Where(p => p.GS_Product.GS_ProductSubFamily.PSu_Id == filters.SubFamilyId);
if (filters.FilterByProductType) result = result.Where(p => p.GS_Product.Pro_Type == filters.ProductTypeEnum);
return result;

how to check if "params Expression" does not contain values

I have the following repository method:-
public async Task<Skill> FindSkill(int id, params Expression<Func<Skill, object>>[] includeProperties)
{
var query = context.Skills.AsQueryable();
if (includeProperties != null )
query = includeProperties.Aggregate(query, (current, include) => current.Include(include));
return await query.SingleOrDefaultAsync(a => a.SkillID == id);
}
and I call this method as follow:-
public async Task<ActionResult> Deactivate(int id, Byte[] timestamp = null)
{
var skill = await unitofwork.SkillRepository.FindSkill(id);
//snip
}
but I can not detect when I am not passing any params Expression, I tried also the following checks but did not work:-
includeProperties.Count() != 0 || includeProperties[0].Name == "0"
now I have noted that inside VS the following will be received when passing empty list:-
When using params and not supplying any parameters for it. An empty array will be created. You should use includeProperties.Length !=0 as Nick Bailey suggests in his comment.

Resources