Обновление нескольких записей на основе ключа из нескольких столбцов
Мне нужно обновить несколько записей на основе списка записей для изменения. Когда этот список идентифицируется одним столбцом, сделать это просто:
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; }
}