无法删除多对多关系

时间:2012-11-18 11:10:46

标签: entity-framework entity-framework-4.1 ef-code-first

我有两个具有多对多关系的实体,如下所示。

public class StandardContact {
    ...
    public virtual ICollection<RelayConfig> RelayConfigs { get; set; }
}

public class RelayConfig {
....
    public virtual ICollection<StandardContact> StandardContacts { get; set; }
}

我正在尝试更新RelayConfig及其与StandardContact的关系。这是我在Update()方法中提出的代码。它只是添加了一些新的StandardContact和/或删除了现有的StandardContacts(根据用户的要求)。参数exposedContacts表示应在数据库中更新的全包式StandardContacts列表

public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {

    RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
                                       .Where(r => r.Id == relayConfig.Id).SingleOrDefault();
    context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);

    List<StandardContact> addedExposedContacts = 
        exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
    List<StandardContact> deletedExposedContacts = 
        dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();

    StandardContact dbExposedContact = null;
    addedExposedContacts.ForEach(exposedContact => {
        dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
        dbRelayConfig.StandardContacts.Add(dbExposedContact);
    });
    deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact); });
}

正确地添加了与RelayConfig相关的新StandardContact实体。但是,删除(上面代码中的最后一行)没有任何效果,并且链接表中的“多对多”关系保持不变。

简而言之,我无法从RelayConfig对象中删除StandardContact实体。我没有得到任何例外,但代码仍然存在。

更新

以下是与数据库相关的代码:

RelayContext类

public class RelayContext : DbContext {

    public DbSet<Station> Stations { get; set; }
    public DbSet<StandardContact> StandardContacts { get; set; }
    public DbSet<RelayConfig> RelayConfigs { get; set; }
    public DbSet<StandardRelay> StandardRelays { get; set; }
    public DbSet<Rack> Racks { get; set; }
    public DbSet<Group> Groups { get; set; }
    public DbSet<Relay> Relays { get; set; }
    public DbSet<Contact> Contacts { get; set; }

    public RelayContext() {
        Database.SetInitializer(new RelayContextInitializer());
    }


    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Entity<Rack>().HasOptional<RelayConfig>(r => r.DefaultFirstRelayConfig).WithMany(rc => rc.RacksWithDefaultFirstRelay).WillCascadeOnDelete(false);
        modelBuilder.Entity<Rack>().HasOptional<RelayConfig>(r => r.DefaultSecondRelayConfig).WithMany(rc => rc.RacksWithDefaultSecondRelay).WillCascadeOnDelete(false);
    }
}

RelayConfigRepository

public class RelayConfigRepository {

    internal RelayContext context;

    public RelayConfigRepository(RelayContext context) {
        this.context = context;
    }
    ....
    public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {
        RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
                                           .Where(r => r.Id == relayConfig.Id).SingleOrDefault();
        context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);

        List<StandardContact> addedExposedContacts = 
            exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
        List<StandardContact> deletedExposedContacts = 
            dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();

        StandardContact dbExposedContact = null;
        addedExposedContacts.ForEach(exposedContact => {
            dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
            dbRelayConfig.StandardContacts.Add(dbExposedContact);
        });
        deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact);});

    }
    .... 
}

UnitOfWork类

public class UnitOfWork : IDisposable {

    private RelayContext context = new RelayContext();

    private bool disposed = false;

    private RelayConfigRepository _relayConfigRepository;
    public RelayConfigRepository RelayConfigRepository {
        get {
            if (_relayConfigRepository == null) {
                _relayConfigRepository = new RelayConfigRepository(context);
            }
            return _relayConfigRepository;
        }
    }

    private StandardContactRepository _standardContactRepository;
    public StandardContactRepository StandardContactRepository {
        get {
            if (_standardContactRepository == null) {
                _standardContactRepository = new StandardContactRepository(context);
            }
            return _standardContactRepository;
        }
    }
    public void Save() {
        try {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex) {
            throw new RepositoryException(ErrorMessages.UpdateConcurrencyError);
        }
        catch (Exception ex) {
            throw new RepositoryException(ErrorMessages.GeneralError);
        }
    }

