MVC Linq Query with dynamic column name in WHERE clause - c#

I have a number of calls for the same query but with slightly different WHERE clause, does anyone know if it's possible to pass a variable as the column name as I can't seem to acheive it.
I know the below isn't correct but just to give you an idea of what i'm trying to acheive.
public EmailUserListViewModel EmailUserListData(int CaseId, string ColumnName)
{
CaseId = CaseId,
EmailUserList = (from u in efContext.PgsUsers
where ColumnName == true
orderby u.FirstName, u.LastName
select new EmailUserListModel
{
UserId = u.Id,
Name = ((u.FirstName == null) ? "" : u.FirstName)
+ ((u.LastName == null) ? "" : " " + u.LastName),
Email = u.Email,
Checked = false
}).ToList()
};
}

I suppose you could use Reflection to dynamically retrieve the value of the property
from u in efContext.PgsUsers where (typeof(PgsUser).GetProperty(ColumnName).GetValue(u) as bool) == true
or
from u in efContext.PgsUsers where (u.GetType().GetProperty(ColumnName).GetValue(u) as bool) == true

You could write such method:
public Expression<Func<T, bool>> getExpression<T>(string columnName)
{
var param = Expression.Parameter(typeof(T));
var equal = Expression.Equal(Expression.Property(param, columnName), Expression.Constant(true));
return (Expression<Func<T, bool>>)Expression.Lambda(equal, param);
}
and use it in where:
where getExpression<YourType>("ColumnName")

Related

How to set dynamic column in linq query

