Getting counts of two items of same column in single table - c#

I am developing a site, which has both desktop and mobile customers. for an analysis, i want to know whether user accessing my site through desktop or mobile device, for that i am having one table in database which has several columns for storing user's device details. In that i have one column called IsMobile, in that i am storing true/false based on user's device. Now i want to get the count of true and false using linq query.
I am using below code.
public IList<IsMobile> IsMobile(DateTime fromDate, DateTime toDate)
{
var isMobile = (from d in _db.UserDeviceDetail
where ((d.CreatedOn.Month >= fromDate.Month && d.CreatedOn.Day >= fromDate.Day && d.CreatedOn.Year >= fromDate.Year) || (d.CreatedOn.Month <= toDate.Month && d.CreatedOn.Day <= toDate.Day && d.CreatedOn.Year <= toDate.Year))
group d by d.IsMobile into g
select new IsMobile
{
Yes = g.Count(n => n.IsMobile == true),
No = g.Count(n => n.IsMobile == false)
}).ToList();
return isMobile;
}
I am getting proper count details, but with two list item one for counting Yes and another for counting No, instead i want to get single list item, by counting both yes and no at a time
As it is:
{Yes 20; No 0}
{Yes 0; No 10}
How should be:
{Yes 20; No 10}
I am new to linq query, please tell what i am doing wrong?

Grouping is what makes LINQ to create two items instead of one.
It separates whole set of items into certain number of sub-sets each processed separately.
Your query without grouping should do the trick.
UPD: following query should return list with one IsMobile item. It can be simplified if just one item can be returned instead of a list:
var isMobile = (from d in _db.UserDeviceDetail
where ((d.CreatedOn.Month >= fromDate.Month && d.CreatedOn.Day >= fromDate.Day && d.CreatedOn.Year >= fromDate.Year) || (d.CreatedOn.Month <= toDate.Month && d.CreatedOn.Day <= toDate.Day && d.CreatedOn.Year <= toDate.Year))
select d).ToList();
return new List<IsMobile>(){
new IsMobile{
Yes = isMobile.Count(n => n.IsMobile == true),
No = isMobile.Count(n => n.IsMobile == false)}
};

Why do you want to select a IList<IsMobile> at all when you just want to know how many items have IsMobile == true and how many have IsMobile == false?
I would return a Tuple<int, int> instead:
public Tuple<int, int> mobileCounts(DateTime fromDate, DateTime toDate)
{
var inTime = _db.UserDeviceDetail.Where(d=> (d.CreatedOn.Month >= fromDate.Month && d.CreatedOn.Day >= fromDate.Day && d.CreatedOn.Year >= fromDate.Year) || (d.CreatedOn.Month <= toDate.Month && d.CreatedOn.Day <= toDate.Day && d.CreatedOn.Year <= toDate.Year));
int isMobileCount = inTime.Count(d => d.IsMobile);
int isNotMobileCount = inTime.Count(d => !d.IsMobile);
return Tuple.Create(isMobileCount,isNotMobileCount);
}
You can access both informations via Item1 and Item2 property of the tuple.

Related

Linq where clause confusion

