Linq to find lasted record in group - c#

I want add a new column to find which is lasted record in group.
Can I write subquery in Select() method?
I have try this
var test = DailyPeriods.Where(x => x.BookingDate == "2016/12/30")
.Select(x =>
new
{
PERIOD_GROUP_ID = x.PeriodGroupID,
PERIOD_NAME = x.PeriodName,
New_Column = DailyPeriods
.Where(z => z.BookingDate == "2016/12/30")
.Select(a =>
new
{
PeriodGroupID = a.PeriodGroupID,
period_name = a.PeriodName
}
)
.GroupBy(b => b.period_name)
.Select(g => g.Last().PeriodGroupID)
.Contains(x.PeriodName)
})
But will occur this error
"column not in scope: A:2211708.C(BOOKING_DATE)"

Try this..
var lastRecords = periodList.GroupBy(l => l.PeriodName)
.Select(x => new { PeriodName = x.Key,
PeriodGroupId = x.OrderBy(l => l.PeriodGroupId).Last().PeriodGroupId});
var result = from period in periodList
from lastRec in lastRecords.Where(r => r.PeriodGroupId == period.PeriodGroupId
&& r.PeriodName == period.PeriodName)
.DefaultIfEmpty()
select new { period.PeriodGroupId,period.PeriodName, New_Column=lastRec==null?false:true };

Related

Duplicated linq query

I have 2 almost identical linq queries and want to remove repeating code from it. The only difference is the extra property in the GroupBy depending on some true/false condition.
How can I conditionally group by in linq without repeating the code like below?
var allergensList = _context.RecipeAllergens
.Where(x => x.ParentId == Id && x.AllergenId != null)
.ToList();
var allergens = new List<AllergenInfo>();
if (isRecipe)
{
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon, x.AllergenMaycontains })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();
}
else
{
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();
}
You can left grouping by x.AllergenMaycontains but under condition
allergens = allergensList
.GroupBy(x => new { x.AllergenName, x.AllergenIcon, AllergenMaycontains = isRecipe ? x.AllergenMaycontains : false })
.Select(a =>
{
var v = a.OrderBy(x => x.AllergenMaycontains).First();
return new AllergenInfo
{
AllergenName = v.AllergenName,
AllergenIcon = v.AllergenIcon,
AllergenMayContain = v.AllergenMaycontains ?? false
};
})
.ToList();

Linq-to-SQL query (AsEnumerable) on multiple tables

Here's a Linq-to-SQL query that uses only one table from my SQL Server database and works perfectly:
private void GetData()
{
DateTime d = DateTime.Now;
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var qte = dc.ENTREES_STOCKS.AsEnumerable()
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
.Select(s => new
{
art = s.ART_CODE,
date = s.ENTSTK_DTENTREE,
numLot = s.ENTSTK_LOT,
pnet = s.ENTSTK_PNET,
nbu = s.ENTSTK_NBU
})
.GroupBy(g => new { g.art, g.date, g.numLot })
.Select(n => new
{
n.Key.art,
n.Key.date,
n.Key.numLot,
pnet = n.Sum(x => Math.Round(Convert.ToDecimal(x.pnet), 2)),
nbu = n.Sum(x => Math.Round(Convert.ToDecimal(x.nbu), 2)),
});
QEntreeTB.Text = qte.First().pnet.ToString();
NbuEntreeTB.Text = qte.First().nbu.ToString();
}
}
How could I modify this code to join other tables to this query like :
private void GetData()
{
DateTime d = DateTime.Now;
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var qte = dc.ENTREES_STOCKS.AsEnumerable()
// Thoseline of codes of course doesn't work
join art in dc.FICHES_ARTICLES on ENTREES_STOCKS.ART_CODE equals art.ART_CODE
join ent in dc.STK_ENT on art.ART_CODE equals ent.ART_CODE
....
//
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
.Select(s =>
new
{
art = s.ART_CODE,
date = s.ENTSTK_DTENTREE,
numLot = s.ENTSTK_LOT,
pnet = s.ENTSTK_PNET,
nbu = s.ENTSTK_NBU
}
)
.GroupBy(g => new { g.art, g.date, g.numLot })
.Select(n =>
new
{
n.Key.art,
n.Key.date,
n.Key.numLot,
pnet = n.Sum(x => Math.Round(Convert.ToDecimal(x.pnet), 2)),
nbu = n.Sum(x => Math.Round(Convert.ToDecimal(x.nbu), 2)),
}
);
QEntreeTB.Text = qte.First().pnet.ToString();
NbuEntreeTB.Text = qte.First().nbu.ToString();
}
}
Or is there à way to code this query another way ??
Because in fact i just want to join multiples tables, groupby some fields and sum others fields.
Firstly, calling AsEnumerable is a bit redundent. Then you can simply use the Join extension method.
var qte = dc.ENTREES_STOCKS
.JOIN(dc.FICHES_ARTICLES,art=>art.ART_CODE, stock => stock.ART_CODE)
.JOIN(dc.STK_ENT,ent => ent.ART_CODE,stock => stock.ART_CODE)
.Where(x => x.ENTSTK_LOT == lot)
.Where(x => x.ART_CODE == artCode)
.Where(x => x.ENTSTK_USER == null)
....
You can find more answers here:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/method-based-query-syntax-examples-join-operators