I have following code,
public List<MemberDto> GetMembers(out int rowCount,int pageIndex,int pageSize, string seachBy = "", string searchTerm = "", string sortBy = "", string sortDiection = "")
{
var members = (from m in context.Members
where (string.IsNullOrEmpty(searchTerm) || m.MemberNumber.Equals(searchTerm))
|| (string.IsNullOrEmpty(searchTerm) || m.LastName.Equals(searchTerm))
select m).AsEnumerable();
if (!string.IsNullOrEmpty(sortBy))
{
PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(EFModel.ClientData.Member)).Find(sortBy, true);
members = (sortDiection.ToLower() == "descnding") ? members.OrderByDescending(x => prop.GetValue(x)).ToList() : members.OrderBy(x => prop.GetValue(x)).ToList();
}
rowCount = (!string.IsNullOrEmpty(searchTerm)) ? members.Count() : rowCount = context.Members.Count() ;
members = members.Skip(pageIndex).Take(pageSize).ToList();
List<MemberDto> memberDtos = new List<MemberDto>();
mapper.Map(members, memberDtos);
return memberDtos;
}
In the above method string seachColumn value can be memberno or lastname or sometime empty. when seachColumn value is memberno. I only need to search searchTerm value in MemberNumber column.
when seachColumn value is lastname. I only need to search searchTerm value in LastName column.
sometimes searchTerm can be empty. when it happen I need all the records without considering any search terms.
Above query is working bit. the problem is that when there is a value for searchTerm, That result is given regardless of the column. How can i do this.
public List<MemberDto> GetMembers(out int rowCount,int pageIndex,int pageSize, string seachColumn = "", string searchTerm = "", string sortBy = "", string sortDiection = "")
{
var query = context.Members;
if(string.IsNullOrWhitespace(searchTerm)
{
return query.ToList();
}
//if you want to check strings equality in ignore-case mode, you should change your db collation using the link below.
return query
.Where(m => m.MemberNumber.Equals(searchTerm) || m.LastName.Equals(searchTerm))
.ToList();
}
String equality check in ef using db collation

Linq to Sql - return all data if 'contains' value is null in where clause

I need to get all vendorname if value(data.vendorname) is null its give an error Exception: "Value cannot be null."
public HttpResponseMessage PostFilter([FromBody] dynamic data)
{
string[] vendorname = data.vendorname != null
? data.vendorname.ToObject<string[]>()
: null;
var items = (from s in context.AllInventories
where
(vendorname!=null
? vendorname.Contains(s.VENDORNAME)
:1==1)
select s)
.Take(500)
.ToList();
}
Why don't you simplify this, by just not applying any where at all if vendorname is null?
public HttpResponseMessage PostFilter([FromBody] dynamic data)
{
string[] vendorname = data.vendorname != null
? data.vendorname.ToObject<string[]>()
: null;
var query = context.AllInventories.AsQueryable();
if (vendorname != null)
{
query = query.Where(s => vendorname.Contains(s.VENDORNAME));
}
var items = query.Take(500).ToList();
}
Why don't you use a simple if-statement?
IEnumerable<Inventory> inventories = context.AllInventories;
if(vendorname != null)
inventories = inventories.Where(i => vendorname.Contains(i.VENDORNAME));
inventories = inventories.Take(500).ToList();
That's much better than hoping that your sql trick works and that the optimizer is clever enough to ignore your pseudo-condition. It's also better to debug.

how to use this function in linq

Given we cannot use a function in this scenario, what could be good way of incorporation GetPreferredName function within Linq query?
List<Employee> Employees = (from d in context.Employees
join a in context.Address on d.ID equals a.EmployeeID
select new Employee
{
Id = d.Id,
PreferredName = GetPreferredName(d.FirstName, d.MiddleName, d.LastName, d.Alias),
StreetAddress = a.StreetAddress
}).ToList();
private string GetPreferredName(string firstName, string middleName, string lastName, string dnsName)
{
if (!string.IsNullOrEmpty(firstName))
return firstName;
else if (!string.IsNullOrEmpty(middleName))
return middleName;
else if (!string.IsNullOrEmpty(lastName))
return lastName;
else if (!string.IsNullOrEmpty(dnsName))
return dnsName;
return "";
}
What you can do is enumerate from the database first, then do a new select to run your extra code:
var Employees =
(from d in context.Employees
join a in context.Address on d.ID equals a.EmployeeID
select new //select the important bits we'll need in memory
{
Employee = d,
Address = a,
})
.AsEnumerable() //AsEnumerable() it to make it enumerate from the database, now everything you need is in memory
.Select(x => new Employee
{
Id = x.Employee.Id,
PreferredName = GetPreferredName(x.Employee.FirstName, x.Employee.MiddleName, x.Employee.LastName, x.Employee.Alias),
StreetAddress = x.Address.StreetAddress
})
.ToList();
Also, with linq, you don't need to do these joins manually, you could change it to this:
var Employees = context.Employees.Select(e =>
select new //select the important bits we'll need in memory
{
Employee = e,
Address = e.Address, //join is done for you!
})
.AsEnumerable() //AsEnumerable() it to make it enumerate from the database, now everything you need is in memory
.Select(x => new Employee
{
Id = x.Employee.Id,
PreferredName = GetPreferredName(x.Employee.FirstName, x.Employee.MiddleName, x.Employee.LastName, x.Employee.Alias),
StreetAddress = x.Address.StreetAddress
})
.ToList();
You can use ?? null-coalescing operator.
PreferredName = d.FirstName ?? d.MiddleName ?? d.LastName ?? d.Alias
Or ?: conditional operator
PreferredName = (d.FirstName != null && d.FirstName != "") ? d.FirstName :
((d.MiddleName != null && d.MiddleName != "") ? d.MiddleName :
((d.LastName != null && d.LastName != "") ? d.LastName : d.Alias))

Nested LINQ Method throwing a `Not Supported...` Exception

This is a follow up from here -->multiple-sorting-on-linq-nested-method .
Basically, on let memberName = ... it is throwing this exception Method 'System.String MemberName(Int32)' has no supported translation to SQL. and I am not figuring out the solution.
Also, BLLCmo and BLLConnect actually use TWO different DataBases. The original app(not mine) uses 4 Seperate DB's so I am trying to make due.
BLLCmo.cs
public static DataTable GetAllMembers(Guid workerID)
{
var AllEnrollees = from enrollment in context.tblCMOEnrollments
where enrollment.CMOSocialWorkerID == workerID || enrollment.CMONurseID == workerID
join supportWorker in context.tblSupportWorkers on enrollment.EconomicSupportWorkerID equals supportWorker.SupportWorkerID into workerGroup
from worker in workerGroup.DefaultIfEmpty()
let memberName = BLLConnect.MemberName(enrollment.ClientID)
orderby enrollment.DisenrollmentDate ascending, memberName ascending
select new
{
enrollment.ClientID,
MemberName = memberName,
NurseName = BLLAspnetdb.NurseName(enrollment.CMONurseID),
SocialWorkerName =BLLAspnetdb.SocialWorkerName(enrollment.CMOSocialWorkerID),
enrollment.DisenrollmentDate,
enrollment.EnrollmentDate,
ESFirstName = worker.FirstName,
ESLastName = worker.LastName,
ESPhone = worker.Phone
};
var dataTable = AllEnrollees.CopyLinqToDataTable();
return dataTable;
}
BLLConnect.cs
public static String MemberName(Int32 personID)
{
var memberName = from person in context.tblPersons
where person.PersonID == personID
select person.FirstName + " " + person.LastName;
return memberName.SingleOrDefault();
}
The problem is that LINQ to SQL is trying to translate your method into SQL. Since MemberName isn't valid SQL, it gives up. Instead, you'll need to pull down the data you need from SQL and then call your methods (and sort) in a separate LINQ to Objects query:
public static DataTable GetAllMembers(Guid workerID)
{
var AllEnrollees =
from enrollment in context.tblCMOEnrollments
where enrollment.CMOSocialWorkerID == workerID || enrollment.CMONurseID == workerID
join supportWorker in context.tblSupportWorkers on enrollment.EconomicSupportWorkerID equals supportWorker.SupportWorkerID into workerGroup
from worker in workerGroup.DefaultIfEmpty()
select new
{
enrollment.ClientID,
enrollment.CMONurseID,
enrollment.CMOSocialWorkerID,
enrollment.DisenrollmentDate,
enrollment.EnrollmentDate,
ESFirstName = worker.FirstName,
ESLastName = worker.LastName,
ESPhone = worker.Phone
};
var result =
from enrollee in AllEnrollees.AsEnumerable()
let memberName = BLLConnect.MemberName(enrollee.ClientID)
orderby enrollee.DisenrollmentDate ascending, memberName ascending
select new
{
enrollee.ClientID,
MemberName = memberName,
NurseName = BLLAspnetdb.NurseName(enrollee.CMONurseID),
SocialWorkerName = BLLAspnetdb.SocialWorkerName(enrollee.CMOSocialWorkerID),
enrollee.DisenrollmentDate,
enrollee.EnrollmentDate,
enrollee.ESFirstName,
enrollee.ESLastName,
enrollee.ESPhone
};
return result.CopyLinqToDataTable();
}

Trouble understanding LINQ return value

I am trying to return the Actual Value in this query but I only get the Expression. Can someone point me in the right direction please.
public static String NurseName(Guid? userID)
{
var nurseName = from demographic in context.tblDemographics
where demographic.UserID == userID
select new {FullName = demographic.FirstName +" " + demographic.LastName};
String nurseFullName = nurseName.ToString();
return nurseFullName;
}
nurseFullName ends up as --> SELECT ([t0].[FirstName] + #p1) + [t0].[LastName] AS [FullName]
FROM [dbo].[tblDemographics] AS [t0]
WHERE ([t0].[UserID]) = #p0
public static String NurseName(Guid? userID)
{
var nurseName = from demographic in context.tblDemographics
where demographic.UserID == userID
select demographic.FirstName +" " + demographic.LastName;
return nurseName.SingleOrDefault();
}
In the above nurseName has the IQueryable expression, so it hasn't executed anything. Its when you enumerate on it, that the query is called. That's also the case when you call methods like SingleOrDefault.
If you were to use your original query expression instead, you can:
public static String NurseName(Guid? userID)
{
var query= from demographic in context.tblDemographics
where demographic.UserID == userID
select new {FullName = demographic.FirstName +" " + demographic.LastName; }
var nurseName = query.SingleOrDefault();
return nurseName != null ? nurseName.FullName : "";
}
Difference between Single vs. SingleOrDefault is that the later allows an empty result.
Difference between First vs. Single is that the later prevents more than 1 row.
Try this:
var nurseName = (from demographic in context.tblDemographics
where demographic.UserID == userID
select new {FullName = demographic.FirstName +" " + demographic.LastName}).First().FullName;
Your code was grabbing a collection, but not a specific item. This modification takes the first item in the list and returns the FullName property.
public static String NurseName(Guid? userID)
{
var nurseName = (from demographic in context.tblDemographics
where demographic.UserID == userID
select new {FullName = demographic.FirstName +" " + demographic.LastName}).SingleOrDefault();
if(null == nurseName)
{
//TODO: Uh-Oh ID not found...
}
string nurseFullName = nurseName.FullName;
return nurseFullName;
}

Categories