Обновление нескольких записей на основе ключа из нескольких столбцов

Мне нужно обновить несколько записей на основе списка записей для изменения. Когда этот список идентифицируется одним столбцом, сделать это просто:

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 ответа

Решение

Запрос ниже выполнит работу. Если для запроса необходима высокая производительность, вы можете использовать хранимую процедуру с табличным параметром.

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

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

Это должно быть что-то вроде этого:

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

Примечание: это работает, но ужасно неэффективно. Оставив этот ответ, потому что он "работает", но, надеюсь, более новый ответ лучше.

Вот рабочий код 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; }
}

Я надеюсь, что это окончательный ответ. Это работает, и его единственная неэффективность заключается в том, что изначально он выберет на несколько строк больше, чем необходимо, но я готов с этим смириться.

Код 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; }
}
Другие вопросы по тегам