Good day, everyone!
I've written one query for my Automation test, but it's taking too long to execute, and I'm not sure how to optimize it effectively because I'm new to the Linq where clause.
Could someone please assist me with this?
var order = OrderRepositoryX.GetOrderByStatus(OrderStatusType.Dispatched, 4000)
.Where(x => x.siteId == 1 || x.siteId == 10 || x.siteId == 8 || x.siteId == 16 || x.siteId == 26 || x.siteId == 27)
.Where(x =>
{
var totalPrice = OrderRepository.GetOrderById(shared_parameters.testConfiguration, x.orderId).TotalPrice;
if (totalPrice < 500)
return false;
return true;
})
.Where(x =>
{
var cnt = ReturnOrderRepositoryX.CheckReturnOrderExists(x.orderId);
if (cnt > 0)
return false;
return true;
})
.Where(x =>
{
var cnt = OrderRepositoryX.CheckActiveOrderJobDetailsByOrderId(x.orderId);
if (cnt > 0)
return false;
return true;
})
.FirstOrDefault();
The biggest code smell here is that you are calling other repositories inside the Where clause which (assuming that repositories actually hit database) it will effectively mean that you are hitting database per every queried item. Lets imagine that OrderRepositoryX.GetOrderByStatus(OrderStatusType.Dispatched, 4000) and first Where will result in 1000 items, only second Whereclause will lead to 1000 queries to the database (and you have some more calls to repositories in subsequent Wheres). And all of this to get just one item (i.e. FirstOrDefault).
Usual approach is to avoid calling database in loops (what Where basically does here) and rewrite such code so only single SQL query will be performed against the database returning only what is needed and performing all the filtering on the database side.
Please try this instead
Avoid too many where clauses. It gets a result and then applies another check on the whole set.
var order = OrderRepositoryX.GetOrderByStatus(OrderStatusType.Dispatched, 4000)
.FirstOrDefault(x => x.siteId == 1 || x.siteId == 10 || x.siteId == 8 || x.siteId == 16 ||
x.siteId == 26 || x.siteId == 27) &&
(x =>
{
var totalPrice = OrderRepository.GetOrderById(shared_parameters.testConfiguration, x.orderId)
.TotalPrice;
return totalPrice >= 500;
})
&& (x =>
{
var cnt = ReturnOrderRepositoryX.CheckReturnOrderExists(x.orderId);
return cnt <= 0;
})
&& (x =>
{
var cnt = OrderRepositoryX.CheckActiveOrderJobDetailsByOrderId(x.orderId);
return cnt <= 0;
});

How to total quantity based on date range from another table?

how to total quantity based on date range from another table?
var items = entity.ItemHeaders.Where(x => x.date >= _dateFrom && x.date <= dateTo);
var tQty = entity.ItemContents.Where(x => x.Warehouse == dto.Warehouse).Sum(x => x.Quantity);
the problem is I don't have any idea how to connect them.
To join the entity by HeaderNumber and sum the total:
var total = (from header in entity.ItemHeaders
from content in entity.ItemContents
where header.date >= _dateFrom && header.date <= dateTo
&& content.Warehouse == dto.Warehouse
&& header.ID = content.HeaderNumber
select content.Quantity).Sum();
Assuming you are using Entity Framework, it will be easier if you have a Navigation Property defined in ItemContent for the ItemHeader. e.g.:
public virtual ItemHeader Header {get; set;}
Then the query could be:
var total = entity.ItemContents
.Where(x=> x.Header.date >= _dateFrom && x.Header.date <= dateTo
&& x.Warehouse == dto.Warehouse)
.Sum(x => x.Quantity);

Get List of Records between two times C# Linq To Entities

