LINQ check if rows fall in between dates with no gaps - c#

I need to write a query where it will only return the rows if the dates fall between a start and end date without having gaps.
Below would fail and return nothing:
Requested -----[---------------------------------]--------
In the DB ----------------[------]------------------------
Requested -----[---------------------------------]--------
In the DB --[----------------]----------------------------
Requested -----[---------------------------------]--------
In the DB --[----------------]---------[----------------->
Below would pass and return the rows from the database:
Requested -----[---------------------------------]--------
In the DB -----[---------------------------------]--------
Requested -----[---------------------------------]--------
In the DB --[------------------------------------------]--
Requested -----[---------------------------------]--------
In the DB --[----------------][---------------][--------]-
EDIT
Here's the POCO object of what the table will look like:
public class SubscriptionPeriod
{
public int Id { get; set; }
public int SubscriptionId { get; set; }
public int? MatchedSubscriptionPeriodId { get; set; }
public SubscriptionPeriodStatuses Status { get; set; }
public int Qty { get; set; }
public int? FillQty { get; set; }
public DateTime SubscriptionStartDate { get; set; }
public DateTime? SubscriptionEndDate { get; set; }
public int? RenewQty { get; set; }
public bool RollOverQty { get; set; }
public DateTime? ProcessedDate { get; set; }
public DateTime CreateDate { get; set; }
}
SubscriptionPeriods
.Where(sp => sp.Subscription.UserId == userId &&
sp.Subscription.ZoneId == zoneId &&
sp.Subscription.Type == 2 &&
sp.MatchedSubscriptionPeriodId == null &&
sp.SubscriptionStartDate <= subscriptionEndDate &&
sp.SubscriptionEndDate >= subscriptionStartDate &&
(sp.Status == SubscriptionPeriodStatuses.Initalized || sp.Status == SubscriptionPeriodStatuses.Open))
.Sum(s => (int?)s.Qty)
NOTE: When SubscriptionEndDate is null means the subscription lasts forever from the start date.

Related

Temporary variables in LINQ select statements for EF Core

I am having trouble writing a slightly complicated Select statement.
My EF objects look like this:
public partial class SensorEvent
{
public SensorEvent()
{
SensorData = new HashSet<SensorData>();
}
public int Id { get; set; }
public int SensorId { get; set; }
public int RecordTime { get; set; }
public Sensor Sensor { get; set; }
public ICollection<SensorData> SensorData{ get; set; }
}
public partial class SensorData
{
public int Id { get; set; }
public int SensorEventId { get; set; }
public string DataType { get; set; }
public string Description { get; set; }
public SensorEvent SensorEvent { get; set; }
}
public partial class Sensor
{
public Sensor()
{
SensorEvent = new HashSet<SensorEvent>();
SensorPlacement = new HashSet<SensorPlacement>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SensorEvent> SensorEvent{ get; set; }
public ICollection<SensorPlacement> SensorPlacement{ get; set; }
}
public partial class Room
{
public Sensor()
{
SensorPlacement = new HashSet<SensorPlacement>();
}
public int Id { get; set; }
public int FloorId { get; set; }
public string Name { get; set; }
public Floor Floor { get; set; }
public ICollection<SensorPlacement> SensorPlacement{ get; set; }
}
public partial class Floor
{
public Floor()
{
Room = new HashSet<Room>();
}
public int Id { get; set; }
public int BuildingId { get; set; }
public string Name { get; set; }
public Building Building { get; set; }
public ICollection<Room> Room{ get; set; }
}
public partial class Building
{
public Building()
{
Floor = new HashSet<Floor>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Floor> Floor{ get; set; }
}
public partial class SensorPlacement
{
public int Id { get; set; }
public int SensorId { get; set; }
public int RoomId { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public Sensor Sensor { get; set; }
public Room Room { get; set; }
}
Let me explain a bit. The main data being inserted to the db is SensorEvent. SensorEvent is essentially a list of SensorData objects sent from a sensor, along with which sensor sent it and when it was recorded. So far pretty simple, if I want to present all the SensorEvents and which sensor they came from, I can return the following domain objects
public class DomainSensorEvent
{
public int Id { get; set; }
public int SensorName { get; set; }
public int RecordTime { get; set; }
public IList<DomainSensorData> SensorData{ get; set; }
}
public partial class DomainSensorData
{
public int Id { get; set; }
public string DataType { get; set; }
public string Description { get; set; }
}
And I can get it like this (Ignoring the Where, Take, OrderBy, and Skip parts for the sake of simplicity).
public List<DomainSensorEvent> GetDomainSensorEvents()
{
return DbContext.SensorEvent.Select(dse => new DomainSensorEvent
{
Id = dse.Id,
SensorName = dse.Sensor.Name,
RecordTime = dse.RecordTime,
SensorData = dse.SensorData.Select(sd => new DomainSensorData
{
Id = sd.Id,
DataType = sd.DataType,
Description = sd.Description
}).ToList()
}).ToList();
}
Pretty straight forward so far, the problem arises when I want to include where a sensor was placed (Which Room, which Floor and Building) when this data was recorded. See, I don't have this information at insert time. The sensor can be moved around, so the SensorPlacement table might be updated after a SensorEvent from that sensor in that time interval has been inserted. Meaning instead of linking a SensorEvents to both a Room and Sensor, the Room is resolved at query time. Yes, this means a query could return the wrong Room, if SensorPlacement has not been updated, but it will eventually be correct. Complicating matters further is of course that a SensorEvent might not have occured in a Room, so I also need to check for null.
The new domain model and query (the part I need help with) looks like this.
public class DomainSensorEvent
{
public int Id { get; set; }
public int SensorName { get; set; }
public int Room { get; set; }
public int Floor { get; set; }
public int Building { get; set; }
public int RecordTime { get; set; }
public IList<DomainSensorData> SensorData{ get; set; }
}
public List<DomainSensorEvent> GetDomainSensorEvents()
{
return DbContext.SensorEvent.Select(dse => new DomainSensorEvent
{
Id = dse.Id,
SensorName = dse.Sensor.Name,
Room = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Name : null,
Floor = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Floor.Name : null,
Building = dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault() != null ?
dse.Sensor.SensorPlacement.Where(sp => dse.RecordTime > sp.From && (sp.To == null || dse.RecordTime < sp.To)).FirstOrDefault().Room.Floor.Building.Name : null,
RecordTime = dse.RecordTime,
SensorData = dse.SensorData.Select(sd => new DomainSensorData
{
Id = sd.Id,
DataType = sd.DataType,
Description = sd.Description
}).ToList()
}).ToList();
}
The most obvious problem is of course the same statement repeated 6 times (making an already slow query even worse). I have tried to solve it by storing it as a temporary variable, but apparently you can only have a single statement in a Select query that is translated to SQL. I have tried running an extra Select statment before this one, returning an anonomous type with SensorEvent and a corresponding SensorPlacement, but this breaks navigation properties. Using Joins also makes using navigation properties difficult.
Am I looking at this from the wrong angle? Is there some syntax I am missing, or do I need to do this another way? I know the best solution would be to be able to know SensorPlacement at insert time, but that is not currently possible.
You have a few questions here, so I don't know that this can really be answered but a few quick thoughts:
Use your navigation properties sooner, and put your where clause on the initial select:
public List<DomainSensorEvent> GetDomainSensorEvents()
{
return DbContext.SensorEvent
.Include("Sensor")
.Include("SensorData")
.Include("Sensor.SensorPlacement")
.Where(w => w.Sensor.SensorPlacement.Where(sp => w.RecordTime > sp.From
&& (sp.To == null || w.RecordTime < sp.To)))
.ToList();
}
This should (assuming the fks and Navigation properties are actually correct) give you a list of your Entities with your List navigations that are in the .Includes, that match on the where Clause (I don't think you'd want it as is though this way).
I'd then look at AutoMapper or something similar to convert it to your domain / view models.
var results = GetDomainSensorEvents();
Mapper.Map<DomainSensorEvent>(results);
Since you know some properties and properties of properties are null in the database, you can handle them in the mapping or even the .AfterMap() as required.
I'd also reconsider your naming strategy. DomainEntityName is descriptive, but it sort of ruins Intellisense when every class starts with the same word. It also reminds me of why you don't want to use Hungarian Notation.
On the Where Clause: This should work for One to One hierarchical properties (Navigation properties with one child:
.Where(w => w.Sensor.SensorPlacement.Where(sp => w.RecordTime > sp.From
&& (sp.To == null || w.RecordTime < sp.To)))
But for getting the parent with a one to many you'd need slightly different logic:
.Where(w => w.Sensor.SensorPlacement.Where(sp => sp.Any(w.RecordTime > sp.From
&& (sp.To == null || w.RecordTime < sp.To))))
This should get you any parent where the included List (In your case List ) has Any matching properties.
Lastly, this was a shot at reducing code I obviously can't reproduce and run locally. Hopefully this helps but it isn't going to be the exact solution.

how to make a property of a foreign table appear on datagridview using LINQ?

I have two tables auto gen from my MySQL database
public partial class adviser
{
public int adviserId { get; set; }
public string employeeNo { get; set; }
public string lastName { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public Nullable<int> statId { get; set; }
public Nullable<int> createdById { get; set; }
public Nullable<System.DateTime> createdOn { get; set; }
public virtual stat stat { get; set; }
public virtual user user { get; set; }
}
public partial class stat
{
public stat()
{
this.advisers = new HashSet<adviser>();
}
public int statId { get; set; }
public string statName { get; set; }
public string details { get; set; }
public virtual ICollection<adviser> advisers { get; set; }
}
then i have this method to get the collections of advisers
public List<adviser> GetList(AdviserParam p)
{
List<adviser> items;
using (VotingContext context = new VotingContext())
{
items = context.advisers.Include("stat").Where(x =>
(x.adviserId == p.AdviserId || p.AdviserId == 0)
&& (x.employeeNo.Contains(p.EmployeeNo) || p.EmployeeNo == "")
&& (x.lastName.Contains(p.LastName) || p.LastName == "")
&& (x.firstName.Contains(p.FirstName) || p.FirstName == "")
&& (x.middleName.Contains(p.MiddleName) || p.MiddleName == "")
// && (x.statId == 1)
).ToList();
}
return items;
}
then I use these codes to put them into the data grid view (win form), I edited the columns so that it wont display the adviserId and statId properties.
ItemsGridView.AutoGenerateColumns = false;
ItemsGridView.DataSource = vm.EntityList; // vm.EntityList is where I put the returned advisers from the GetList() method)
but the dgv doesn't display the statName (from stats table). How can I make it appear?
To get value from stat object you can select an anonymous type and include all the required properties.
I've shown a simple example to include statName and employeeNo.
var items = context.advisers.Where(x => x.adviserId == yourAdviserID)
.Select(a=> new {name = a.stat.statName, employee=a.employeeNo}).ToList();
Now if you bind items to Datagridview you'll see only name and employee columns.
If you dont want to display advisorID and statID then just dont include them in your new anonymous type.

ASP MVC Code First working with datetime computed columns

I have gone through numerous examples, trying to figure out how to automatically set the CreatedDate value for each entity when saving changes to through unitOfWork and Repository pattern.
So what i have is simple POCO:
public class Partners:IAutoGenerateDateFields
{
[Key]
public int PartnerId { get; set; }
public string PartnerCode { get; set; }
public string PartnerName { get; set; }
public string CompanyName { get; set; }
public string City { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public string TaxId { get; set; }
public int PartnerTypeId { get; set; }
public int PartnerStateId { get; set; }
public int LocationId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreatedDate { get; set; }
// [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
// public DateTime ModifiedDate { get;set;}
public virtual Locations Location { get; set; }
}
This Class implements the IAutoGenerateDateFields interface:
interface IAutoGenerateDateFields
{
DateTime CreatedDate { get; set; }
// DateTime ModifiedDate { get; set; }
}
Finally in my unitOfWork class i check if the added/modified entity implements the above interface and set the CreatedDate value to current date:
public void Save() {
context.ChangeTracker.DetectChanges();
var added = context.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Added)
.Select(t => t.Entity).ToArray();
foreach (var entity in added) {
if (entity is IAutoGenerateDateFields) {
var track = entity as IAutoGenerateDateFields;
track.CreatedDate = DateTime.Now;
}
}
var modified = context.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified)
.Select(t => t.Entity).ToArray();
foreach (var entity in modified) {
if (entity is IAutoGenerateDateFields) {
var track = entity as IAutoGenerateDateFields;
track.CreatedDate = DateTime.Now;
}
}
context.SaveChanges();
}
But every time i hit savechanges i get an error that CreatedDate cannot be null. Thus, the entity does not get set with the current date.
What am i missing here?
Remove the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute, since you generate the value in your code.
I basically did the same thing yesterday, and the following code is working fine :
var pendingChanges = GetContext().ChangeTracker.Entries<T>().Select(e => e.Entity).ToList();
foreach (var entity in pendingChanges)
{
entity.DateModified = DateTime.Now
}

EF can't return the items with condition but sql can why?

I have a query like this :
int a= pgc.Fronts.Where(i => i.ItemCode == itemcode && i.PC == pc).Count()
PGC is my dbcontext .The itemcode is '10414' and the pc value is null the result is 0.
So i changed the query to sql command :
/****** Script for SelectTopNRows command from SSMS ******/
SELECT [Id]
,[No]
,[ItemCode]
,[QtyRequest]
,[PC]
,[Date]
,[Type]
,[Line]
,[joint]
,[QTYIssue]
,[Recivedby]
FROM [pgc].[dbo].[Fronts] where ItemCode='10414' and pc is null
it returns 3 records as you can see here :
So why it happens ?
public partial class Front
{
public long Id { get; set; }
public string No { get; set; }
public string ItemCode { get; set; }
public Nullable<double> QtyRequest { get; set; }
public string PC { get; set; }
public System.DateTime Date { get; set; }
public string Type { get; set; }
public string Line { get; set; }
public string joint { get; set; }
public double QTYIssue { get; set; }
public string Recivedby { get; set; }
}
If I remember correctly (at least for Linq2Sql) if you want to compare with null values you should use Object.Equals:
int a = pgc.Fronts.Where(i =>
i.ItemCode == itemcode && Object.Equals(i.PC, pc)).Count();
This "bug" seems to have been fixed in later versions of Entity framework. For now you can use:
int a = pgc.Fronts.Where(i =>
i.ItemCode == itemcode && (i.PC == pc || (pc == null && i.PC == null)).Count();

Entity Framework: search for database entries with null columns

I have the following model:
public class FactCache
{
public int ID { get; set; }
public DateTime MonthDate { get; set; }
public string AttributeKey { get; set; }
public decimal RawValue { get; set; }
public decimal ManDayValue { get; set; }
public decimal FTEValue { get; set; }
public int? InputResourceID { get; set; }
public int? PhaseID { get; set; }
public virtual InputResources InputResource { get; set; }
public virtual DimPhase Phase { get; set; }
}
As you can see above, InputResourceID and PhaseID are nullable optional fields.
I want to write a query to find the first entry for a given date and AttributeKey where both PhaseID and InputResourceID are null.
I have tried the following code:
FactCache fact = db.FactCache.FirstOrDefault(
a => a.InputResourceID == null
&& a.PhaseID == null
&& a.MonthDate == monthDate
&& a.AttributeKey == key);
However, it returns a null object. What is the correct way to write the above query?
I have checked the database and there are indeed entries with null PhaseID and InputResourceID.
Not sure, but perhaps...
FactCache fact = db.FactCache.FirstOrDefault(
a => false == a.InputResourceID.HasValue
&& false == a.PhaseID.HasValue
&& a.MonthDate == monthDate
&& a.AttributeKey == key);
Just a note, I you are lucky enough to be running EF version 5 or above try the solution proposed here: Call is failing with null parameter

Categories

Resources