Except Sql query in Linq - entity-framework

I am using the bellow sql to get available room:
select rooms.number
from rooms
EXCEPT
(SELECT rooms.number
FROM dbo.Bookings
INNER JOIN dbo.Rooms ON Extent1.RoomId = dbo.Rooms.Id
WHERE
(Bookings.StartDate <= '2014-12-01')
AND (Bookings.EndDate >= '2016-12-05')
)
Could you please help me to convert it to Linq? (like "var list= from a in db...."
Thanks in advance

try this:
var start = DateTime.ParseExact("20141201",
"yyyyMMdd",
CultureInfo.InvariantCulture);
var end = DateTime.ParseExact("20141205",
"yyyyMMdd",
CultureInfo.InvariantCulture);
var list = dbContext.Rooms
.Except(dbContext.Bookings
.Where(e => e.StartDate >= start)
.Where(e => e.EndDate <= end)
.Select(e => e.Room))
.SELECT(e => e.Number)
.ToList();

Related

Select FROM Subquery without starting with another context object

I am trying to model the following MSSQL query that I am trying to replicate in netCore 2.2 - EF Core:
SELECT
wonum,
MIN(requestdate) AS startdate,
MAX(requestdate) AS enddate,
MIN(laborcode)
FROM
(
SELECT
wo.wonum,
sw.requestdate,
wo.wolablnk AS 'laborcode'
FROM
DB1.dbo.web_users wu INNER JOIN
DB2.dbo.workorder wo on
wu.laborcode = wo.wolablnk INNER JOIN
DB2.dbo.sw_specialrequest sw on
wo.wonum = sw.wonum
WHERE
wo.status in ('LAPPR', 'APPR', 'REC') AND
sw.requestdate > GETDATE()
) a
GROUP BY
wonum
ORDER by
I have the subquery portion built and working but that leaves me at an impasse:
var workOrders = await _db1Context.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC")
.ToListAsync();
var specialRequests = await _db2Context.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now)
.ToListAsync();
var subQuery = (from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
orderby webUser.Laborcode, specialRequest.Requestdate, specialRequest.Wonum
select new { workOrder.Wonum, Laborcode = workOrder.Wolablnk, specialRequest.Requestdate, workOrder.Workorderid })
.ToList();
I am not sure how to initiate the query I need with the subquery i've built and i'm not sure if I am on the right track even. I've looked at a couple of other examples but i'm not getting it.
Would anyone be able to shed some light on the subject and help?
Thank you!
Write LINQ query identical to the SQL and do not mix with ToListAsync(). After ToListAsync() query is sent to the server. Also you should use only one DbContext for such query.
var webUsers = _db1Context.Webuser;
var workOrders = _db1Context.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC");
var specialRequests = _db1Context.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now);
var subQuery =
from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
select new
{
workOrder.Wonum,
Laborcode = workOrder.Wolablnk,
specialRequest.Requestdate
};
var resultQuery =
from a in subQuery
group a by a.Wonum into g
select new
{
Wonum = g.Key,
StartDate = g.Min(x => x.Requestdate),
EndDate = g.Max(x => x.Requestdate),
Laborcode = g.Min(x => x. Laborcode)
};
// final materialization
var result = await resultQuery.ToListAsync();

ef core select records from tableA with matching fields in tableB

