根据多列键更新多条记录

时间:2018-06-25 15:37:35

标签: c# linq linqpad

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

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(???));

这有可能吗?

4 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

应该是这样的:

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

答案 2 :(得分:0)

注意:这可行,但是效率很低。离开此答案是因为它“有效”,但希望较新的答案更好。

这是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; }
}

答案 3 :(得分:0)

我希望这是最终答案。它行之有效,唯一的低效率是它会选择比原来多的几行,但我愿意忍受。

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; }
}