Linq Group By & Sum Query On Multiple Columns

var fpslist = db.FPSinformations.Where(x => x.Godown_Code != null && x.Godown_Code == godownid).ToList();
var data1 = fpslist.GroupBy(x => x.Ration_Card_Type1)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count1)
}).ToList();
var data2 = fpslist.GroupBy(x => x.Ration_Card_Type2)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count2)
}).ToList();
var data3 = fpslist.GroupBy(x => x.Ration_Card_Type3)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count3)
}).ToList();
var data4 = fpslist.GroupBy(x => x.Ration_Card_Type4)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count4)
}).ToList();
var data5 = fpslist.GroupBy(x => x.Ration_Card_Type5)
.Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count5)
}).ToList();
var GodownRCCount = data1.Where(x => x.CardType_Name != null).ToList();
var GodownRCCounts = GodownRCCount;
GodownRCCount = data2.Where(x => x.CardType_Name != null).ToList();
GodownRCCounts.AddRange(GodownRCCount);
GodownRCCount = data3.Where(x => x.CardType_Name != null).ToList();
GodownRCCounts.AddRange(GodownRCCount);
GodownRCCount = data4.Where(x => x.CardType_Name != null).ToList();
GodownRCCounts.AddRange(GodownRCCount);
GodownRCCount = data5.Where(x => x.CardType_Name != null).ToList();
GodownRCCounts.AddRange(GodownRCCount);
I have 10 Columns in My Database Like
Ration_Card_Type1
Ration_card_count1
Ration_Card_Type2
Ration_card_count2
Ration_Card_Type3
Ration_card_count3
Ration_Card_Type4
Ration_card_count4
Ration_Card_Type5
Ration_card_count5
Now What I want is to get the sum of Ration_Card_Counts and Its Type from its Type
Expected Output :
CardType_Name
CardType_Count
Well the above code works fine but I want to optimize it in max possible way as this will be inside a Loop and there are about 1.5 million records.
Thanks
Union should operate faster than AddRange!
you can try the following:
var data = (from g in fpslist.GroupBy(x => x.Ration_Card_Type1).Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count1)
}).Union(
fpslist.GroupBy(x => x.Ration_Card_Type2).Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count2)
})).Union(
fpslist.GroupBy(x => x.Ration_Card_Type3).Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count3)
})).Union(
fpslist.GroupBy(x => x.Ration_Card_Type4).Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count4)
})).Union(
fpslist.GroupBy(x => x.Ration_Card_Type5).Select(x => new
{
CardType_Name = x.Key,
CardType_Count = x.Sum(y => y.Ration_Card_Count5)
}))
select g).ToList();
In general from grouping I would go with Thomas!
Database grouping is far better for me, since you are fetching the data required aggregated and therefore the data transferred over the network if much less!
You can rewrite the same query using SQL and put some indexes(performance) :
SELECT Ration_Card_Type = Ration_Card_Type1, Ration_Card_Count = sum(Ration_card_count1)
FROM
FPSinformations
GROUP BY
Ration_Card_Type1
UNION
SELECT Ration_Card_Type = Ration_Card_Type2, Ration_Card_Count = sum(Ration_card_count2)
FROM
FPSinformations
GROUP BY
Ration_Card_Type2
UNION
SELECT Ration_Card_Type = Ration_Card_Type3, Ration_Card_Count = sum(Ration_card_count3)
FROM
FPSinformations
GROUP BY
Ration_Card_Type3
UNION
SELECT Ration_Card_Type = Ration_Card_Type4, Ration_Card_Count = sum(Ration_card_count4)
FROM
FPSinformations
GROUP BY
Ration_Card_Type4
UNION
SELECT Ration_Card_Type = Ration_Card_Type5, Ration_Card_Count = sum(Ration_card_count5)
FROM
FPSinformations
GROUP BY
Ration_Card_Type5
I am not sure but this query make me think about UNPIVOT maybe you can investigate in this direction too.

LINQ query to dictionary

