简体   繁体   English

在实体框架中更新多对多关系中的记录

[英]Updating records in a many to many relationship in Entity Framework

In my database I have two tables: Trucks and TruckTypes.在我的数据库中,我有两个表:Trucks 和 TruckTypes。 Many trucks can have many truck types.许多卡车可以有多种卡车类型。 I've created my models using the database designer and the intermediate table (that only holds primary keys) has been ignored, which I understand is EF's default behaviour:我已经使用数据库设计器创建了我的模型,并且中间表(仅包含主键)已被忽略,我理解这是 EF 的默认行为:

在此处输入图片说明

When I check the models, it seems both now have an ICollection of the corresponding model which seems to be working.当我检查模型时,似乎两者现在都有相应模型的 ICollection 似乎正在工作。 The problem comes when I try to add new items or update existing items.当我尝试添加新项目或更新现有项目时,问题就出现了。

At the moment I update my records with this:目前我用这个更新我的记录:

using (var db = new Entities())
{
    var truckResult = from t in db.ant_Truck
                        where t.Id == model.Id
                        select t;

    var truck = truckResult.Single();

    truck.Name = model.Name;
    truck.ant_TruckType.Clear();
    foreach (var truckType in model.TruckTypes)
        truck.ant_TruckType.Add(new ant_TruckType()
        {
            Name = truckType.Name,
            Disabled = truckType.Disabled
        });
    truck.Disabled = model.Disabled;

    db.SaveChanges();
}

I think I understand the problem: That the new ant_truckType models are being added as 'new' rather than linking to existing records in the 'ant_truckType' table but I'm still not sure how to link them.我想我理解这个问题:新的ant_truckType模型被添加为“新”而不是链接到“ant_truckType”表中的现有记录,但我仍然不确定如何链接它们。

I tried creating another method that queried the table and attempted to return an ICollection<ant_truckType> but this starts giving me other errors about converting to an ICollection:我尝试创建另一种查询表的方法并尝试返回ICollection<ant_truckType>但这开始给我其他关于转换为 ICollection 的错误:

public ICollection<ant_TruckType> GetDbTruckTypesForTruck(ant_Truck truck)
{
    using (var db = new Entities())
    {
        var result = from tt in db.ant_TruckType
                        where tt.ant_Truck.Contains(truck)
                        select tt;

        var truckTypes = result.ToList() as ICollection<ant_TruckType>;

        return truckTypes;
    }
}

I'm not sure if I'm doing things correctly at this point as I've spent so long trying to understand why this isn't working.我不确定此时我是否做对了,因为我花了很长时间试图理解为什么这不起作用。 What's the most efficient way of trying to do the above?尝试执行上述操作的最有效方法是什么?

Thanks in advance.提前致谢。

Truck Types is a reference list, where a truck can have many Truck Types. Truck Types 是一个参考列表,其中一辆卡车可以有许多 Truck Types。 I'd probably avoid adding a Trucks collection inside Truck Types just to simplify referencing.为了简化引用,我可能会避免在 Truck Types 中添加 Trucks 集合。 It's still many-to-many, but instead of:它仍然是多对多的,但不是:

.HasMany(x => x.TruckTypes)
.WithMany(x => x.Trucks)

... with the associated linking table config, it would be simplified to: ...使用关联的链接表配置,它将被简化为:

.HasMany(x => x.TruckTypes)
.WithMany()

When updating TruckTypes on a truck, you'll want a set of all TruckTypes to reference then you can split it up to determine which Types need to be added vs. removed:在卡车上更新 TruckTypes 时,您需要引用一组所有 TruckTypes,然后您可以将其拆分以确定需要添加和删除哪些类型:

Ideally your model just needs to send a list of Truck Type IDs that should be associated to the Truck.理想情况下,您的模型只需要发送应与卡车关联的卡车类型 ID 列表。 If you're sending Truck Types then add a step to get the relevant IDs you want to leave associated to the Truck.如果您要发送卡车类型,则添加一个步骤以获取您希望与卡车关联的相关 ID。

using (var db = new Entities())
{
    // Load our truck and eager load its TruckTypes.
    var truck = db.ant_Truck.Include(t => t.TruckTypes).Single(t => t.Id == model.Id);
    
    // TODO: Update truck fields ...
    
    // Load all truck types we will want to associate with this truck.
    // For smaller sets we can simply load add truck types.
    var truckTypes = db.ant_TruckType.Where(tt => model.TruckTypeIds.Contains(tt.Id)).ToList();
    
    var existingTruckTypeIds = truck.TruckTypes.Select(tt => tt.Id).ToList();
    var truckTypeIdsToRemove = existingTruckTypeIds.Except(model.TruckTypeIds);
    var truckTypeIdsToAdd = model.TruckTypeIds.Except(existingTruckTypeIds);

    var truckTypesToRemove = truck.TuckTypes.Where(tt => truckTypeIdsToRemove.Contains(tt.Id));
    var truckTypesToAdd = truckTypes.Where(tt => truckTypeIdsToAdd.Contains(tt.Id));

    foreach(var truckType in truckTypesToRemove)
        truck.TruckTypes.Remove(truckType);
    foreach(var truckType in truckTypesToAdd)
        truck.TruckTypes.Add(truckType);

    db.SaveChanges();
}

We load the truck and include its current truck types, then load references to all truck types we want associated with the truck from the DB Context.我们加载卡车并包含其当前的卡车类型,然后从 DB Context 加载对我们希望与卡车关联的所有卡车类型的引用。 From there we take a simple inventory of the truck type IDs on our truck model.从那里,我们对卡车模型上的卡车类型 ID 进行了简单的盘点。 From that we can use Except to determine the truck type IDs that need to be removed from our truck and added to the truck.从中我们可以使用Except来确定需要从我们的卡车中删除并添加到卡车中的卡车类型ID。 Those resulting ID lists can then get references from our truck's types (to remove) and from the loaded truck types (to add).然后,这些生成的 ID 列表可以从我们的卡车类型(要删除)和已装载的卡车类型(要添加)中获取引用。

With bidirectional references you may need the overhead of going through the Trucks collection on the respective TruckType and removing or adding the current Truck from it as well.使用双向引用,您可能需要在相应TruckType上遍历Trucks集合并TruckType删除或添加当前 Truck 的开销。 When it comes to bi-directional references, I recommend avoiding them unless they are truly necessary/helpful.当涉及到双向引用时,我建议避免使用它们,除非它们确实有必要/有帮助。 You can always filter truck types by truck by querying criteria against Truck then using SelectMany to provide the TruckTypes.您始终可以通过针对 Truck 查询条件,然后使用SelectMany提供 TruckType,按卡车过滤卡车类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM