Во-первых, когда я получаю актив (или активы) из модели, я хочу получить соответствующие страны, с которыми он связан.Затем я хотел бы иметь возможность привязать список стран к IEnumerable.
Не уверен, правильно ли я понимаю, но EntityCollection<T>
реализует IEnumerable<T>
, поэтому вам не нужно делатьЧто-нибудь особенное, вы можете просто использовать Asset.Countries
после загрузки активов, включая страны.
Во-вторых, я хочу иметь возможность выбрать список стран, в которых AssetId == какое-то значение.
using (var context = CreateAssetContext())
{
var countries = context.Countries
.Where(c => c.Assets.Any(a => a.AssetId == givenAssetId))
.ToList();
}
Или:
using (var context = CreateAssetContext())
{
var countries = context.Assets
.Where(a => a.AssetId == givenAssetId)
.Select(a => a.Countries)
.SingleOrDefault();
}
Второй вариант в порядке (не уверен, что он лучше, чем первый с точки зрения SQL), поскольку AssetId
является первичным ключом, поэтомуможет быть только один актив.Для запросов по другим критериям, например Asset.Name == "XYZ"
, где можно ожидать более одного актива, я бы предпочел первый вариант.Для второго вам пришлось заменить Select
на SelectMany
и SingleOrDefault
на ToList
и использовать Distinct
, чтобы отфильтровать возможные дублированные страны.SQL, вероятно, будет более сложным.
Наконец, я хочу иметь возможность обновить список стран для данного Актива.
Это сложнее, потому что вам нужночтобы разобраться со случаями: 1) страна была добавлена к активу, 2) страна была удалена из актива, 3) страна уже связана с активом.
Скажем, у вас есть список идентификаторов страны (IEnumerable<int> countryIds
) и вы хотите связать эти страны с данным активом:
using (var context = CreateAssetContext())
{
var asset = context.Assets.Include("Countries")
.Where(a => a.AssetId == givenAssetId)
.SingleOrDefault();
if (asset != null)
{
foreach (var country in asset.Countries.ToList())
{
// Check if existing country is one of the countries in id list:
if (!countryIds.Contains(country.Id))
{
// Relationship to Country has been deleted
// Remove from asset's country collection
asset.Countries.Remove(country);
}
}
foreach (var id in countryIds)
{
// Check if country with id is already assigned to asset:
if (!asset.Countries.Any(c => c.CountryId == id))
{
// No:
// Then create "stub" object with id and attach to context
var country = new Country { CountryId = id };
context.Countries.Attach(country);
// Then add to the asset's country collection
asset.Countries.Add(country);
}
// Yes: Do nothing
}
context.SaveChanges();
}
}
Редактировать
Для цены второго обхода в базу данных вы, вероятно, можете использовать этоболее простой код:
using (var context = CreateAssetContext())
{
var asset = context.Assets.Include("Countries")
.Where(a => a.AssetId == givenAssetId)
.SingleOrDefault();
if (asset != null)
{
// second DB roundtrip
var countries = context.Countries
.Where(c => countryIds.Contains(c.CountryId))
.ToList();
asset.Countries = countries;
context.SaveChanges();
}
}
Обнаружение изменений EF должно распознавать, какие страны были добавлены или удалены из списка стран актива.Я не уверен на 100%, если последний код будет работать правильно.