I am using a Union to combine two tables audit and message. They both have all the same column names except audit has an extra column called deleteDate. I am try to use a Union to merge both table where if deleteDate does not exist, than it sets the DeleteDate method to null. I commented out what I am trying to get to work. When I uncomment the code, I get an error. Is there a work around? Here is my code
var audit = from a in _Audit.GetAll()
.Where(a => a.PInt == pInt && a.CreateDate < startDate && (endDate == null || endDate > a.CreateDate))
select new
{
a.MInt,
a.MId,
a.Desc,
a.PString,
a.PType,
a.MType,
a.CreateDate,
// a.DeleteDate,
a.PInt
};
var message = from a in _Message.GetAll()
.Where(a => a.PartnerInt == partnerInt && a.CreateDate < startDate && (endDate == null || endDate > a.CreateDate))
select new
{
a.MInt,
a.MId,
a.Desc,
a.PString,
a.PType,
a.MType,
a.CDate,
a.PInt
};
var test = from a in audit.Union(message)
select new AuditMessagesGroup
{
MInt = a.Int,
MId = a.Id,
Desc = a.Desc,
PString = a.PString,
PType = a.PayloadType,
MessageType = a.MType,
CreateDate = a.CreateDate,
// DeleteDate = a.DeleteDate != null ? a.DeleteDate : null,
PInt = a.PInt
};
Here is the error
Error CS1929 'IQueryable<<anonymous type: int MInt, Guid
MId, string Desc, string PString, string PType,
MTypes MType, DateTime CreateDate, DateTime DeleteDate, int?
PInt>>' does not contain a definition for 'Union' and the best
extension method overload 'ParallelEnumerable.Union<<anonymous type: int
MInt, Guid MId, string Desc, string PString, string
PType, MTypes MType, DateTime CreateDate, int? PInt>>
(ParallelQuery<<anonymous type: int MInt, Guid MId, string
Desc, string PString, string PType, MTypes
MType, DateTime CreateDate, int? PInt>>, IEnumerable<<anonymous
type: int MInt, Guid MId, string Desc, string
PString, string PType, MTypes MType, DateTime
CreateDate, int? PInt>>)' requires a receiver of type
'ParallelQuery<<anonymous type: int MInt, Guid MId, string
Desc, string PString, string PType, MTypes
MType, DateTime CreateDate, int? PInt>>'
To make .Union(...) work the anonymous type must be exactly the same - and that requires it to have the exact same number of fields, with the exact same types, with the exact same names.
So, from your queries, I think you need this:
var audit =
from a in _Audit
.GetAll()
.Where(a => a.PInt == pInt)
.Where(a => a.CreateDate < startDate)
.Where(a => endDate == null || endDate > a.CreateDate)
select new
{
a.MInt,
a.MId,
a.Desc,
a.PString,
a.PType,
a.MType,
a.CreateDate,
a.DeleteDate,
a.PInt
};
var message =
from a in _Message
.GetAll()
.Where(a => a.PartnerInt == partnerInt)
.Where(a => a.CreateDate < startDate)
.Where(a => endDate == null || endDate > a.CreateDate)
select new
{
a.MInt,
a.MId,
a.Desc,
a.PString,
a.PType,
a.MType,
CreateDate = a.CDate,
DeleteDate = (DateTime?)null,
a.PInt
};
I can't tell for sure what the types are of each table, but hopefully that's pretty close.
It is possible that you'll need to pull the records in to memory using .ToArray() followed by a new .Select(...) to get the field types to align, but assuming they are already the same it should work fine.
NO you can't. Basic criteria on union is that column count must match b/w tables. You can generate a dummy column though to bypass. like
considering a.DeleteDate is DateTime in audit change your message to be
var message = from a in _Message.GetAll()
.Where(a => a.PartnerInt == partnerInt && a.CreateDate < startDate && (endDate == null || endDate > a.CreateDate))
select new
{
a.MInt,
a.MId,
a.Desc,
a.PString,
a.PType,
a.MType,
a.CDate,
DateTime.Now, // a dummy column
a.PInt
};
Then you can perform the UNION
Related
I have a linq query that I would like to return results with user input data. However, if this function gets called and there is zero data from user, OR user just wants to search via data, OR just one of the other parameters, how can I efficiently write the linq to accommodate for this? Here is the Linq and function:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId, int logLevelId,
DateTime startDate, DateTime endDate)
{
var logsList = new List<Objects.Logs.GenericLog>();
using(var db = CORAContext.GetCORAContext())
{
logsList = (from i in db.GenericLog select new Objects.Logs.GenericLog()
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
})
.Where(i => i.LogDateTime >= startDate && i.LogDateTime <= endDate)
.Where(i => i.EntityId == entityId || i.EntityId == null)
.Where(i => i.LogLevelId == logLevelId || i.EntityId == null)
.ToList();
}
return logsList;
}
For example, in the second and third Where(), I have || i.EntityId == null... thinking this would accomodate for is user input for Entity is null?
Will this work?
Also, how can I do this for date ranges? Can I also do the same?
Finally, is there a BETTER way to do this?
Split creating a query and generating a final result by .ToList()
When you generate a query, you can add where statements on demand, like this:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId, int logLevelId, DateTime startDate, DateTime endDate)
{
var logsList = new List<Objects.Logs.GenericLog>();
using(var db = CORAContext.GetCORAContext())
{
var query = (from i in db.GenericLog select new Objects.Logs.GenericLog()
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
});
if(someCondition) {
query = query.Where(i => i.LogDateTime >= startDate && i.LogDateTime <= endDate)
}
query = query.Where(i => i.EntityId == entityId || i.EntityId == null)
query = query.Where(i => i.LogLevelId == logLevelId || i.EntityId == null)
logsList = query.ToList();
}
return logsList;
}
If I understand you correctly, you have a method that gets a filtered set of data based on the values of the parameters passed in. But you want to make the parameters optional, so if the user wants data for all entities, they wouldn't pass in an entityId.
If that's the case, then you can make the arguments optional by providing a default value for them in the method signature. We can then check if the argument has the default value, and if it does, don't apply that filter; otherwise apply it.
We can do this by doing .Where(x => argHasDefaultValue || someFilter). This works because if the argument has the default value, then the second part of the || is ignored.
For example:
public static List<Objects.Logs.GenericLog> GetLogs(int entityId = int.MinValue,
int logLevelId = int.MinValue, DateTime startDate = default(DateTime),
DateTime endDate = default(DateTime))
{
using(var db = CORAContext.GetCORAContext())
{
return db.GenericLog
.Where(i => startDate == default(DateTime) || i.LogDateTime >= startDate)
.Where(i => endDate == default(DateTime) || i.LogDateTime <= endDate)
.Where(i => entityId == int.MinValue || i.EntityId == entityId)
.Where(i => logLevelId == int.MinValue || i.LogLevelId == logLevelId)
.Select(i => new Objects.Logs.GenericLog
{
EntityId = i.FkEntityId,
LogSourceCode = i.FkLogSourceCode,
LogLevelId = i.FkLogLevelId,
LogDateTime = i.LogDateTime,
LogId = i.PkLogId,
Message = i.Message
}).ToList();
}
}
x.CreateDate DateTime is stored in our database down to milliseconds. My dateTimePicker values startdate and enddate only allows for querying down to seconds.
How can change my query to ignore the milliseconds of x.CreateDate? I thought the code I wrote below would work but it is not.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.AddMilliseconds(-x.CreateDate.Millisecond) == startdate)
.OrderByDescending(x => x.ID)
.Take(count);
var query = from l in _context.Logs
where l.ApplicationID == applicationId
&& SqlMethods.DateDiffSecond(l.CreateDate,startdate) == 0
orderby l.ID descending
select l).Take(count);
This avoids converting every date in you table into a string and the subsequent string comparison, by comparing the two dates as dates.
Getting CreateDate and startdate in the same format will help you compare apples to apples. This should accomplish that.
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.ToString(#"MM/DD/YYYY h:mm:ss") == startdate.ToString(#"MM/DD/YYYY h:mm:ss")
.OrderByDescending(x => x.ID)
.Take(count);
I have no idea why I could not get any results from the queries posted above as I tried several variations of their themes. However I did get it working correctly by adding milliseconds to the startdate and enddate variables and it s working.
if (stardDateIsValid && endDateIsValid)
startdate = startdate.AddMilliseconds(000);
enddate = enddate.AddMilliseconds(999);
query = _context.Logs.Where(x => x.ApplicationID == applicationId && x.CreateDate >= startdate && x.CreateDate <= enddate).OrderByDescending(x => x.ID).Take(count);
You can create extension method.
public const long TicksPerMillisecond = 10000;
public const long TicksPerSecond = TicksPerMillisecond * 1000;
public static bool IsEqualIgnoreMilliseconds(this DateTime date, DateTime compareDate)
{
long tickDiff = date.Ticks - compareDate.Ticks;
return tickDiff > 0 ? tickDiff < TicksPerSecond : tickDiff < -TicksPerSecond;
}
Then you can use this:
if (stardDateIsValid && endDateIsValid && startdate == enddate)
query = _context.Logs
.Where(x => x.ApplicationID == applicationId &&
x.CreateDate.IsEqualIgnoreMilliseconds(startdate)
.OrderByDescending(x => x.ID)
.Take(count);
I've created the most complex linq query ever, but still do not have exactly what I need. From this query, I need to know how many related students in the EventStudents table that have a DateDeleted == null. So, the StudentCount = ev.EventStudents - where DateDeleted == null .Count().
Can anyone help with this please?
var inf = (from ev in db.Events
where (ev.StartDate >= beginDate && ev.StartDate <= endDate)
&& ev.DeletedDate == null
orderby ev.StartDate descending
select new
{
EventID = ev.EventID,
EventTitle = ev.Title,
EventDate = ev.StartDate,
StudentCount = ev.EventStudents.Count(),
CreatedUsername = ev.CreatedUsername
}).AsEnumerable().Select(x => new
{
EventID = x.EventID,
EventTitle = x.EventTitle,
EventDate = x.EventDate,
StudentCount = x.StudentCount,
CreatedUsername = x.CreatedUsername,
CreatedFullname = sortedUserDictionary.Where(u => u.Value == x.CreatedUsername)
.Select(f => f.Key)
.FirstOrDefault() ?? x.CreatedUsername
});
Change ev.EventStudents.Count() to ev.EventStudents.Count(es => es.DateDeleted == null).
I have a datatable with two columns FromDate and ToDate , which are in string format.
I want to check if there are any duplicate records in my table.i.e
From Date To Date
----------------------
9/01/2012 9/16/2012
8/23/2012 8/24/2012
8/25/2012 8/25/2012
8/5/2012 8/6/2012
8/26/2012 8/27/2012
9/15/2012 9/23/2012
The table contains duplicate records as their date range is mapping for
From Date To Date
----------------------
9/01/2012 9/16/2012
9/15/2012 9/23/2012
It should return false.
var query = from row in dt.AsEnumerable()
from row1 in dt.AsEnumerable()
where
(
(
DateTime.Parse(row1.Field<string>("fromDate")) >= DateTime.Parse(row.Field<string>("fromDate")) &&
DateTime.Parse(row1.Field<string>("fromDate")) <= DateTime.Parse(row.Field<string>("toDate"))
)
||
(
DateTime.Parse(row1.Field<string>("toDate")) >= DateTime.Parse(row.Field<string>("fromDate")) &&
DateTime.Parse(row1.Field<string>("toDate")) <= DateTime.Parse(row.Field<string>("toDate"))
)
)
select new
{
fromDate = DateTime.Parse(row1.Field<string>("fromDate")),
toDate = DateTime.Parse(row1.Field<string>("toDate"))
};
//This lst contains the dates which are overlapping
var lst = query.Distinct().ToList();
Okay then, a selfjoin will help here:
I have a small class TimePeriod, just to meet your needs
public class TimePeriod
{
public int Id;
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public static DateTime Parse(string date)
{
var dt = DateTime.Parse(date,
CultureInfo.CreateSpecificCulture("en-US"), DateTimeStyles.RoundtripKind);
return dt;
}
}
then I have some TestData
var list = new List();
list.Add(new TimePeriod() { Id = 1, FromDate = TimePeriod.Parse("9/01/2012"), ToDate = TimePeriod.Parse("9/16/2012") });
list.Add(new TimePeriod() { Id = 2, FromDate = TimePeriod.Parse("8/23/2012"), ToDate = TimePeriod.Parse("8/24/2012") });
list.Add(new TimePeriod() { Id = 3, FromDate = TimePeriod.Parse("8/25/2012"), ToDate = TimePeriod.Parse("8/25/2012") });
list.Add(new TimePeriod() { Id = 4, FromDate = TimePeriod.Parse("8/5/2012"), ToDate = TimePeriod.Parse("8/6/2012") });
list.Add(new TimePeriod() { Id = 5, FromDate = TimePeriod.Parse("8/26/2012"), ToDate = TimePeriod.Parse("8/27/2012") });
list.Add(new TimePeriod() { Id = 6, FromDate = TimePeriod.Parse("9/15/2012"), ToDate = TimePeriod.Parse("9/23/2012") });
And here is the solution: (with some inspiration of OraNob, thanks for that)
var overlaps = from current in list
from compare in list
where
(
(compare.FromDate > current.FromDate &&
compare.FromDate < current.ToDate) ||
(compare.ToDate > current.FromDate &&
compare.ToDate < current.ToDate)
)
select new
{
Id1 = current.Id,
Id2 = compare.Id,
};
Perhaps you want to leave out the second Id (as you will have duplicates
here ( 1 / 6) and (6 / 1)
Sort by ToDate, FromDate (or build a sorted array of indexes into your DataTable). Loop from row or array position #2 to the end and see if the FromDate <= to the previous item's ToDate. Place overlapping items into a list. Job done.
You can also sort by FromDate, ToDate and do similar logic.
Try parsing the "To Date" column and for each, search the "From Date" column for any earlier date that has a later corresponding "To Date".
Use DataTable.Search() mehod to find out existence of any record in your DataTable this way you could enforce uniqueness in your records.
Something like this
string expression;
expression = "FromDate = #9/01/2012# AND ToDate = #9/16/2012#";
DataRow[] foundRows;
// Use the Select method to find all rows matching the filter.
foundRows = table.Select(expression);
if(foundRows.Length > 0)
// Show duplicate message
else
// Insert your new dates
For more Go here
If you handle large datatables you should use #ErikE response.
which needs more lines of code, but is definitely the most efficient by far.
If its small table and you prefer to compare each two rows.
you can use what other advised.
just make sure to prevent row from being compared to itself, and duplicates in the result enumeration.
var query = from x in list
where list.Exists((y) => x != y &&
x.FromDate <= y.ToDate &&
y.FromDate <= x.ToDate)
select x;
strong textDeclare #Table Table
(
RowId Int Identity(1, 1) Not Null,
Id NChar(3) Not Null,
StartDate DATETIME Not Null,
EndDate DATETIME Not Null
);
Insert Into #Table (Id, StartDate, EndDate)
Select 'id1', '20131210 10:10', '20131220 10:10' Union All
Select 'id1', '20131211', '20131215' Union All
Select 'id1', '20131201', '20131205' Union All
Select 'id1', '20131206', '20131208' Union All
Select 'id1', '20131225 10:10', '20131225 10:11'
Select *
From #Table;
With Overlaps (OverlapRowId, BaseRowId, OStart, OEnd, BStart, BEnd)
As
(
Select Overlap.RowId, Base.RowId, Overlap.StartDate, Overlap.EndDate, Base.StartDate, Base.EndDate
From #Table As Base
Inner Join #Table As Overlap On Overlap.Id = Base.Id
Where (((Overlap.StartDate > Base.StartDate) And (Overlap.StartDate < Base.EndDate))
Or ((Overlap.StartDate = Base.StartDate) And (Overlap.EndDate > Base.EndDate)))
And (Base.RowId != Overlap.RowId)
)
-- Remove records that were found to cause overlap issues.
Delete T
From #Table As T
Inner Join
(
Select O.OverlapRowId
From Overlaps As O
Left Join Overlaps As Fp On Fp.OverlapRowId = O.BaseRowId
Where (Fp.OverlapRowId Is Null)
) As SubQuery On SubQuery.OverlapRowId = T.RowId;
-- Select the valid options.
Select RowId, Id, StartDate, EndDate
From #Table where StartDate<EndDate;
Go
I have a query that looks like this:
var TheQuery = (from....
where x.TheDate >= StartDate && x.TheDate <= EndDate
select new MyModel()
{
Total = (int?)x.Count() ?? 0,
....
}).Single();
Basically, I'm querying a number of records based between 2 dates. If for the date there are 0 values, it returns 0 as the Total. However, if there are no values at all, it returns null and crashes. I could add .SingleOrDefault() but it would return null instead of MyModel populated with a 0. The property Total is defined as an int.
How can I solve this?
Thanks
Count has an overload with a predicate, and returns 0 when no item matches the predicate
var result = new MyModel {
Total = <yourDataSource>
.Count(x.TheDate >= StartDate && x.TheDate <= EndDate)
};
if(TheQuery !=null || TheQuery .Count()>0){
//do something you wanna do
}
or
var v = TheQuery.ToList();
now check
if (v.Count > 0)
You should opt for:
int count = (from x in ...
where x.TheDate >= StartDate && x.TheDate <= EndDate
select c).Count();
That's what you want.
var TheQuery = (from....
where x.TheDate >= StartDate && x.TheDate <= EndDate
select new MyModel()
{
Total = (int?)x.Count() ?? 0,
....
}).DefaultIfEmpty(0).Single()'