//https://stackoverflow.com/questions/63992874/ef-core-group-by-month-and-year-with-utc-dates
public static ReturnType CustomMethod(ArgType arg) => throw new NotImplementedException();
protected override void OnModelCreating(ModelBuilder builder){
builder.HasDbFunction(typeof(Context).GetMethod(nameof(CustomMethod)));
}
public static DateTimeOffset ToTimeZone(this DateTimeOffset value, string name) => throw new NotImplementedException();
public class SqlFragmentListExpression : SqlExpression
{
public SqlFragmentListExpression(Type type, RelationalTypeMapping typeMapping, params SqlExpression[] fragments) : base(type, typeMapping)
{
Fragments = fragments;
}
public SqlExpression[] Fragments { get; }
public override void Print(ExpressionPrinter expressionPrinter)
{
foreach (var f in Fragments)
f.Print(expressionPrinter);
}
protected override Expression VisitChildren(ExpressionVisitor visitor)
{
var frags = new SqlExpression[Fragments.Length];
var changed = false;
for(var i = 0; i < Fragments.Length; i++)
{
frags[i] = (SqlExpression)visitor.Visit(Fragments[i]);
changed |= frags[i] != Fragments[i];
}
return changed ? new SqlFragmentListExpression(Type, TypeMapping, frags) : this;
}
}
// OnModelCreating
builder
.HasDbFunction(typeof(Extensions).GetMethod(nameof(Extensions.ToTimeZone)))
.HasTranslation(args => {
var dto = args.ElementAt(0);
return new SqlFragmentListExpression(dto.Type, dto.TypeMapping,
dto,
new SqlFragmentExpression(" AT TIME ZONE "),
args.ElementAt(1));
});
// use it like
_dbContext.Tickets
.Where(x => x.Date >= from && x.Date <= to)
.Select(x => new {
Date = x.Date.ToTimeZone("Central European Standard Time")
})
.GroupBy(x => new {
Year = x.Date.Year,
Month = x.Date.Month
},
(x, e) => new {
x.Year,
x.Month,
Count = e.Count()
})