I'm trying to convert below LINQ query result into dictionary
var browser = (from tbf in context.tblFeedBacks
where tbf.email == dboard.userEmail
select tbf).GroupBy(l => l.browser)
.Select(g => new
{
browser = g.Key,
count = g.Select(l => l.browser).Distinct().Count()
});
It gives me a compilation error.
var browser = (from tbf in context.tblFeedBacks
where tbf.email == dboard.userEmail
select tbf).GroupBy(l => l.browser)
.Select(g => new
{
browser = g.Key,
count = g.Select(l => l.browser).Distinct().Count()
}).ToDictionary<string, double>(x => x.browser,y=>y.count);
Instance argument: cannot convert from
'System.Linq.IQueryable' to
'System.Linq.ParallelQuery'
got it working.
var browser = (from tbf in context.tblFeedBacks
where tbf.email == dboard.userEmail
select tbf).GroupBy(l => l.browser)
.Select(g => new
{
browser = g.Key,
count = g.Select(l => l.browser).Count()
}).ToDictionary(x => x.browser, x => x.count);

Is it possible to combine these 2 linq queries?

I'm using entity framework to return my data.
I have 2 almost identical methods/queries. The only difference is that one has an additional Where statement.
The first query gets the average value of accepted transactions, and the second query gets the average of all transactions.
Here are the 2 methods:
static IEnumerable<BuyerEarning> GetBuyerEPA()
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
return r.Find()
.Where(x =>
x.ResultTypeId == (int)MatchResultType.Accepted &&
x.CreatedOn <= collectTo &&
x.CreatedOn >= collectFrom)
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.ToList()
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission)))
.ToList();
}
}
static IEnumerable<BuyerEarning> GetBuyerEPL()
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
return r.Find()
.Where(x =>
x.CreatedOn <= collectTo &&
x.CreatedOn >= collectFrom)
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.ToList()
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission)))
.ToList();
}
}
Rather than have 2 distinct queries I'd like to use 1 that returns BuyerId, TreeId, TierId, EpaValue, EplValue. Is this possible and if so, how?
While most of the other answers are showing you how to use the same code to toggle the query between EPL and EPA, I believe what you are asking is how to get both values in a single query.
static IEnumerable<BuyerEarning> GetBuyerEPA()
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
return r.Find()
.Where(x =>
x.CreatedOn <= collectTo &&
x.CreatedOn >= collectFrom)
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission), //EPL
x.Where(y => y.ResultTypeId == (int)MatchResultType.Accepted)
.Average(y => y.Commission)) //EPA
.ToList();
}
}
You can create helper method with a predicate as parameter:
static IEnumerable<BuyerEarning> GetXXXX(Func<MatchHistory, bool> predicate = null)
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
var filtered = r.Find()
.Where(x.CreatedOn <= collectTo && x.CreatedOn >= collectFrom);
if(predicate != null)
filtered = filtered.Where(predicate);
return filtered
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.ToList()
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission)))
.ToList();
}
}
And then use it within each of the original methods:
static IEnumerable<BuyerEarning> GetBuyerEPA()
{
return GetXXXX(x => x.ResultTypeId == (int)MatchResultType.Accepted);
}
static IEnumerable<BuyerEarning> GetBuyerEPL()
{
return GetXXXX();
}
Btw., why do you use ToList() before final projection? It makes the Average calculation being performed by application (using LINQ to Objects), which is much less efficient then when done by SQL Server.
Why not refactor the Where clause and pass it in as a variable?
var epaFilter = new Func<MatchHistory, bool>(x => x.ResultTypeId == (int)MatchResultType.Accepted && x.CreatedOn <= collectTo && x.CreatedOn >= collectFrom);
var eplFilter = new Func<MatchHistory, bool>(x => x.CreatedOn <= collectTo && x.CreatedOn >= collectFrom);
private static IEnumerable<MatchHistory> GetBuyerByFilter(Func<MatchHistory,Boolean> filter)
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
return r.Find()
.Where(filter)
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.ToList()
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission)))
.ToList();
}
}
Seems like you want this:
static IEnumerable<BuyerEarning> GetBuyer(bool acceptedOnly)
{
var collectTo = DateTime.Now.AddDays(-1).DayEnd();
var collectFrom = collectTo.AddDays(-29).DayStart();
using (var uow = new UnitOfWork(ConnectionString.PaydayLenders))
{
var r = new Repository<MatchHistory>(uow.Context);
IQueryable<MatchHistory> results = r.Find()
.Where(x =>
x.CreatedOn <= collectTo &&
x.CreatedOn >= collectFrom);
if (acceptedOnly)
{
results = results
.Where(x => x.ResultTypeId == (int)MatchResultType.Accepted);
}
return results
.GroupBy(x => new
{
x.BuyerId,
x.TreeId,
x.TierId
})
.ToList()
.Select(x => new BuyerEarning(
x.Key.BuyerId,
x.Key.TreeId,
x.Key.TierId,
x.Average(y => y.Commission)))
.ToList();
}
}

Categories

Resources