In my application, I am wanting to fill a list with records from the database that match specific conditions:
public ActionResult SelectYearGraph(int year)
{
var lstAllSummaries = db.Summaries.ToList();
var lstMonths =
lstAllSummaries.Where(x => x.FlightDay.Year == year)
.Select(x => x.TestDay.Month)
.Distinct()
.OrderBy(x => x)
.ToList();
List<string> lstMonthNames = new List<string>();
List<int> lstCountSummaries = new List<int>();
List<int> lstCountDailySummariesDifferentTime = new List<int>();
var tsSix = new TimeSpan(6, 0, 0);
var tsTen = new TimeSpan(22, 0, 0);
foreach (var item in lstMonths)
{
var monthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(item);
lstMonthNames.Add(monthName);
foreach (var item in lstMonths)
{
var monthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(item);
lstMonthNames.Add(monthName);
lstCountDailySummaries.Add(lstAllSummaries.Count(x => x.FlightDay.Month == item && x.FlightDay.Year == year
&& (x.FlightDay.TimeOfDay >= tsSix && x.FlightDay.TimeOfDay <= tsTen)
&& !x.deleted));
lstCountDailySummariesDifferentTime.Add(lstAllSummaries.Count(x => x.FlightDay.Month == item && x.FlightDay.Year == year
&& (x.FlightDay.TimeOfDay > tsTen || x.FlightDay.TimeOfDay < tsSix)
&& !x.deleted));
}
}
... // more down here but not relevant to question
}
When I run this, I get a runtime error:
The specified type member 'TimeOfDay' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
I have tried Date and Time Canonical Functions for LINQ-to-Entities, but I am receiving the same error.
How do I get all records between 6am and 10pm?
You don't need TimeOfDay just reading Hour, Minute, Second of your DateTime will give you the same result in your case.
the TimeOfDay property returns a TimeSpan value that represents a DateTime value's time component.
So just change your code to:
...
&& (DbFunctions.CreateTime(x.TestDay.Hour, x.TestDay.Minute,x.TestDay.Second) > DbFunctions.CreateTime(6,0,0)
&& (DbFunctions.CreateTime(x.TestDay.Hour, x.TestDay.Minute, x.TestDay.Second) < DbFunctions.CreateTime(22,0,0))
...
The problem with looking for times which cross midnight is that whereas a time can be both after 6am AND before 10pm the same is not true for after 10pm AND before 6am.
You'll need to change your AND to an OR
...
&& (
(DbFunctions.CreateTime(x.TestDay.Hour, x.TestDay.Minute,x.TestDay.Second) > DbFunctions.CreateTime(22,0,0)
|| (DbFunctions.CreateTime(x.TestDay.Hour, x.TestDay.Minute, x.TestDay.Second) < DbFunctions.CreateTime(6,0,0))
)
...
var query = from x in db.Summaries
let time = DbFunctions.CreateTime(x.TestDay.Hour, x.TestDay.Minute, x.TestDay.Second)
where x.FlightDay.Year == year
&& !x.deleted
&& (time < DbFunctions.CreateTime(6, 0, 0))
&& (time > DbFunctions.CreateTime(22, 0, 0))
group x by x.FlightDay.Month into summaries
let month = summaries.Key
select new
{
Month = month,
Count = summaries.Count(),
DifferentCount = (from d in db.DailySummaries
let timed = DbFunctions.CreateTime(d.TestDay.Hour, d.TestDay.Minute, d.TestDay.Second)
where d.FlightDay.Year == year && d.FlightDay.Month == month
&& !d.deleted
&& (timed < DbFunctions.CreateTime(6, 0, 0))
&& (timed > DbFunctions.CreateTime(22, 0, 0))
select d).Count(),
};

Filtering Linq Query Correctly

I have this query that retrieves sales from a bunch of shops, and returns them as a list. On my report request screen, the user can filter by a number of ids - ShopOwnerId, ShopRegionId, ShopTypeCode, etc.
My query is structured to get all sales between selected dates, then filter down depending on the choices. This is obviously highly inefficient. :
private List<Sales> GetFilteredListOfSales(Request reportreq)
{
ModelContainer ctn = new ModelContainer();
List<ShopSale> shopsSales = new List<shopsale>();
// If no filters are selected
//
if (reportreq.RegionalId == null && reportreq.OwnerId == null && reportreq.ShopTypeCode == null)
{
shopsSales = (from sale in ctn.ShopSales
where sale.DateSold >= reportreq.FromDate && sale.DateSold <= reportreq.ToDate
select sale).ToList();
}
// If the regional ID has a value...
//
if (reportreq.RegionalId.HasValue)
{
shopsSales = (from sale in ctn.ShopSales
where sale.Shop.Owner.RegionalId == reportreq.RegionalId
&& sale.DateSold >= reportreq.FromDate && sale.DateSold <= reportreq.ToDate
select sale).ToList();
}
// If the owner ID has a value...
//
if (reportreq.OwnerId.HasValue)
{
shopsSales = (from sale in ctn.ShopSales
where sale.Shop.OwnerId == reportreq.OwnerId
&& sale.DateSold >= reportreq.FromDate && sale.DateSold <= reportreq.ToDate
select sale).ToList();
}
if (!string.IsNullOrEmpty(reportreq.ShopTypeCode))
{
shopsSales = (from sale in ctn.ShopSales
where sale.Shop.ShopTypeCode.ToUpper().Contains(reportreq.ShopTypeCode.ToUpper())
&& sale.DateSold >= reportreq.FromDate && sale.DateSold <= reportreq.ToDate
select sale).ToList();
}
return shopsSales;
}
You can see, that this method, queries the sales table between certain dates only if no filters have been selected.
What I'm stuck on is, the user could select more than one filter - e.g. they might select a regionId, and a ShopTypeCode, so I don't want to query ctn.ShopSales for each if filter, because if more than one is selcted, it will wipe out the previously retrieved values.
Does anyone have any advice as to how to get around this issue? If more info is needed just ask!
Thanks
Sure - you don't want to build the query from scratch each time. Just add extra Where calls conditionally:
var query = ctn.ShopSales.Where(sale => sale.DateSold >= reportreq.FromDate
&& sale.DateSold <= reportreq.ToDate);
if (reportreq.OwnerId.HasValue)
{
query = query.Where(sale => sale.Shop.OwnerId == reportreq.OwnerId);
}
if (!string.IsNullOrEmpty(reportreq.ShopTypeCode))
{
query = query.Where(sale.Shop.ShopTypeCode.ToUpper()
.Contains(reportreq.ShopTypeCode.ToUpper());
}
var shopSales = query.ToList();
Note that the query won't execute until you materialize it in the final line.

asp.net mvc LINQ datetime problem

string NewsFillter = string.Empty;
List<string> PublishDatePostMeta = (from post in postrepository.GetAllPosts()
join pstmt in postrepository.GetAllPostMetas()
on post.int_PostId equals pstmt.int_PostId
where (post.int_PostTypeId == 4 && post.int_PostStatusId == 2 && post.int_OrganizationId == layoutrep.GetSidebarDetailById(SidebarDetailsId).int_OrganizationId) && pstmt.vcr_MetaKey=="Publish Date"
select pstmt.vcr_MetaValue).ToList();
int DatesCount = PublishDatePostMeta.Count();
foreach (string PublishDate in PublishDatePostMeta)
{
if (PublishDate != "")
{
NewsFillter += System.DateTime.Now + ">=" + Convert.ToDateTime(PublishDate);
}
}
var postsidebar = from post in postrepository.GetAllPosts()
join pstmt in postrepository.GetAllPostMetas()
on post.int_PostId equals pstmt.int_PostId
where (post.int_PostTypeId == 4 && post.int_PostStatusId == 2 && post.int_OrganizationId == layoutrep.GetSidebarDetailById(SidebarDetailsId).int_OrganizationId)
&& (pstmt.vcr_MetaKey.Contains(filter) && pstmt.vcr_MetaValue.Contains("true"))
select post;
1st question .The thing is that how NewsFillter would be accomdated in the postsidebar query in the pstmt object after true ( i would be putting it in contains,equals join or what) .
2nd question . is there any way that a chunk (between &&s) return enumerable and i can get away with this. at this moment it is not allowing that
I haven't udnerstood you properly, but if you want to apply multiple filters, here is my solution:
//Book is a table in the database
List<Expression<Func<Book, bool>>> filters = new List<Expression<Func<Book, bool>>>();
IQueryable<Book> query = dc.Books;
filters.Add(b => b.BookId == long.Parse(id));
//apply all filters
foreach (var f in filters)
query = query.Where(f);
Your questions:
This question requires an example of input and output. Try something like this:
|| pstmt.vcr_MetaKey=="Publish Date" && (
pstmt.vcr_MetaValue == "" || DateTime.Parse(pstmt.vcr_MetaValue) < DateTime.Now)
There is method AsEnumerable, if you mean what I think.

Categories

Resources