I need to ignore null parameters in the where clause so that i can fetch the appropriate data with the applied filters from the user side. Inorder to acheive this, I am currently using the if..else nested approach which grows in size as the number of parameters grow. I would like to know if there is any other effecient way of handling this scenario by avoiding the number of lines and complexity and improving readablility
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
var members = new List<Members>();
if (package != null)
{
//include package
if (packageStatus != null)
{
//include package, packageStatus
if (branch != null)
{
//include package,packageStatus,branch
members = db.Members.Where(x => x.PackageName == package && x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include package,packageStatus
members = db.Members.Where(x => x.PackageName == package && x.PackageStatus == packageStatus).ToList();
}
}
else
{
if (branch != null)
{
//include package,branch
members = db.Members.Where(x => x.PackageName == package && x.Branch == branch).ToList();
}
else
{
//include package
members = db.Members.Where(x => x.PackageName == package).ToList();
}
}
}
else
{
if (packageStatus != null)
{
//include packageStatus
if (branch != null)
{
//include packageStatus,branch
members = db.Members.Where(x => x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include packageStatus
members = db.Members.Where(x => x.PackageStatus == packageStatus).ToList();
}
}
else
{
if (branch != null)
{
//include packageStatus,branch
members = db.Members.Where(x => x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include nothing
members = db.Members.ToList();
}
}
}
return members;
}
You can add those conditions to the query. It won't make for the most readable SQL, but assuming you find readable code more important and trust SQL Server's optimizer:
members = db.Members.Where(x =>
(package == null || x.PackageName == package) &&
(packageStatus == null || x.PackageStatus == packageStatus) &&
(branch == null || x.Branch == branch)
).ToList();
Alternatively, you could conditionally append Where()s to a variable of type IQueryable<Member>. See for example entity framework: conditional filter.
I would like to suggest a readable version of the method:
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
var members = new List<Members>();
members = db.Members.ToList();
if (package != null)
{
members = members.Where(x => x.PackageName == package);
}
if (packageStatus != null)
{
members = members.Where(x => x.PackageStatus == packageStatus);
}
if (branch != null)
{
members = members.Where(x => x.Branch == branch);
}
return members.ToList();
}
I think this works (haven't tried compiling it but it makes sense in my head)
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
List<Members> ret = db.Members()
if(memberStatus != null || package != null || packageStatus != null || branch != null)
{
ret = db.Members.Where(x =>
(memberStatus == null) ? (true) : (memberStatus == x.MemberStatus)
&& (package == null) ? (true) : (package == x.PackageName)
&& (packageStatus == null) ? (true) : (packageStatus == x.PackageStatus)
&& (branch == null) ? (true) : (branch == x.Branch)
).ToList();
}
return ret;
}
Related
the following is the code and I do not know how to achieve the following using lambda
public void AddClimber(string name, int age, Skill skill)
{
if (GetClimber(name) == null)
{
Climber toAdd = new Climber(name, age, skill);
this.climbers.Add(toAdd);
if (AddedLowLevelClimer != null)
{
if (skill == Skill.LOW) { AddedLowLevelClimer(toAdd); }
}
else if (AddedMediumLevelClimer != null)
{
if (skill == Skill.LOW) { AddedMediumLevelClimer(toAdd); }
}
else if (AddedHighLevelClimer != null)
{
if (skill == Skill.LOW) { AddedHighLevelClimer(toAdd); }
}
else if (AddedProLevelClimer != null)
{
if (skill == Skill.LOW) { AddedProLevelClimer(toAdd); }
}
}
}
You may try to use a null coalescing operator to choose the first non-null handler:
var target = AddedLowLevelClimer
?? AddedMediumLevelClimer
?? AddedHighLevelClimer
?? AddedProLevelClimer;
if (target != null)
{
if (skill == Skill.LOW)
target(toAdd);
}
SCENARIO
Code Jumper is a time-saver extension/plugin for Visual Studio that adds a kind of navigation panel to easily jump between the members of your code with a single click.
For big APIs it's really useful because it avoids scrolling thousands of lines, opening #regions, hidding Xml documentation, searching for a method or member name, etc... its a must-have extension, because It really saves a lot of time:
PROBLEM
Code Jumper has one big problem for my needs, it can't parse Vb.Net Modules, no matter what says in the product description, because the reality is that it can't (its written by a C# guy so maybe he didn't knew the existance of Modules...)
When opening a module, the window of Code jumper is empty, any member is shown.
QUESTION
Here is the downloadable source-code of Code Jumper, written in C#.
What modifications I need to do to make it work with Vb.Net Modules?.
OPTIONAL QUESTION
Someone knows a similar and better extension than Code Jumper?.
RESEARCH
I think this is the most relevant code, in DocumentParser.cs file:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
if (elements == null)
return;
_recurseCount++;
if (_recurseCount % 2 == 0) {
if (ParserProgress != null)
_appConfig.elementView.Dispatcher.BeginInvoke((Action)(() => ParserProgress(this)));
}
foreach (CodeElement element in elements) {
System.Threading.Thread.Sleep(10);
if (element.Kind == vsCMElement.vsCMElementClass) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementNamespace) {
RecurseCodeElements(GetMembers(element), aggregate);
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementFunction ||
element.Kind == vsCMElement.vsCMElementProperty ||
element.Kind == vsCMElement.vsCMElementVariable ||
element.Kind == vsCMElement.vsCMElementDelegate ||
element.Kind == vsCMElement.vsCMElementEvent)
{
var func = element as CodeFunction;
aggregate.Add(element);
}
}
}
private CodeElements GetMembers(CodeElement element) {
if (element == null)
return null;
if (element.Kind == vsCMElement.vsCMElementNamespace) {
return (element as CodeNamespace).Members;
}
if (element.Kind == vsCMElement.vsCMElementClass) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
return (element as CodeInterface).Members;
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
return (element as CodeEnum).Members;
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
return (element as CodeStruct).Members;
}
return null;
}
I tried to add an additional condition for vsCMElement.vsCMElementModule, however, it still don't work, and I 'm not sure whether I'm on the right direction:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
if (elements == null)
return;
_recurseCount++;
if (_recurseCount % 2 == 0) {
if (ParserProgress != null)
_appConfig.elementView.Dispatcher.BeginInvoke((Action)(() => ParserProgress(this)));
}
foreach (CodeElement element in elements) {
System.Threading.Thread.Sleep(10);
if (element.Kind == vsCMElement.vsCMElementClass) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementModule) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementNamespace) {
RecurseCodeElements(GetMembers(element), aggregate);
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementFunction ||
element.Kind == vsCMElement.vsCMElementProperty ||
element.Kind == vsCMElement.vsCMElementVariable ||
element.Kind == vsCMElement.vsCMElementDelegate ||
element.Kind == vsCMElement.vsCMElementEvent)
{
var func = element as CodeFunction;
aggregate.Add(element);
}
}
}
private CodeElements GetMembers(CodeElement element) {
if (element == null)
return null;
if (element.Kind == vsCMElement.vsCMElementNamespace) {
return (element as CodeNamespace).Members;
}
if (element.Kind == vsCMElement.vsCMElementModule) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementClass) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
return (element as CodeInterface).Members;
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
return (element as CodeEnum).Members;
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
return (element as CodeStruct).Members;
}
return null;
}
I can't get it to compile, but if you look at all references for CodeElementType.Class, almost all of them need to be "duplicated" with CodeElementType.Module, especially in IsStackableElement, probably answering your question.
Partial code to explain the required modifications:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
// ...
foreach (CodeElement element in elements) {
// ...
if (element.Kind == vsCMElement.vsCMElementModule) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
// ...
}
}
private CodeElements GetMembers(CodeElement element) {
// ...
if (element.Kind == vsCMElement.vsCMElementModule) {
return (element as CodeClass).Members;
}
// ...
}
public bool IsStackableElement {
get
{
if (IsRoot)
// ...
return (ElementType == CodeElementType.Class || ElementType == CodeElementType.Module || ... )
// ...
}
}
public static CodeElementType GetElementType(CodeElement element) {
switch (element.Kind) {
// ...
case vsCMElement.vsCMElementModule:
return CodeElementType.Module;
// ...
}
}
public void Draw() {
// ...
if (ElementType == CodeElementType.Module) {
_elementStackPanel.Background = _appConfig.ClassBackgroundBrush;
}
// ...
}
public void Update() {
// ...
if (ElementType == CodeElementType.Module)
{
ElementLabel.AddExpander(_elementStackPanel);
ElementLabel.AddPictureToSingleGrid(ElementInfo.ElementImage);
ElementLabel.AddPictureToSingleGrid(ElementInfo.Accessibility.AccessibilityImage);
ElementLabel.AddText(ElementInfo.Name, _appConfig.ClassTextBrush, _appConfig.HyperlinkTextImageSpacer, FontWeights.Bold);
}
// ...
}
private bool GetElementInfo() {
// ...
if (ElementInfo.ElementType == CodeElementType.Module) {
ElementInfo.ElementImage = _appConfig.GetImage("staticclass");
}
// ...
}
private void ParseCodeElement() {
// ...
if (ElementInfo.ElementType == CodeElementType.Module)
{
ElementInfo.IsStackable = true;
var codeClass = (CodeClass)Original;
var codeClass2 = (CodeClass2)Original;
ElementInfo.Accessibility = new AccessibilityContainer(_appConfig, codeClass.Access);
ElementInfo.IsStatic = true;
}
// ...
}
I'm trying to accomplish an expression function alternative
private static Expression<Func<UserProfile, bool>> CompareFilter(FilterViewModel f)
{
...
}
on this one:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
if (filter.FirstName != null)
{
if (profile.FirstName != null)
{
if (profile.FirstName.CompareTo(filter.FirstName) != 0)
{
return false;
}
}
else
{
return false;
}
}
if (filter.TownId != null)
{
if (profile.TownId != filter.TownId)
{
return false;
}
}
// true if at least one of the filter interests match
if (filter.InterestsIds != null)
{
var firstInterestFound = profile.Interests
.Where(i => filter.InterestsIds.Contains(i.Id))
.FirstOrDefault();
if (firstInterestFound == null)
{
return false;
}
}
...
return true;
}
Is there a way to fit this many nested if statements to a lambda expression or something else that will work for the expression function? The idea is the verification to go trough all the if statements and to return true only if all of them are not returning false.
Thanks in advance!
First of all, your current method is so long because you are not making good use of boolean logic. It can be simplified to this:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
if (filter.FirstName != null && filter.FirstName != profile.FirstName)
{
return false;
}
if (filter.TownId != null && filter.TownId != profile.TownId)
{
return false;
}
// true if at least one of the filter interests match
if (filter.InterestsIds != null &&
!profile.Interests.Any(i => filter.InterestsIds.Contains(i.Id)))
{
return false;
}
...
return true;
}
You can turn this into a big hulking expression by inverting all the logic1:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
return (filter.FirstName == null || filter.FirstName == profile.FirstName) &&
(filter.TownId == null || filter.TownId == profile.TownId) &&
(filter.InterestsIds == null ||
profile.Interests.Any(i => filter.Interests.Contains(i.Id)));
// etc. etc.
}
And once you have this, it's a piece of cake to turn it into a lambda and get your Expression<T>:
private static Expression<Func<UserProfile, bool>> CompareFilter(FilterViewModel f)
{
return profile =>
(filter.FirstName == null || filter.FirstName == profile.FirstName) &&
(filter.TownId == null || filter.TownId == profile.TownId) &&
(filter.InterestsIds == null ||
profile.Interests.Any(i => filter.Interests.Contains(i.Id)));
}
Technically, you can just || together all the conditions from the first method and put a big !( ) around it: return !( (...) || (...) || (...));, but inverting everything and joining the conditions with && is a lot nicer.
My data model has a number of tables with a common parent. I am using Entity Framework and have a navigation property from the child object back to the parent, and not the other way. The reason for this is there will be 100's of children of this common parent. The where criteria for every report will be the same, as an example:
crudEngine.Read<ChildEntity>()
.Include(parameters => parameters.ParentEntity)
.Where(parameter => parameter.ParentEntity.LastUser.Contains(searchText) ||
(isInt && SqlFunctions.DatePart("year", parameter.ParentEntity.CreationDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("month", parameter.ParentEntity.CreationDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("day", parameter.ParentEntity.CreationDate) == intSearchText) ||
(isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parameter.ParentEntity.CreationDate), "1900-01-01") == dateSearchText) ||
(isInt && SqlFunctions.DatePart("year", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("month", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("day", parameter.ParentEntity.LastModifiedDate) == intSearchText) ||
(isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parameter.ParentEntity.LastModifiedDate), "1900-01-01") == dateSearchText) ||
parameter.ParentEntity.CreationUser.Contains(searchText) ||
parameter.ParentEntity.Description.Contains(searchText)...
This same criteria will be used for every child entity that references ParentEntity. If possible, I would like to somehow separate this where section into its own function like this:
private Predicate<ParentEntity> GetParentWhere(string searchText)
{
int intSearchText;
bool isInt = int.TryParse(searchText, out intSearchText);
DateTime dateSearchText;
bool isDate = DateTime.TryParse(searchText, out dateSearchText);
SubmissionStatus submissionStatus;
bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);
return parent =>
(isInt && SqlFunctions.DatePart("year", parent.CreationDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("month", parent.CreationDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("day", parent.CreationDate) == intSearchText) ||
(isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parent.CreationDate), "1900-01-01") == dateSearchText) ||
(isInt && SqlFunctions.DatePart("year", parent.LastModifiedDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("month", parent.LastModifiedDate) == intSearchText) ||
(isInt && SqlFunctions.DatePart("day", parent.LastModifiedDate) == intSearchText) ||
(isDate && SqlFunctions.DateAdd("dy", SqlFunctions.DateDiff("dy", "1900-01-01", parent.LastModifiedDate), "1900-01-01") == dateSearchText) ||
parent.CreationUser.Contains(searchText) ||
parent.Description.Contains(searchText) ||
parent.LastUser.Contains(searchText) ||
(isStatus && parent.Status == (int) submissionStatus);
}
Then call this from my code, something like this:
return crudEngine.Read<ChildEntity>().Include(parameters => parameters.ParentEntity)
.Where(GetParentWhere(searchText));
This doesn't work however because the Where expression is typed with the child type, not the parent. Any thoughts on how to accomplish this?
Thanks!
The simplest option would be for your child entities to implement an interface which exposes the ParentEntity property:
public interface IHaveParentEntity
{
ParentEntity ParentEntity { get; }
}
private Expression<Func<TChildEntity, bool>> GetParentWhere<TChildEntity>(string searchText)
where TChildEntity : IHaveParentEntity
{
int intSearchText;
bool isInt = int.TryParse(searchText, out intSearchText);
DateTime dateSearchText;
bool isDate = DateTime.TryParse(searchText, out dateSearchText);
SubmissionStatus submissionStatus;
bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);
return child =>
child.ParentEntity.CreationUser.Contains(searchText) ||
...
;
}
...
return crudEngine.Read<ChildEntity>()
.Include(parameters => parameters.ParentEntity)
.Where(GetParentWhere<ChildEntity>(searchText));
If you can't modify your child entities, you'll probably need to look at building an expression tree. Due to the complexity of your query, it would probably be easier to use expression re-writing rather than building the entire expression by hand:
private Expression<Func<TChildEntity, bool>> GetParentWhere<TChildEntity>(
Expression<Func<TChildEntity, ParentEntity>> parentSelector,
string searchText)
{
int intSearchText;
bool isInt = int.TryParse(searchText, out intSearchText);
DateTime dateSearchText;
bool isDate = DateTime.TryParse(searchText, out dateSearchText);
SubmissionStatus submissionStatus;
bool isStatus = Enum.TryParse(searchText, true, out submissionStatus);
Expression<Func<ParentEntity, bool>> predicate = parent =>
parent.CreationUser.Contains(searchText) ||
...
;
Expression body = ReplacementVisitor.Replace(
predicate,
predicate.Parameters[0],
parentSelector.Body);
return Expression.Lambda<Func<TChildEntity, bool>>(body,
parentSelector.Parameters[0]);
}
private sealed class ReplacementVisitor : ExpressionVisitor
{
private IList<ParameterExpression> SourceParameters { get; set; }
private Expression ToFind { get; set; }
private Expression ToReplace { get; set; }
public static Expression Replace(
LambdaExpression source,
Expression toFind,
Expression toReplace)
{
var visitor = new ReplacementVisitor
{
SourceParameters = source.Parameters,
ToFind = toFind,
ToReplace = toReplace,
};
return visitor.Visit(source.Body);
}
private Expression ReplaceNode(Expression node)
{
return (node == ToFind) ? ToReplace : node;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (SourceParameters.Contains(node)) return ReplaceNode(node);
return SourceParameters.FirstOrDefault(p => p.Name == node.Name) ?? node;
}
}
...
return crudEngine.Read<ChildEntity>()
.Include(parameters => parameters.ParentEntity)
.Where(GetParentWhere<ChildEntity>(child => child.ParentEntity, searchText));
My action is :
[HttpPost]
public ViewResult SearchPost(FormCollection frm)
{
IList <post> p =db.posts.Include("user").ToList();
if (Request.Form["area"] != null)
{
if ((p!=null) && (p.Any()))
{
p =p.Where(a=>a.area==Request.Form["area"]).ToList();
}
}
if (Request.Form["floor"] != null)
{
if ((p!=null) && (p.Any()))
{
p = p.Where(a => a.floor ==
Request.Form["floor"]).ToList();
}
}
if (Request.Form["garage"] != null)
{
if ((p!=null) && (p.Any()))
{
p = p.Where(a => a.garage ==
Request.Form["garage"]).ToList();
}
}
return View(p);
}
it has shown no errors. but always return null. it should return filtered post objects or simply all posts without filtering. is there any problem ?? i can't find it .
I don't know the full signatures of the types that you are dealing with, but perhaps try this code and see if you get any better results:
[HttpPost]
public ViewResult SearchPost(FormCollection frm)
{
var area = Request.Form["area"];
var floor = Request.Form["floor"];
var garage = Request.Form["garage"];
return View(db.posts.Include("user")
.Where(a => area == null || a.area == area)
.Where(a => floor == null || a.floor == floor)
.Where(a => garage == null || a.garage == garage).ToList());
}
(This is essentially a rewrite of your code down into a single query that might help you with debugging.)