    protected virtual void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

WPF客户端(ViewModel),它在RelayConfigRepository上调用Update方法。

[Export(typeof(RelayConfigEditViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class RelayConfigEditViewModel :Screen {
    private RelayConfig _relayConfig;
    public RelayConfig RelayConfig {
        get { return _relayConfig; }
        set { _relayConfig = value; NotifyOfPropertyChange(() => RelayConfig); }
    }

    private ObservableCollection<StandardContact> _standardContacts = new ObservableCollection<StandardContact>();
    public ObservableCollection<StandardContact> StandardContacts {
        get { return _standardContacts;}
        set { _standardContacts = value; NotifyOfPropertyChange(() => StandardContacts); }
    }
    ....
    public void Save() {
        List<StandardContact> exposedContacts = StandardContacts.Where(sc => sc.IsMarked).ToList();
        try {
            using (UnitOfWork unitOfWork = new UnitOfWork()) {
                if (editMode == EditMode.Add) {
                    unitOfWork.RelayConfigRepository.Insert(RelayConfig, exposedContacts);
                }
                else {
                    unitOfWork.RelayConfigRepository.Update(RelayConfig, exposedContacts);
                }
                unitOfWork.Save();
            }
        }
        catch (Exception ex) {
            HandleException(ex);
        }
        events.Publish(RelayConfig);
    }
}

更新2

跟踪有点笨拙,但我可以看到与更新方法相关的特殊程序声明。以下是EF生成的相关SQL staments。

update [dbo].[RelayConfigs]
set [Name] = @0, [DisplayName] = @1
where (([Id] = @2) and ([Version] = @3))


**exec sp_executesql N'insert [dbo].[RelayConfigStandardContacts]([RelayConfig_Id], [StandardContact_Id])
values (@0, @1)
',N'@0 int,@1 int',@0=1,@1=4**

正如您所看到的,有用于更新RelayConfig记录和在链接表中添加另一个条目的SQL RelayConfigStandardContact(我在多对多记录中添加的新记录)。但是没有为我从多对多关系中删除的链接表记录生成SQL语句。

1 个答案:

答案 0 :(得分:0)

测试程序以显示它应该按预期工作:

using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;

namespace EFDelMany
{
    public class StandardContact 
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<RelayConfig> RelayConfigs { get; set; }
    }

    public class RelayConfig
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public virtual ICollection<StandardContact> StandardContacts { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<StandardContact> StandardContacts { get; set; }
        public DbSet<RelayConfig> RelayConfigs { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
            using (var context = new MyContext())
            {
                context.Database.Initialize(true);

                // Create one RelayConfig and 5 StandardContacts
                var relayConfig1 = new RelayConfig { Name = "R1" };
                var standardContact1 = new StandardContact { Name = "S1" };
                var standardContact2 = new StandardContact { Name = "S2" };
                var standardContact3 = new StandardContact { Name = "S3" };
                var standardContact4 = new StandardContact { Name = "S4" };
                var standardContact5 = new StandardContact { Name = "S5" };

                // Create relationship for StandardContacts 1,2,3
                relayConfig1.StandardContacts = new List<StandardContact>
                {
                    standardContact1,
                    standardContact2,
                    standardContact3
                };

                context.RelayConfigs.Add(relayConfig1);
                context.StandardContacts.Add(standardContact4);
                context.StandardContacts.Add(standardContact5);

                context.SaveChanges();
            }
            // see screenshot "Before"

            RelayConfig relayConfig = new RelayConfig { Id = 1, Name = "R1a" };
            List<StandardContact> exposedContacts = new List<StandardContact>
            {
                // delete relationship to StandardContacts 1 and 3
                new StandardContact { Id = 2 }, // keep relationship to StandardContact 2
                new StandardContact { Id = 4 }, // add relationship to StandardContact 4
                new StandardContact { Id = 5 }  // add relationship to StandardContact 5
            };

            using (var context = new MyContext())
            {
                RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
                                                   .Where(r => r.Id == relayConfig.Id).SingleOrDefault();
                context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);

                List<StandardContact> addedExposedContacts = 
                    exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
                List<StandardContact> deletedExposedContacts = 
                    dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();

                StandardContact dbExposedContact = null;
                addedExposedContacts.ForEach(exposedContact => {
                    dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
                    dbRelayConfig.StandardContacts.Add(dbExposedContact);
                });
                deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact); });

                context.SaveChanges();
            }
            // see screenshot "After"
        }
    }
}

之前更新:

Before

更新后:

After