Here is my SQL query. I'm trying to produce the same results in my .NET Core web app using a linq query.
SELECT *
FROM dbo.BUD
WHERE LEFT(PeriodD, 4) = YEAR(GETDATE())
Here is what I've tried with no success in my linq query
List<SelectListItem> budNames = _ctx.Budgets.AsNoTracking()
.Where(n => (n.Period, 4) == GETDATE())
.Select(n => new SelectListItem
{
// values
}).ToList();
Mainly this line causes you problems:
.Where(n=> (n.Period,4) == GetDate())
To get the first 4 characters of a string, use the Substring method:
n.Period.Substring(0, 4) // beginn 0 away from the first char, take 4 of them
To get a sting of the current year (DateTime):
var currentYear = DateTime.Now.ToString("yyyy");
Applied to your example:
var currentYear = DateTime.Now.ToString("yyyy");
List<SelectListItem> budNames = _ctx.Budgets.AsNoTracking()
.Where(n => n.Period.Substring(0, 4) == currentYear)
.Select(n => new SelectListItem
{
// values
}).ToList();
Or, you could use the StartsWith method:
var currentYear = DateTime.Now.ToString("yyyy");
List<SelectListItem> budNames = _ctx.Budgets.AsNoTracking()
.Where(n => n.Period.StartsWith(currentYear))
.Select(n => new SelectListItem
{
// values
}).ToList();
Related
I have this query:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
}).ToDictionary(y => y.NodeId, y => y)
}).SingleOrDefaultAsync();
and also tried like this:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.ToDictionary(y => y.Id, y => new TreeItemModel(
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
}).SingleOrDefaultAsync();
Both ways I got this exception:
What could be causing this? and is there a way I could make this work without fetching as a list and then covert it to dictionary? What could be the most efficient way to achieve this?
Regards
What could be causing this?
Translation of the nested ToDictionary call (none of the available overloads) is not supported. Consider it one of the (many) current (latest at the time of writing official v5.0.11) EF Core shortcomings.
Interestingly though, the Dictionary<TKey, TValue> constructor overload with IEnumerable<KeyValuePair<TKey, TValue>> argument as well as projection (Select) to KeyValuePair<TKey, TValue> is supported, which gives the workaround - replace the ToDictionary call with the aforementioned constructor and Select, e.g. (replace int with the type of the Id)
TreeDataDict = new Dictionary<int, TreeItemModel>(
x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
.Select(e => new KeyValuePair<int, TreeItemModel>(e.NodeId, e)));
This will fix the current error. But note that you are using other non-translatable constructs (custom method calls like SetTaskParentId(y, y.Type) and TaskHelper.GetTaskIcon(y.Type)) which are supported only in the final Select, so make sure to not add LINQ operator like Where, OrderBy etc. after the root query Select(x => new ProjectModel { ... }, otherwise you'll get other runtime errors. ToList, `First
I have 3 tables,
Table Operation
Columns:
idOperation int cdLangPrimary char(2)
Table Languages
Columns:
cdLang char(2) nmLang nvarchar(10)
Table OperationLanguages
Columns: idOperation int cdLang char(2)
My code:
var jsonObject = dbContext.Operations
.Single(o => o.idOperation == idOperation)
.Languages
.Select(l => new { l.cdLang, l.nmLang });
What I was trying to do (with no success),
is Order the Languages by a-Z, but put the cdLangPrimary as the first.
I know it is possible if I create a List(or Dictionary) like so:
var languages = new List<Languages>();
var dLanguage = operation.Languages.Single(l => l.cdLang == operation.cdLangPRIMARY);
languages.Add(dLanguage);
languages.AddRange(operation.Languages.Where(l => l.cdLang != dLanguage.cdLang));
Just wondering if there is an option to the same with linq
or in a more effective way?
If I understand your question, the following should do it:
var result = dbContext.Languages
.Where(l => l.OperationId == idOperation)
.OrderByDescending(l => l.cdLang == l.Operation.cdLangPrimary)
.ThenBy(l => l.cdLang)
.Select(l => new { l.cdLang, l.nmLang });
If you don't have relationships set up in entity framework between an Operation and a Language then you can achieve the same result as follows:
var operation = dbContext.Operations
.SingleOrDefault(o => o.idOperation == idOperation);
if(operation != null)
{
var result = dbContext.Languages
.Where(l => l.OperationId == idOperation)
.OrderByDescending(l => l.cdLang == operation.cdLangPrimary)
.ThenBy(l => l.cdLang)
.Select(l => new { l.cdLang, l.nmLang });
}
I want to optimize this query and reduce the number of loops if possible. Atleast the one where i have to select all the client id first for iteration purpose.
any help appreciated.
public DataTable convertCollectionExpectedToDatatable(List<Invoice> lst)
{
DataTable dtcollection = new DataTable();
try
{
dtcollection.Columns.Add("ClientId", typeof(string));
dtcollection.Columns.Add("customerName", typeof(string));
dtcollection.Columns.Add("BalAmnt1", typeof(string));
dtcollection.Columns.Add("BalAmnt2", typeof(string));
dtcollection.Columns.Add("BalAmnt3", typeof(string));
dtcollection.Columns.Add("totalAmt", typeof(string));
DateTime promiseDate1 = DateTime.Today;
DateTime promiseDate2 = promiseDate1.AddDays(1);
DateTime promiseDate3 = promiseDate2.AddDays(1);
var select = (from l in lst select l.ClientId).Distinct();
List<long> lstInv = select.ToList<long>();
DataRow dr;
foreach (long inv in lstInv)
{
decimal BalAmnt1 = lst.Where(Invoice => Invoice.ExpDt ==
promiseDate1 && Invoice.ClientId == inv).Select(Invoice => Invoice.BalAmnt).Sum();
decimal BalAmnt2 = lst.Where(Invoice => Invoice.ExpDt ==
promiseDate2 && Invoice.ClientId == inv).Select(Invoice => Invoice.BalAmnt).Sum();
decimal BalAmnt3 = lst.Where(Invoice => Invoice.ExpDt ==
promiseDate3 && Invoice.ClientId == inv).Select(Invoice => Invoice.BalAmnt).Sum();
var clientName = (from l in lst where l.ClientId == inv select
l.Client.Name).FirstOrDefault();
dr = dtcollection.NewRow();
dr["ClientId"] = inv.ToString();
dr["customerName"] = clientName.ToString();
dr["BalAmnt1"] = string.Format("{0:n2}", BalAmnt1);
dr["BalAmnt2"] = string.Format("{0:n2}", BalAmnt2);
dr["BalAmnt3"] = string.Format("{0:n2}", BalAmnt3);
dr["totalAmt"] = string.Format("{0:n2}", BalAmnt1 + BalAmnt2 +
BalAmnt3);
dtcollection.Rows.Add(dr);
}
}
You can group invoices by ClientId and calculate all sums with this query:
from invoice in lst
group invoice by invoice.ClientId into g
select new {
BalAmnt1 = g.Where(i => i.ExpDt == promiseDate1).Sum(i => i.BalAmnt),
BalAmnt2 = g.Where(i => i.ExpDt == promiseDate2).Sum(i => i.BalAmnt),
BalAmnt3 = g.Where(i => i.ExpDt == promiseDate3).Sum(i => i.BalAmnt)
}
Also consider better naming. What name lst will tell to people who will read your code. If this is a list of invoices, then name it invoices. Same with promiseDates - today and tomorrow will better describe what dates stored in variables:
DateTime today = DateTime.Today;
DateTime tomorrow = today.AddDays(1);
// don't know how to name dayAfterTomorrow
// suppose it has some business-specific name in your case
Also you can extract duplicated code into separate method:
public static decimal CalculateBalanceOn(
this IEnumerable<Invoice> invoices, DateTime date)
{
return invoices.Where(i => i.ExpDt == date).Sum(i => i.BalAmnt);
}
With all above query will look like:
from invoice in invoices
group invoice by invoice.ClientId into g
select new {
BalanceToday = g.CalculateBalanceOn(today),
BalanceTomorrow = g.CalculateBalanceOn(tomorrow),
BalanceAfterTomorrow = g.CalculateBalanceOn(dayAfterTomorrow)
}
The EntityModel is defined as:
Personnel has a link to a Country
When executing this code in LinqPad, I see that the SQL which is generated is not optimized (all fields are returned) in the first query ? What am I missing here or doing wrong ?
Query 1 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
personnelIds.Dump();
Query 1 SQL
exec sp_executesql N'SELECT [t0].[Id], [t0].[Version], [t0].[Identifier], [t0].[Name], , [t0].[UpdatedBy] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
Query 2 LINQ
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds2 = Personnels.Where(p => p.Country == Country).Select(p => p.Id).ToArray();
personnelIds2.Dump();
Query 2 SQL
exec sp_executesql N'SELECT [t0].[Id] FROM [Personnel] AS [t0] WHERE [t0].[Country_Id] = #p0',N'#p0 bigint',#p0=100000581
The database used is SQL Express 2008. And LinqPad version is 4.43.06
//var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
var personnelIds = context.Personnels
.Where(p => p.Country.Id == 100000581)
.Select(p => p.Id)
.ToArray();
personnelIds.Dump();
Try this, it should be better.
Personnels collection will be populated via lazy loading when accessed, hence retrieving all of the fields from the DB. Here's what's happening...
// retrieves data and builds the single Country entity (if not null result)
var Country = Countries.FirstOrDefault(o => o.Id == 100000581);
// Country.Personnels accessor will lazy load and construct all Personnel entity objects related to this country entity object
// hence loading all of the fields
var personnelIds = Country.Personnels.Select(p => p.Id).ToArray();
You want something more like this:
// build base query projecting desired data
var personnelIdsQuery = dbContext.Countries
.Where( c => c.Id == 100000581 )
.Select( c => new
{
CountryId = c.Id,
PersonnelIds = c.Personnels.Select( p => p.Id )
}
// now do enumeration
// your example shows FirstOrDefault without OrderBy
// either use SingleOrDefault or specify an OrderBy prior to using FirstOrDefaul
var result = personnelIdsQuery.OrderBy( item => item.CountryId ).FirstOrDefault();
OR:
var result = personnelIdsQuery.SingleOrDefault();
Then get the array of IDs if not null
if( null != result )
{
var personnelIds = result.PersonnelIds;
}
Try can also try grouping personnel into a single query
var groups =
(from p in Personnel
group p by p.CountryId into g
select new
{
CountryId = g.Key
PersonnelIds = p.Select(x => x.Id)
});
var personnelIds = groups.FirstOrDefault(g => g.Key == 100000581);
Do you have the ForeignKey explicitly defined in your POCO for Personnel? It's common to leave it out in EF, but adding it would massively simplify both this code and the resulting SQL:
public class Personnel
{
public Country Country { get; set; }
[ForeignKey("Country")]
public int CountryId { get; set; }
. . .
}
> update-database -f -verbose
var ids = db.Personnel.Where(p => p.CountryId == 100000581).Select(p => p.Id).ToArray();
I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};