I'm trying to translate a query from raw sql to linq statement in EF Core 2
Query is something like this
SELECT * FROM TableA
WHERE FieldA = ConditionA
AND FieldB IN (SELECT FieldC FROM TableB WHERE FieldD = ConditionB)
FieldA and FieldB are from TableA, FieldC and FieldD are from TableB
I need all fields from TableA and none from TableB returned, so it should be something like
return Context.TableA
.Where(x => x.FieldA == ConditionA)
.[ some code here ]
.ToList()
my current solution is to get two distinct result sets and join them in code, something like this
var listA = Context.TableA
.Where(x => x.FieldA == ConditionA).ToList();
var listB = Context.TableB
.Where(x => x.FieldD == ConditionB).Select(x => x.FieldC).ToList();
listA.RemoveAll(x => !listB.Contains(x.FieldB);
return listA;
i hope it works, i still have to run tests on it, but i'm looking for a better solution (if anything exists)
You can simply use Contains function in query like this :
var ids = Context.TableB
.Where(p => p.FieldD == conditionD)
.Select(p => p.FieldC);
var result = Context.TableA
.Where(p => ids.Contains(p.FieldB))
.ToList();
It's a simple join that needs to be applied -
var result = (from a in Context.TableA.Where(x => x.FieldA == ConditionA )
join b in Context.TableB.Where(x => x.FieldD == ConditionB) on a.FieldB equals b.FieldC
select new {a}
).ToList();

Can't add/sum string values in IEnumerable

The ultimate goal is to display a GrandTotal column using Highcharts. The GrandTotal should be the sum of TotalAmount for a given Offer id. TotalAmount is a string and the values are like $10.00 or 10.00. GrandTotal is an int, but could easily be changed. Here is what I have done so far.
Step 1) Convert the two IEnumerable lists into their ViewModel counterparts. I set GrandTotal to 0 here because I don't know the amount.
var offersConvert = offers
.Select(o => new OfferSummaryViewModel
{
Id = o.Id,
Name = o.Name,
Created = o.Created,
Shares = o.Shares,
Redemptions = o.Redemptions,
GrandTotal = 0
})
.ToList();
var sharedOffersConvert = sharedOffers
.Select(s => new SharedOfferViewModel
{
OfferId = s.OfferId,
//TotalAmount = s.TotalAmount.Replace("$", string.Empty).Replace(",", string.Empty).Trim()
TotalAmount = s.TotalAmount
})
//.Where(i => i.TotalAmount != null)
.ToList();
Step 2) Join the two lists on the Id of the Offer.
var data = offersConvert
.Join(sharedOffersConvert,
o => o.Id,
s => s.OfferId,
(o, s) => new { offersConvert = o, sharedOffersConvert = s })
.Select(o => new
{
Id = o.offersConvert.Id,
Created = o.offersConvert.Created,
Shares = o.offersConvert.Shares,
Redemptions = o.offersConvert.Redemptions,
Name = o.offersConvert.Name,
OfferId = o.sharedOffersConvert.OfferId,
TotalAmount = o.sharedOffersConvert.TotalAmount,
//GrandTotal = Convert.ToInt32(o.sharedOffersConvert.TotalAmount.Replace("$", string.Empty).Replace(",", string.Empty).Trim())
//GrandTotal = Convert.ToInt32(Convert.ToDouble(o.sharedOffersConvert.TotalAmount, CultureInfo.InvariantCulture))
})
//.Where(o => o.Id == o.OfferId)
.OrderBy(o => o.Created.Add(offset))
.ToList();
As you can tell, I've tried to remove any dollar signs and commas. I've even tried to trim white space in order to get clean data. I am then trying to convert the strings to int values, so I can sum them. Nothing seems to work. I've even tried .GroupBy and other methods (see below). At least with .GroupBy I can get to the .Sum operator. With the other method I run into issues when I can't convert int into ToList(), so I have to try and convert ToString().
.Where(o => o.Id == o.OfferId)
.GroupBy(g => g.Id)
.Select(x => new { GrandTotal = x.Sum(o => o.TotalAmount) })
ERROR in above: can't convert TotalAmount to decimal
.Where(i => i.Id == i.OfferId)
.Sum(i => Convert.ToInt32(Convert.ToDouble(i.TotalAmount, CultureInfo.InvariantCulture)
)).ToString()
Does anyone know how I can add/sum the string values in TotalAmount to get a GrandTotal per Offer id?
Any help us much appreciated. Thanks!
UPDATE: This works, but I really don't understand why and I don't think it is very clean. I really couldn't find many examples where people were joining two lists together and summing one of the columns. This seems pretty common to me, but perhaps it is not.
var data = (from o in offersConvert
join s in sharedOffersConvert on o.Id equals s.OfferId
orderby o.Created.Add(offset)
let k = new
{
Id = o.Id,
Name = o.Name,
Created = o.Created,
Shares = o.Shares,
Redemptions = o.Redemptions
}
group s by k into totals
select new
{
OfferId = totals.Key.Id,
Name = totals.Key.Name,
Created = totals.Key.Created,
Shares = totals.Key.Shares,
Id = totals.Key.Id,
Redemptions = totals.Key.Redemptions,
GrandTotal = totals.Sum((s => s.TotalAmount == null ? Decimal.Zero : Decimal.Parse(s.TotalAmount, NumberStyles.Currency)))
})
.ToList();
You can use
Decimal.TryParse("$10.00", NumberStyles.Currency, CultureInfo.CurrentCulture, out var res);
Or in the context of your LINQ,
GrandTotal = Decimal.Parse(o.sharedOffersConvert.TotalAmount, NumberStyles.Currency)
if o.sahredOffersConvert.TotalAmount may be null,
GrandTotal = (o.sharedOffersConvert.TotalAmount ==null) ? Decimal.Zero : Decimal.Parse(o.sharedOffersConvert.TotalAmount, NumberStyles.Currency)
I'm still puzzled why everyone lately has this odd fascination with the Lambda syntax. And that seems to have been only one of several means you've used to make this more complicated than necessary:
var data = (from o in offers
join s in sharedOffers on o.Id equals s.OfferId
orderby o.Created.Add(offset)
select new
{
Id = o.Id,
Created = o.Created,
Shares = o.Shares,
Redemptions = o.Redemptions,
Name = o.Name,
OfferId = o.OfferId,
TotalAmount = Decimal.Parse(o.TotalAmount, NumberStyles.Currency)
})
.ToList();
Further, as offset seems to be a constant (for the life of this query), adding it to Created isn't going to affect the ordering, and that bit can be removed.
And, since it appears your final output is just the grand totals, it can be reduced further to:
var data = (from o in offers
join s in sharedOffers on o.Id equals s.OfferId
orderby o.Created
group Decimal.Parse(o.TotalAmount, NumberStyles.Currency) by o.id into totals
select new
{
Id = totals.Key,
GrandAmount = totals.Sum()
})
.ToList();
UPDATE: Putting back what I took out... This should work (I don't have your tables, so I can't test it)
var data = (from o in offers
join s in sharedOffers on o.Id equals s.OfferId
orderby o.Created.Add
group o by o.Id into totals
let item = totals.First()
select new
{
Id = item.Id,
Created = item.Created,
Shares = item.Shares,
Redemptions = o.Redemptions,
Name = item.Name,
OfferId = item.OfferId,
GrandTotal = totals.Sum(t=>Decimal.Parse(t.TotalAmount, NumberStyles.Currency))
})
.ToList();

