I'm starting my journey with .NET and I need a little help.
I will describe my situation by example what I have and what I need to do but I don't know how to do that.
So I have a class like this
public class Ban
{
public int ID { get; set; }
public string Nick { get; set; }
public string IP { get; set; }
public string GroupName { get; set; }
}
and variable bans which is IQueryable
Then in method of signature
public IEnumerable<Ban> FindBans(Ban filter);
I need to search through that bans variable;
How I search now
public IEnumerable<Ban> FindBans(Ban filter)
{
var bans = GetBansQueryable();
if (!string.IsNullOrWhiteSpace(filter.GroupName))
{
bans = bans.Where(b => b.GroupName == filter.GroupName);
}
if (!string.IsNullOrWhiteSpace(filter.Nick))
{
bans = bans.Where(b => b.Nick == filter.Nick);
}
if (!string.IsNullOrWhiteSpace(filter.IP))
{
bans = bans.Where(b => b.IP == filter.IP);
}
return bans.AsEnumerable();
}
Which filters with AND. SQL query part will be like this
... WHERE group_name = 'abc' AND nick = 'def' AND ip = 'ghi';
What I need is
... WHERE group_name = 'abc' AND (nick = 'def' OR ip = 'ghi');
All of this need to be dynamic (if we don't pass GroupName don't filter by it etc.)
I have no idea how I can achieve that beside making this dynamics manualy like
if (!string.IsNullOrWhiteSpace(filter.GroupName) &&
string.IsNullOrWhiteSpace(filter.Nick) &&
string.IsNullOrWhiteSpace(filter.IP))
{
bans = bans.Where(b => b.GroupName == filter.GroupName);
}
else if (!string.IsNullOrWhiteSpace(filter.GroupName) &&
!string.IsNullOrWhiteSpace(filter.Nick) &&
string.IsNullOrWhiteSpace(filter.IP))
{
bans = bans.Where(b => b.GroupName == filter.GroupName && b.Nick == filter.Nick);
}
else if (!string.IsNullOrWhiteSpace(filter.GroupName) &&
!string.IsNullOrWhiteSpace(filter.Nick) &&
!string.IsNullOrWhiteSpace(filter.IP))
{
bans = bans.Where(b => b.GroupName == filter.GroupName && (b.Nick == filter.Nick || b.IP == filter.IP));
}
and so on... and now add another variable to Ban.
I think you can simplify you entire constraint like this:
bans = bans.Where(b => ( string.IsNullOrWhiteSpace(filter.GroupName) || b.GroupName == filter.GroupName )
&&
( ( string.IsNullOrWhiteSpace(filter.Nick) || b.Nick == filter.Nick )
||
( string.IsNullOrWhiteSpace(filter.IP) || b.IP == filter.IP )
)
);
You probably would like to look at Scott Hansleman blog post on dynamic sql, predicate builder and linqkit:
The Weekly Source Code 48 - DynamicQueryable makes custom LINQ expressions easier
Otherwise there is a very nice blog post about using dynamic filter with Kendo UI grid and Web Api:
Kendo UI Open Sources Dynamic LINQ Helpers
You could special-case the situation where both nick and ip are known:
public IEnumerable<Ban> FindBans(Ban filter)
{
var bans = GetBansQueryable();
if (!string.IsNullOrWhiteSpace(filter.GroupName))
{
bans = bans.Where(b => b.GroupName == filter.GroupName);
}
if (!string.IsNullOrWhiteSpace(filter.Nick) && !string.IsNullOrWhiteSpace(filter.IP))
{
bans = bans.Where(b => b.Nick == filter.Nick || b.IP == filter.IP);
}
else if (!string.IsNullOrWhiteSpace(filter.Nick))
{
// filter.IP is empty
bans = bans.Where(b => b.Nick == filter.Nick);
}
else if (!string.IsNullOrWhiteSpace(filter.IP))
{
// filter.Nick is empty
bans = bans.Where(b => b.IP == filter.IP);
}
return bans.AsEnumerable();
}
Related
I trying to get data from datatabase and assign it to list
Here is 2 conditions. 1 - I have only categoryId and 2 - I have category and subcategoryId
I wrote code like this
public async Task<List<TimelineItemDto>> GetFilteredClasses(int? categoryId, int? subcategoryId)
{
List<TimelineItemDto> filteredClasses;
if (categoryId != null)
{
filteredClasses = await _context.Events
.Where(x => x.CategoryId == categoryId && x.EventType == EntityType.Class)
.ProjectTo<TimelineItemDto>()
.ToListAsync();
}
if (categoryId != null && subcategoryId != null)
{
filteredClasses = await _context.Events
.Where(x => x.CategoryId == categoryId && x.SubcategoryId == subcategoryId &&
x.EventType == EntityType.Class)
.ProjectTo<TimelineItemDto>()
.ToListAsync();
}
else
{
filteredClasses = await _context.Events.Where(x =>
x.EventType == EntityType.Class).ProjectTo<TimelineItemDto>()
.ToListAsync();
}
return filteredClasses;
}
but at first if I got this
A second if and at else all okay and it's executed and filling list
How I can solve this?
Well I would do something like below :
public async Task<List<TimelineItemDto>> GetFilteredClasses(int? categoryId, int? subcategoryId)
{
var filteredClasses = _context.Events.Where(x => x.EventType == EntityType.Class);
if (categoryId != null)
{
filteredClasses = filteredClasses.
.Where(x => x.CategoryId == categoryId);
}
if (categoryId != null && subcategoryId != null)
{
filteredClasses = filteredClasses.Where(x => x.SubcategoryId == subcategoryId );
}
return await filteredClasses.ProjectTo<TimelineItemDto>()
.ToListAsync();;
}
This way you will avoid materializing multiple queries.
You should update the condition flow as below:
if (categoryId != null && subcategoryId != null)
{
...
}
else if (categoryId != null)
{
...
}
else
{
...
}
With above, the filteredClasses will not be overridden by last else condition. Your current code first evaluate if and then if & else. Both are different code blocks and last else is always getting executed.
I currently have the following method:
public List<Order> GetOrders(int profileId, string timeSpan, string workOrd, string partNo, bool includeDeleted)
{
DateTime startDate = DateTime.Now;
DateTime endDate = DateTime.Now;
string[] times = (!string.IsNullOrWhiteSpace(timeSpan)) ? timeSpan.Trim().Split('-') : new string[] { "", "" };
if (!string.IsNullOrWhiteSpace(times[0]) && !string.IsNullOrWhiteSpace(times[0]))
{
startDate = DateTime.Parse(times[0]).Date;
endDate = DateTime.Parse(times[1]).Date;
}
//New Real Query
IQueryable<Order_Travel> otQuery = _context.Order_Travels.Where(x =>
(profileId != 0 || x.Profile.ProfileID == profileId)
&& ((timeSpan == null || timeSpan.Trim() == "") || ((DbFunctions.TruncateTime(x.TimeRecieved) >= startDate)
&& (DbFunctions.TruncateTime(x.TimeRecieved) <= endDate)))
&& ((workOrd == null || workOrd.Trim() == "") || x.Order.WorkOrdNo == workOrd)
&& ((partNo == null ||partNo.Trim() == "") || x.Order.PartNo == partNo)
&& (!includeDeleted || x.Aborted == true));
//The results is now in order_travel. Under here binding them to a list of orders with only the respective orderTravels included.
List<Order> orders = new List<Order>();
List<Order_Travel> ots = otQuery.ToList();
foreach (Order_Travel ot in ots)
{
var OrderInList = orders.FirstOrDefault(X => X == ot.Order);
if (OrderInList == null)
{
orders.Add(ot.Order);
OrderInList = orders.FirstOrDefault(X => X == ot.Order);
OrderInList.OrderTravels.Clear();
OrderInList.OrderTravels.Add(ot);
}
else
{
OrderInList.OrderTravels.Add(ot);
}
}
return orders;
}
What I need it to do, is (as I've attempted) to make a call, finding all Order_Travel objects that match the paramters sent to it. If some (or all) are left blank, it takes everything, regardless of the values.
The code right now, does not return anything, if a blank search is made (a search that does not have any parameters), and I can not see what could be the issue. I have tried debugging it, but with no luck.
Any help would be greatly appreciated!
Thanks!
Filter one option at a time, instead of trying to put everything into a single expression:
IQueryable<T> query = all; // start with everything
if (IsPresent(option1))
{
query = query.Where(t => t.XXX == option1);
}
Example
IQueryable<Order_Travel> otQuery = _context.Order_Travels;
if (profileId != 0)
{
otQuery = otQuery.Where(x => x.Profile.ProfileID == profileId);
}
if (timeSpan != null && timeSpan.Trim() != "")
{
otQuery = otQuery.Where(x => DbFunctions.TruncateTime(x.TimeRecieved) >= startDate &&
DbFunctions.TruncateTime(x.TimeRecieved) <= endDate);
}
You will also find this easier to maintain than one huge expression.
Probably this part is your problem:
(profileId != 0 || x.Profile.ProfileID == profileId)
It should be
(profileId == 0 || x.Profile.ProfileID == profileId)
If your profile ID is 0, it will only find entries with x.Profile.ProfileID being 0. Probably there are no such entries.
I have a list of Object Product :
public Class Product
{
public int ID { get; set; }
public string Title { get; set; }
public string Status { get; set; }
}
I have a list of the above product :
List<Product> p ;
I am trying to filter this List , based on Search Criteria ID, Title, Status.
Example :
Id Title Status
1 ABC OPEN
2 CDE CLOSED
3 FGH RESOLVED
4 IJK PROPOSED
5 LMN SET
6 OPQ CLOSED
7 MNO OPEN
8 STU CLOSED.
If Search Fields ID 1 is entered : It should return
1 ABC OPEN
If search Fields Status OPEN is entered : It should return
1 ABC OPEN
7 MNO OPEN
If Search Fields Title "MNO" and Status "OPEN" are entered it should return :
7 MNO OPEN
If Id = "" and Title = "" and Status = "ALL" it should return all the elements.
I am going to bind this List of objects to an asp.net grid view.
So far the code I have is below :
var results = new List<Product>();
foreach (var prod in Product)
{
if (prod.ID == txtProd)
{
results.Add(prod);
}
if (prod.Title.ToUpper() == txtTitle)
{
results.Add(prod);
}
if (prod.Status.ToUpper() == strStatus)
{
results.Add(prod);
}
}
Can you please tell me how I can modify this code to achieve my results ?
Basically you want to use AND between your conditions, what you are currently doing is an OR, and you will add the same item twice when it satisfies more than one condition.
You could built a dynamic linq query based on parameters:
var query = Product.AsQueryable();
query = txtProd != string.Empty ? query.Where(x => x.ID == txtProd) : query;
query = txtTitle != string.Empty ? query.Where(x => x.Title == txtTitle) : query;
query = strStatus != string.Empty ? query.Where(x => x.Status == strStatus) : query;
var results = query.ToList();
As a single Linq query it would look like this:
var results = p.Where(x =>
(string.IsNullOrEmpty(txtProd) || x.ID == Convert.ToInt32(txtProd))
&& (string.IsNullOrEmpty(txtTitle) || x.Title == txtTitle)
&& (string.IsNullOrEmpty(strStatus) || x.Status == strStatus)).ToList();
By the way, in the question the List results is of type Task and you try to add Products, this is not possible.
Try this:
results = p.Where(((product.ID < yourIDLowerBound) || (product.ID == yourID))
&& (string.IsNullOrEmpty(product.yourTitle) || product.Title == yourTitle)
&& (string.IsNullOrEmpty(product.yourStatus) || product.Status == yourStatus)).ToList();
var results = new List < Product > ();
if (strStatus == "ALL" || txtProd == "" || txtTitle == "")
results = p;
else
foreach(var prod in p) {
if (prod.ID == Int32.Parse(txtProd)) {
results.Add(prod);
}
if (prod.Title.ToUpper() == txtTitle) {
results.Add(prod);
}
if (prod.Status.ToUpper() == strStatus) {
results.Add(prod);
}
}
results = results.Distinct()
.ToList();
How about creating a product query method. I think this meets all of requirements, I don't think any other covered the "ALL" requirement.
public List<Product> ProductQuery(List<Product> Products
, int? IDCriteria
, string TitleCriteria
, string StatusCriteria)
{
var query = Products.AsQueryable();
if (IDCriteria != null)
{
query.Where(x => x.ID == IDCriteria);
}
if (!String.IsNullOrEmpty(TitleCriteria))
{
query.Where(x => x.Title == TitleCriteria);
}
if (!String.IsNullOrEmpty(StatusCriteria))
{
if (StatusCriteria.Equals("all", StringComparison.CurrentCultureIgnoreCase))
{
query.Where(x => x.Status.Length > 0 || String.IsNullOrEmpty(x.Status)); //will return any status
}
else
{
query.Where(x => x.Status == StatusCriteria);
}
}
return query.ToList();
}
I want to refactor a method but I'm not too sure how to but I know you can do it.
My current method:
public bool AdminShutdown(int accountID, int accountPin)
{
var status = false;
if (accountID == AdminLogin && accountPin == AdminPin)
{
status = true;
}
return status;
}
I think it should be something like
var status = (accountID == AdminLogin) && (accountPin == AdminPin) but that doesn't work ( Operator '&&' cannot be applied to operands of type 'bool' and 'int').
Suggestions?
P.S. Would this code work?
var tempReturnPerson = AccountHoldersList.Single((x => x.AccountNumber == accountId));
instead of:
public AccountHolders ReturnAccountInfo(int accountId)
{
//use linq to do this later
var returnPerson = new AccountHolders();
foreach (var person in AccountHoldersList)
{
if (accountId == person.AccountNumber)
{
returnPerson = person;
break;
}
}
return returnPerson;
}
if AdminLogin and AdminPin are int, than
public bool AdminShutdown(int accountID, int accountPin)
{
return (accountID == AdminLogin && accountPin == AdminPin);
}
The error message you get make me think you may have used an = instead of an ==
EDIT
For your second question
var tempReturnPerson = AccountHoldersList.Single((x => x.AccountNumber == accountId));
should rather be
var tempReturnPerson = AccountHoldersList.FirstOrDefault(x => x.AccountNumber == accountId) ?? new AccountHolders();
If you want the same result as your method.
"(accountID == AdminLogin && accountPin == AdminPin)" can simply be evaluated to a bool
public bool AdminShutdown(int accountID, int accountPin)
{
return (accountID == AdminLogin && accountPin == AdminPin)
}
I'm trying to structure my code and be able to better maintain some of my LINQ queries. Actually I created a new helper class with some functions but I'm having some problems executing some of my IQueryable functions inside another IQueryable functions.
When I execute the function SupplierMappings.GetSupplierAssociatedRT(int supplierID) I'm getting the following error:
Method 'System.Linq.IQueryable`1[VMPortal.DataAccessLayer.CostCentre]
GetSupplierAssociatedCCPerRT(Int32, Int32)' has no supported translation to SQL.
What I'm doing wrong? Below is the part of related code:
public class SupplierMappings
{
private static DataLayer dl = DataLayer.GetDataContext();
public static IQueryable<ResourceType> GetSupplierAvailableRT(int supplierID)
{
return dl.ResourceTypes.Where(x =>
dl.SuppliersCompanies2.Any(y => y.SupplierID == supplierID
&& y.ResourceTypeID == x.ResourceTypeID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active));
}
public static IQueryable<ResourceType> GetSupplierAssociatedRT(int supplierID)
{
return GetSupplierAvailableRT(supplierID).Where(x =>
// Check if we have at least one CC associated with that RT
GetSupplierAssociatedCCPerRT(supplierID, x.ResourceTypeID).Count() >= 1);
}
public static IQueryable<CostCentre> GetSupplierAvailableCCPerRT(int supplierID, int rtID)
{
return dl.CostCentres.Where(x => x.StatusID == (short)CostCentersStatusEnum.Active
// Check than the supplier is mapped at supplier level at same company & RT
&& dl.SuppliersCompanies2.Any(y => y.CompanyID == x.CompanyID
&& y.SupplierID == supplierID
&& y.ResourceTypeID == rtID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active)
// Check than the PA is active
&& x.DeliveryGroup.StatusID == (short)DeliveryGroupsStatusEnum.Active);
}
public static IQueryable<CostCentre> GetSupplierAssociatedCCPerRT(int supplierID, int rtID)
{
return GetSupplierAvailableCCPerRT(supplierID, rtID).Where(x =>
dl.SuppliersCostCentre2.Count(y => y.SupplierID == supplierID
&& y.StatusID == (short)SuppliersCostCentreStatusEnum.Inactive
&& y.ResourceTypeID == rtID) != dl.SuppliersCompanies2.Count(y => y.SupplierID == supplierID
&& x.CompanyID == y.CompanyID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active
&& y.ResourceTypeID == rtID)
&& dl.SuppliersPracticeAreas.Count(y => y.SupplierID == supplierID
&& y.StatusID == (short)SuppliersPracticeAreaStatusEnum.Inactive
&& y.ResourceTypeID == rtID) != dl.SuppliersCompanies2.Count(y => y.SupplierID == supplierID
&& x.CompanyID == y.CompanyID
&& y.StatusID == (short)SuppliersCompaniesStatusEnum.Active
&& y.ResourceTypeID == rtID));
}
}
It is pity but in linq to sql you cannot use you defined methods that returns anything except expressions. And there is limited number of expressions that can be translated to sql query (this translations is defined in linqtosql library). So you cannot do what you try to do.
You could try to inline your functions.