在Entity Framework 4中选择和更新多对多

Cra*_*gly 4 entity-framework entity-framework-4

在此输入图像描述

我是EF的新手,并且上面有以下实体模型,其中包括资产和国家.资产可以属于许多国家/地区,因此与国家/地区具有多对多关系(数据库中具有连接表,两个字段都作为主键).

我希望能够做到以下几点:

首先,当我从模型中检索资产(或资产)时,我希望获得与其关联的各个国家/地区.然后,我希望能够将国家/地区列表绑定到IEnumerable.以这种方式检索国家为我提供了一个国家对象的EntityCollection,它具有ToList()的扩展方法.因此不确定如果我正沿着正确的大道走下去.这是我的GetAll方法:

public IEnumerable<Asset> GetAll()
{
    using (var context = CreateAssetContext())
    {
        var assetEntities = context.Assets.Include("Countries").ToList();
        return AssetMapper.FromEntityObjects(assetEntities);
    }
}
Run Code Online (Sandbox Code Playgroud)

其次,我希望能够选择AssetId ==某个值的国家/地区列表.

最后,我希望能够更新给定资产的国家/地区列表.

非常感谢.

Sla*_*uma 6

首先,当我从模型中检索资产(或资产)时,我希望获得与其关联的各个国家/地区.然后,我希望能够将国家/地区列表绑定到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();
}
Run Code Online (Sandbox Code Playgroud)

要么:

using (var context = CreateAssetContext())
{
    var countries = context.Assets
        .Where(a => a.AssetId == givenAssetId)
        .Select(a => a.Countries)
        .SingleOrDefault();
}
Run Code Online (Sandbox Code Playgroud)

第二个选项是OK(不确定它是否优于SQL视点中的第一个)因为AssetId是主键,所以只能有一个资产.对于按其他标准查询 - 例如Asset.Name == "XYZ"- 您可能期望多个资产我更喜欢第一个选项.对于第二个,你必须替换Selectby SelectManySingleOrDefaultby ToListDistinct用来过滤掉可能重复的国家.SQL可能会更复杂.

最后,我希望能够更新给定资产的国家/地区列表.

这更棘手,因为您需要处理案例:1)国家已添加到资产,2)国家已从资产中删除,3)已与资产相关的国家/地区.

假设您有一个国家/地区ID(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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

对于数据库的第二次往返的价格,您可以使用这个更简单的代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

EF的变更检测应识别已从资产的国家/地区列表中添加或删除了哪些国家/地区.如果后一个代码能够正常工作,我不是100%肯定.