实体框架多对多表插入复杂的关系

时间:2017-01-26 15:00:51

标签: entity-framework

这是我的简化架构。

enter image description here

我想添加一名托管人,并将其附加到现有设施。 ContactType仅说明记录的联系类型,例如公司或个人。在这种情况下,保管人是个人。 我可以自己插入联系人。

对我而言,复杂性是由于多对多和一对多的关系。

在控制器中我有

     vm.Contact.Facilities.Add(new Facility { FacilityID = vm.SelectedFacilityID });

    _repo.SaveContact(vm);

在回购

            _db.Contacts.Add(vm.Contact);
            _db.SaveChanges();

这给了我一个外键错误,因为它试图插入一个新设施,并且我在该表中没有显示其他外键。我不想添加设施,只是引用FacilityID。

约定 Not 明确尝试通过这样的方式直接在多对多的FacilityCustodian表中插入记录

 var fc = new FacilityCustodian { CustodianFacilityID = vm.SelectedFacilityID };
 vm.Contact.FacilityCustodian.Add(fc);

我也试过

            foreach (var facility in vm.Contact.Facilities)
            {
                _db.Entry(facility).State = EntityState.Unchanged;
            }

我见过的大多数例子都没有这两种关系所以我不知道如何继续。

感谢您的帮助。

添加代码

using Licensing.Models;
namespace Licensing
{
using System.Data.Entity;
public class Context : DbContext
{
    public Context()
        : base("name=Context")
    {
    }
    public virtual DbSet<Contact> Contacts { get; set; }
    public virtual DbSet<ContactType> ContactTypes { get; set; }
    public virtual DbSet<Facility> Facilities { get; set; }
    public virtual DbSet<FacilityCandler> FacilityCustodians { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Contact>()
            .Property(e => e.FName)
            .IsUnicode(false);

        modelBuilder.Entity<Contact>()
            .Property(e => e.LName)
            .IsUnicode(false);

        modelBuilder.Entity<Contact>()
            .HasMany(e => e.Facilities)
            .WithRequired(e => e.Contact)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Contact>()
            .HasMany(e => e.FacilityCustodians)
            .WithRequired(e => e.Contact)
            .HasForeignKey(e => e.CustodianFacilityID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<ContactType>()
            .Property(e => e.Type)
            .IsUnicode(false);

        modelBuilder.Entity<ContactType>()
            .HasMany(e => e.Contacts)
            .WithRequired(e => e.ContactType)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Facility>()
            .HasMany(e => e.FacilityCustodians)
            .WithRequired(e => e.Facility)
            .HasForeignKey(e => e.CustodianFacilityID)
            .WillCascadeOnDelete(false);
    }

}
}

namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;

[Table("Contact")]
public class Contact
{

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Contact()
    {
        Facilities = new HashSet<Facility>();
        FacilityCustodians = new HashSet<FacilityCustodian>();

    }

    [Key]
    public int ContactID { get; set; }

    public int ContactTypeID { get; set; }


    [Display(Name = "First Name")]
    [RegularExpression(@"^[a-zA-Z'.\s]+$", ErrorMessage = "Enter a valid Name")]
    [StringLength(150)]
    public string FName { get; set; }

    [Display(Name = "Last Name")]
    [RegularExpression(@"^[a-zA-Z'\s]+$", ErrorMessage = "Enter a valid Name")]
    [StringLength(150)]
    public string LName { get; set; }


    public virtual ContactType ContactType { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Facility> Facilities { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<FacilityCustodian> FacilityCustodians { get; set; }
}
}


using System.ComponentModel.DataAnnotations;

namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

[Table("Facility")]
public class Facility
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Facility()
    {
        FacilityCustodians = new HashSet<FacilityCustodian>();
    }

    [Key]
    public int FacilityID { get; set; }

    public int ContactID { get; set; }

