首页 > 解决方案 > 如何在 EF Core 中实现这种“通过之间的连接”SQL?

问题描述

我有这个 SQL:

SELECT `o`.*,
    (SELECT ROUND(`o`.`norm` * `sr`.`multiplier`, 2)) AS `time`,
    (SELECT ROUND(`c`.`rate` * `sr`.`multiplier`, 2)) AS `tariff`
FROM `operations` AS `o`
INNER JOIN `plan_structures` AS `ps` ON `ps`.`id` = `o`.`plan_structure_id`
INNER JOIN `plans` AS `pl` ON `pl`.`id` = `ps`.`plan_id`
LEFT JOIN `surcharges` AS `sr` ON `pl`.`pairs` BETWEEN `sr`.`pairs_min` AND `sr`.`pairs_max`
LEFT JOIN `class_rates` AS `c` ON `c`.`class` = `o`.`class`

如何在 EF Core 中实现这一点?

  1. 我的第一个想法是使用 DB 视图 - 这很有效,但后来我必须拥有 EF 中实体关系的所有相关表的视图。我无法将Operation和链接OperationViewPlanStructure
  2. 我的第二个想法是“以某种方式”BETWEEN在 SQL 中加入它,但我发现这是不可能的(或者是吗?)
  3. 虚拟列 - 在独立表之间也是不可能的。

那么...除了数据库视图还有其他方法吗?

我的实体:

public class Plan
{
    public Guid Id { get; set; }
    public int Pairs { get; set; }

    public ICollection<PlanStructure> PlanStructures { get; set; }
}

public class PlanStructure
{
    public Guid Id { get; set; }

    public Plan Plan { get; set; }
    public ICollection<Operation> Operations { get; set; }
}

public class Operation
{
    public Guid Id { get; set; }
    public int Class { get; set; }
    public int Norm { get; set; }
    public double Time { get; } // computed somehow
    public double Tariff { get; } // computed somehow

    public PlanStructure PlanStructure { get; set; }
}

public class ClassRate
{
    public int Class { get; set; }
    public double Rate { get; set; }
}

public class Surcharge
{
    public int PairsMin { get; set; }
    public int PairsMax { get; set; }
    public double Multiplier { get; set; }
}

编辑:这在Operations包含时也应该起作用 - 当我这样做时context.Plans.Include(p => p.PlanStructures).ThenInclude(s => s.Operations);,所有这些操作都应该具有那些计算的属性。

标签: c#entity-frameworklinqentity-framework-core

解决方案


LEFT JOIN 可以表示为Where(...).DefaultIfEmpty()。还Math.Round没有翻译,所以计算是在客户端完成的。

var query = 
    from o in ctx.Operations
    from sr in ctx.Surcharges
        .Where(sr => o.PlanStructure.Plan.Pairs >= sr.PairsMin && o.PlanStructure.Plan.Pairs <= sr.PairsMax)
        .DefaultIfempty()
    join c in ctx.ClassRates on o.Class equals c.Class into gj
    from c in gj.DefaultIfempty()
    select new 
    {
        Operation = o,
        Rate = (double?)c.Rate,
        Multiplier = (double?)sr.Multiplier
    };

var result = query.AsEnumerable()
    .Select(e => new 
    {
        e.Operation,
        time = e.Multiplier == null
                ? (double?) null 
                : Math.Round(e.Multiplier.Value * e.Operation.Norm),
        tariff =  e.Multiplier == null == null || e.Rate == null
                ? (double?) null
                : Math.Round(e.Multiplier.Value * e.Rate.Value)
    });

推荐阅读