【问题标题】:Update Multiple Records Based on Multi-column Key基于多列键更新多条记录
【发布时间】:2018-06-25 15:37:35
【问题描述】:

我需要根据要更改的记录列表更新多条记录。当该列表由一列标识时,这样做很简单:

var fooIds = new List<int> { 2, 3, 4}
var foosToChange = Foo.Where(f => fooIds.Contains(f.Id));
// Update foosToChange and save here

当传入列表是一个具有两个属性的对象时,会发生什么情况?例如:

var fooIds = new []
  {
    new { prop1 = "12345", prop2 = 2017111701 },
    new { prop1 = "hij", prop2 = 2018060101 }
  };

这需要变成什么?

var foosToChange = Foo.Where(f => fooIds.Contains(???));

这可能吗?

【问题讨论】:

  • 我想我知道你的意思,但我认为这不会奏效。如果您确实包含在第一个列表中,那么在第二个列表中,您可以接受任何属性组合,而您实际上只能接受特定的 prop1 + prop2 组合。
  • stackoverflow.com/a/2381086/1350913这可能有帮助吗?
  • 您必须分 3 步完成。使用 where+any 过滤记录(参见下面的答案),从 EF 中具体化记录,然后执行内存连接,以便更新具体化记录,最后将更改持久化。
  • 我通过执行以下操作获得结果:var foos = from foo in foos join fooDb in Foo on foo.processId equals fooDb.ProcessId select fooDb; 那是因为您无法将本地连接到 SQL,但您可以根据此答案执行相反操作:stackoverflow.com/a/26966037/279516跨度>

标签: c# linq linqpad


【解决方案1】:

下面的查询将完成工作。如果查询需要高性能,您可以考虑使用带有Table Valued Parameter 的存储过程。

var keys = fooIds.Select(f => f.prop1 + "." + f.prop2);

Foo.Where(f => keys.Contains(f.prop1 + "." + f.prop2))

【讨论】:

    【解决方案2】:

    应该是这样的:

    var foosToChange = Foo.Where(f => fooIds.Any(x => x.prop1 == f.Id1 && x.prop2 == f.Id2));
    

    例子:

            A[] Foo = new A[]{ new A{ Id1 = "12345", Id2 = "2017111701" }, new A { Id1 = "fakeid", Id2 = "2017111701" } };
    
            var res = Foo.Where(f => fooIds.Any(x => x.prop1 == f.Id1 && x.prop2 == f.Id2));
    
            // res will return first element
    

    【讨论】:

    • 示例不是您的用例吗? res 包含您要更新的元素。
    • 我现在正在尝试。我正在解决这个错误:Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.
    • 哦,等一下。您正在对Foo 执行Where()。我假设那是你的模拟数据。如果是这样,那么您所做的一切都在内存中,并且有效。但是在生成 SQL 时它不起作用。好吧,至少不是 LINQ to SQL,这是 LinqPad 默认使用的。我需要让它与 EF 一起工作,看看它是否可以在那里工作......
    【解决方案3】:

    我希望这是最终的答案。它可以工作,唯一的低效是它会选择比最初需要的多几行,但我愿意接受。

    LinqPad 代码:

    void Main()
    {
        var payrolls = new List<PayrollKey>
          {
            new PayrollKey { CompanyId = "12345", ProcessId = 2017111701 },
            new PayrollKey { CompanyId = "hij", ProcessId = 2018060101 }
          };    
    
        // Store just the companyIds from the incoming list
        var companyIds = payrolls.Select(x => x.CompanyId);
    
        // From the DB, get all the foos for the companies in the list.
        // We will be getting rows for all processIds for a company, but that's ok because:
        // A) I'm guessing that's not super common, and B) they'll be filtered-out in the next query.
        var allFoosForCompanies =
          from foo in Foo
          where foo.Status == "Open" && companyIds.Contains(foo.CompanyId)
          select foo;
    
        // Now, from the two in-memory lists, get only the foos we care about
        // (having the correct processId).
        var foosToChange =
          from payroll in payrolls
          join foo in allFoosForCompanies on
            new { CompanyId = payroll.CompanyId, ProcessId = payroll.ProcessId }
            equals 
            new { CompanyId = foo.CompanyId, ProcessId = foo.ProcessId }
          where foo.Status == "Open"
          select foo;
    
        // Now make the change and save.
        foreach(var foo in foosToChange)
        {
          foo.Status = "Sent";
        }
        SubmitChanges();
    }
     
    public class PayrollKey
    {
      public string CompanyId { get; set; }
      public int ProcessId { get; set; }
    }
    

    【讨论】:

      【解决方案4】:

      注意:这可行,但效率极低。留下这个答案是因为它“有效”,但希望新的答案更好。

      这是 LinqPad 的工作代码:

      void Main()
      {
          var fooToChange = new List<FooClass>
            {
              new FooClass { CompanyId = "12345", ProcessId = 2017111701 },
              new FooClass { CompanyId = "hij", ProcessId = 2018060101 }
            };    
      
          var foos =
            from foo in fooToChange
            join fooDb in Foo on
              new { CompanyId = foo.CompanyId, ProcessId = foo.ProcessId }
              equals 
              new { CompanyId = fooDb.CompanyId, ProcessId = fooDb.ProcessId }
            select fooDb;
      
          foreach(var foo in foos)
          {
            foo.Status = "Sent";
          }
      
          SubmitChanges();
      }
      
      public class FooClass
      {
        public string CompanyId { get; set; }
        public int ProcessId { get; set; }
      }
      

      【讨论】:

      • 这不是有效地执行select * from foo 然后它在内存中执行连接吗?如果 foo 是一个大表,性能会很糟糕。
      猜你喜欢
      • 2015-02-15
      • 2014-09-01
      • 1970-01-01
      • 2016-05-01
      • 2013-05-02
      • 2014-03-04
      • 1970-01-01
      • 2019-02-18
      • 1970-01-01
      相关资源
      最近更新 更多