How can I fix linq query to select count of ids with group by?

I want to create this SQL query to linq:
SELECT
COUNT(m.FromUserId) AS Messages,
m.FromUserId AS UserId
FROM
dbo.ChatMessages m
INNER JOIN
dbo.ChatMessagesRead mr ON mr.ChatMessageId = m.ChatMessageId
WHERE
m.ToUserId = #toUserId
GROUP BY
m.FromUserId
I have tried create following linq query:
var messages = from m in _dbContext.ChatMessages
join mread in _dbContext.ChatMessagesRead on m.ChatMessageId equals mread.ChatMessageId
where m.ToUserId == userId
group m by m.FromUserId into g
select new
{
UserId = g.Key,
Messages = g.Count()
};
var messagesList = messages.ToList();
But this doesn't work.
How can I fix this linq query?
I get this exception:
Expression of type 'System.Func2[Microsoft.Data.Entity.Query.EntityQueryModelVisitor+TransparentIdentifier2[Project.BL.ChatMessages.ChatMessages,Project.BL.ChatMessages.ChatMessagesRead],System.Int32]' cannot be used for parameter of type 'System.Func2[<>f__AnonymousType12[Project.BL.ChatMessages.ChatMessages,Project.BL.ChatMessages.ChatMessagesRead],System.Int32]' of method 'System.Collections.Generic.IEnumerable1[System.Linq.IGrouping2[System.Int32,Project.BL.ChatMessages.ChatMessages]] _GroupBy[<>f__AnonymousType12,Int32,ChatMessages](System.Collections.Generic.IEnumerable1[<>f__AnonymousType12[Project.BL.ChatMessages.ChatMessages,Project.BL.ChatMessages.ChatMessagesRead]], System.Func2[<>f__AnonymousType12[Project.BL.ChatMessages.ChatMessages,Project.BL.ChatMessages.ChatMessagesRead],System.Int32], System.Func2[<>f__AnonymousType1`2[Project.BL.ChatMessages.ChatMessages,Project.BL.ChatMessages.ChatMessagesRead],Project.BL.ChatMessages.ChatMessages])'"
I'm facing the same issue and I've found that there is an opened issue on the Entity Framework Core bugtracker
The only workaround for now seems to split the request in two.
var filtered = (from m in _dbContext.ChatMessages
join mread in _dbContext.ChatMessagesRead on m.ChatMessageId equals mread.ChatMessageId
where m.ToUserId == userId
select m).ToList();
var messages = from m in filtered
group m by m.FromUserId into g
select new
{
UserId = g.Key,
Messages = g.Count()
};
you can try this
var res = ctx.MyTable // Start with your table
.GroupBy(r => r.id) / Group by the key of your choice
.Select( g => new {Id = g.Key, Count = g.Count()}) // Create an anonymous type w/results
.ToList(); // Convert the results to List
Your code should work. However I created another version of your query using extension methods.
var messages =
_dbContext
.ChatMessages
.Where(message => message.ToUserId == userId)
.Join(
_dbContext.ChatMessageRead,
message => message.ChatMessageId,
readMessage => readMessage.ChatMessageId,
(m, mr) => m.FromUserId
)
.GroupBy(id => id)
.Select(group =>
new
{
UserId = group.Key,
Messages = group.Count()
}
);
Could you please try it if it also throws the same exception or not?

Group by clause in Entity Framework or Micro ORM using lambda

How to use following query by using Lambda Expression (Entity Framework or Micro ORM)?
SELECT a.RestaurantID
,MAX(a.EventDate) LastPeriodCloseDate
FROM (
SELECT RestaurantID
,EventType
,EventDate
FROM SystemCalendar
WHERE EventType = 'P'
) a
GROUP BY RestaurantID
HAVING MAX(a.EventDate) BETWEEN '2014-10-31'
AND '2015-03-31'
Your inner select isn't necessary. You can just do this:
DateTime start = new DateTime(2014, 10, 31);
DateTime end = new DateTime(2015, 3, 31);
var query = _db.SystemCalendar
.Where(x => x.EventType == "P")
.GroupBy(x => x.RestaurantID,
x => new
{
RestaurantID = x.Key,
LastPeriodCloseDate = x.Max(y => y.EventDate)
})
.Where(x => x.LastPeriodCloseDate >= start &&
x.LastPeriodCloseDate < end);