    public virtual Contact Contact { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<FacilityCustodian> FacilityCustodians { get; set; }
}
}

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Licensing.Models
{
[Table("FacilityCustodian")]
public partial class FacilityCustodian
{
    [Key]
    [Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int CustodianFacilityID { get; set; }

    [Key]
    [Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int CustodianContactID { get; set; }

    public virtual Contact Contact { get; set; }

    public virtual Facility Facility { get; set; }
}
}

namespace Licensing.Models
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Table("ContactType")]
public class ContactType
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public ContactType()
    {
        Contacts = new HashSet<Contact>();
    }

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int ContactTypeID { get; set; }

    [Required]
    [StringLength(10)]
    public string Type { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Contact> Contacts { get; set; }
}
}

更新工作代码

  var existingFacility = _repo.GetFacilityByFacilityID(vm.SelectedFacilityID);
                    vm.Contact.LName = vm.LName;
                    vm.Contact.FName = vm.FName;
                    vm.Contact.Name = vm.FName + " " + vm.LName;
                    vm.Contact.ContactTypeID = 1;
                    vm.Contact.FacilityCustodians.Add(existingFacility);

                _db.Contacts.Add(vm.Contact);
                _db.SaveChanges();

1 个答案:

答案 0 :(得分:1)

尝试这个因为我没有完整的文件夹我试图简化模型所有类都是图中提到的名称,但我创建。以下是我的模型类。需要注意的重要事项是首先从contecxt中读取entoty,然后更新值并相应地设置entitystate。这将解决您的问题。

模型类: -

           public class ContactType
        {
            public int ContactTypeId { get; set; }
            public string Name { get; set; }
        }
        public class Contact
        {
            public int ContactId { get; set; }
            public string Name { get; set; }
            public int ContactTypeId { get; set; }
            [ForeignKey("ContactTypeId")]
            public virtual ContactType ContactType { get; set; }

            public virtual ICollection<Facility> Facilities { get; set; } 
        }
        public class FacilityCustodian
        {
            public int FacilityId { get; set; }

            public int ContactTypeId { get; set; }
            [ForeignKey("ContactId")]
            public virtual Contact Contact { get; set; }
             [ForeignKey("FacilityId")]
            public virtual 
                Facility Facility { get; set; }
        }
        public class Facility
        {
            public int FacilityId { get; set; }
            public string Location { get; set; }
            public int ContactId { get; set; }
            [ForeignKey("ContactId")]
            public virtual Contact Contact { get; set; }
            public virtual ICollection<Contact> Contacts { get; set; }
        }

-- here I am showing you to update existing contact and add a new facility to it . same as you case you want to update facility ...

    using (var ctx = new SampleDbContext())
        {
           //this is important to read the entity first .
            var contact = ctx.Contacts.FirstOrDefault(x=>x.ContactId ==vm.Contact.ContactId);
            if (contact != null)
            {
                // Since facilityId is a primary key I set it to autoincrement so I dont have to set it .You can set if it is not primarykey 
                contact.Name = "Updated";
                // update the entity and add new information inthis i am adding facility
                var facility = new Facility
                {
                    Location = "LocA",
                    // assiging same entity to facility so that it will not treat it as a new contact 
                    Contact = contact
                };

                contact.Facilities.Add(facility);
              // Finaly update the state of the entity .
               ctx.Entry(contact).State = EntityState.Modified;

                ctx.SaveChanges();
            }
        }

此后,您在保存时不会插入新的联系人。在我的例子中,我选择添加新设施但保持联系相同。您可以使用设施,但概念是相同的。首先从EF coontext更新值中读取对象,您需要更新它并将状态设置为Modified。这将告诉您图中的所有其他实体与您修改的内容相同。

SQL事件探查器查询以确保更新为联系人并插入设施

  exec sp_executesql N'UPDATE [dbo].[Contacts]
  SET [Name] = @0, [ContactTypeId] = @1
  WHERE ([ContactId] = @2)
  ',N'@0 nvarchar(max) ,@1 int,@2 int',@0=N'Updated',@1=1,@2=1
 go

 exec sp_executesql N'INSERT [dbo].[Facilities]([Location], [ContactId],              [Contact_ContactId])
           VALUES (@0, @1, @2)
          SELECT [FacilityId]
        FROM [dbo].[Facilities]
    WHERE @@ROWCOUNT > 0 AND [FacilityId] = scope_identity()',N'@0nvarchar(max) ,@1 int,@2 int',@0=N'LocA',@1=1,@2=1