【问题标题】:How do I translate this specifik SQL statement to LINQ如何将此特定 SQL 语句转换为 LINQ
【发布时间】:2021-12-23 16:59:01
【问题描述】:

SQL:

SELECT
  monitors.monitor_name,
  alltime       = ISNULL(SUM(count), 0),
  today         = ISNULL(SUM(CASE WHEN time_stamp >= CAST(CAST(GETUTCDATE() AS date) AS datetime)
                                   AND time_stamp < CAST(DATEADD(day, 1, CAST(GETUTCDATE() AS date)) AS datetime)
                                  THEN count END), 0),
  current_hour  = ISNULL(SUM(CASE WHEN time_stamp >= DATEADD(hour, DATEDIFF(hour, '20000101', GETUTCDATE()), '20000101')
                                   AND time_stamp <  DATEADD(hour, DATEDIFF(hour, '20000101', GETUTCDATE()) + 1, '20000101')
                                  THEN count END), 0)
FROM CollectorOptions monitors
JOIN CollectorCounters counters ON counters.monitor_name= monitors.monitor_name
WHERE monitors.is_active = 1
GROUP BY
  monitors.monitor_name;

C# 对象:

监控

class Monitor {

public string monitor_name{ get; set;}
public bool is_active {get;set;}
}

计数器

public class Counter{

public string monitor_name { get; set;}
public DateTimeOffset time_stamp { get; set;}
public int count { get; set;}
}

而我需要把它变成的对象是:

所有统计数据

[Keyless]
public class AllStatistics{

 public string monitor_name {get;set;}
 public int alltime {get;set;}
 public int today {get;set;}
 public int current_hour {get;set;}
}

我没有太多使用 LINQ 的经验,所以我用它取得的最大成就是:

var list = (from MyDbContext.Monitors
            select monitor_name).Where(monitor => monitor.is_active != 0).ToList();

我的问题是,如何编写具有与上述 SQL 查询相同功能的 LINQ 语句?

【问题讨论】:

  • 我觉得使用 EF 扩展(我习惯于 EF 核心,不确定您使用的是什么 EF)可以使用 dateadd 函数:docs.microsoft.com/en-us/ef/core/providers/sql-server/… 但是我只使用无键实体这是因为查询似乎足够复杂
  • 我也在使用 EF Core,我已经用 [Keyless] 注释更新了类。

标签: c# sql .net linq


【解决方案1】:

看来你需要这样的东西

var list = (
    from monitor in MyDbContext.Monitors
    where monitor.is_active != 0
    select new {
        monitor.MonitorName,
        counters = MyDbContext.CollectorCounters.Where(counter => monitor.monitor_name  == counter.monitor_name)
    } into mc
    select new {
        mc.MonitorName,
        alltime = mc.Sum(c => c.count),
        today = mc.counters.Where(c =>
            c.time_stamp >= DateTime.UtcNow.Date &&
            c.time_stamp < DateTime.UtcNow.Date.AddDays(1)
          ).Sum(c => c.count),
        current_hour = mc.counters.Where(c =>
            c.time_stamp >=
             new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0) &&
            c.time_stamp < 
             new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, DateTime.UtcNow.Hour, 0, 0).AddHours(1)
          ).Sum(c => c.count),
    }
).ToList();

【讨论】:

  • 我认为你在某处遗漏了一些东西,但你几乎得到了它! counters 无法识别
  • 我认为现在应该可以工作
  • 我认为它现在有效,我会尝试它,但我还有最后一个问题。如果我想将检索到的数据放入具有 4 个属性的对象中并将它们放入列表中怎么办?
  • 那么你可以使用select new MyObject { MonitorName = mc.monitorName......}).ToList()
【解决方案2】:

一种方法是使用无钥匙实体:

https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations

MyKeylessEntity.cs

[Keyless]
public class MyKeylessEntity{ ... } 

MyDbContext.cs

public DbSet<MyKeylessEntity> MyKeylessEntity{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<MyKeylessEntity>(e =>
    {
            e.HasNoKey()
                .ToSqlQuery(@"

                            ")
                .ToView(null); // Workaround for EF core migrations b.u.g => fixed in net core 5
        });

// ...

所以大致如下:

[Keyless]
public class AllStatistics
{
 public string monitor_name {get;set;}
 public int alltime {get;set;}
 public int today {get;set;}
 public int current_hour {get;set;}
}

public DbSet<AllStatistics> AllStatistics{ get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<AllStatistics>(e =>
    {
        e.HasNoKey()
            .ToSqlQuery(@"
                        SELECT
                            monitors.monitor_name,
                            alltime       = ISNULL(SUM(count), 0),
                            today         = ISNULL(SUM(CASE WHEN time_stamp >= CAST(CAST(GETUTCDATE() AS date) AS datetime)
                                                    AND time_stamp < CAST(DATEADD(day, 1, CAST(GETUTCDATE() AS date)) AS datetime)
                                                    THEN count END), 0),
                            current_hour  = ISNULL(SUM(CASE WHEN time_stamp >= DATEADD(hour, DATEDIFF(hour, '20000101', GETUTCDATE()), '20000101')
                                                    AND time_stamp <  DATEADD(hour, DATEDIFF(hour, '20000101', GETUTCDATE()) + 1, '20000101')
                                                    THEN count END), 0)
                        FROM 
                            CollectorOptions monitors
                        JOIN CollectorCounters counters ON counters.monitor_name= monitors.monitor_name
                        WHERE monitors.is_active = 1
                        GROUP BY monitors.monitor_name;
                        ")
            .ToView(null); // Workaround for EF core migrations b.u.g => fixed in net core 5
    });

【讨论】:

  • 我需要将 modelBuilder.Entyty... 插入到 DBContext 内的 OnModelCreating(ModelBuilder builder) 方法中,对吗?我应该如何在 MyKeyLessEntity 类中命名我的属性?
  • 是的。您应该将属性命名为与查询相同。您还需要兼容的数据类型
  • 在我的 sql 查询中,我通常使用别名来确保我的 dto 和查询使用相同的名称,因此 monitors.monitor_name as MonitorNamepublic string MonitorName {get;set;}。另外你的 c# 看起来很糟糕,看看 docs.microsoft.com/en-us/dotnet/csharp/fundamentals/…
  • 我知道错误的代码约定(在属性中)我只是不确定它是否会正确检测到属性的名称
  • 虽然不是真的“将此 sql 转换为 linq”.. 如果要保留此路由,则问题标题可能需要编辑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 2015-04-12
  • 2021-12-17
相关资源
最近更新 更多