【问题标题】:Insert data to multiple tables in single query在单个查询中将数据插入多个表
【发布时间】:2015-02-24 14:17:52
【问题描述】:

目前我正在学习 ASP.NET,我坚持将数据存储到多个表中。数据库模型如下图:

我想要实现的是当我将新联系人添加到联系人表以获取最后插入 ID 并自动插入表电话/标签/电子邮件的数据(如果该表有一些数据)。是否有机会在单个查询中执行此操作,还是我需要为每个表运行新查询?

这是控制器中使用的模型:

 public partial class Contact
    {
        public Contact()
        {
            this.Emails1 = new HashSet<Email>();
            this.Phones1 = new HashSet<Phone>();
            this.Tags1 = new HashSet<Tag>();
        }

        public int id { get; set; }
        public string firstname { get; set; }
        public string lastname { get; set; }
        public string address { get; set; }
        public string city { get; set; }
        public Nullable<byte> bookmarked { get; set; }
        public string notes { get; set; }

        public virtual ICollection<Email> Emails1 { get; set; }
        public virtual ICollection<Phone> Phones1 { get; set; }
        public virtual ICollection<Tag> Tags1 { get; set; }
    }
}

这里是电话/标签/电子邮件表格的模型(在一列中名称不同)

public partial class Email
    {
        public int id { get; set; }
        public int id_contact { get; set; }
        public string email1 { get; set; }

        public virtual Contact Contact1 { get; set; }
    }

这是向数据库添加新行的控制器类:

 public string AddContact(Contact contact)
        {
            if (contact != null)
            {

                db.Contacts.Add(contact);
                db.SaveChanges();
                    return "Contact Added";
            }
            else
            {
                return "Invalid Record";
            }
        }

【问题讨论】:

标签: c# sql asp.net asp.net-mvc-4


【解决方案1】:

您的联系人对象包含电子邮件、电话和标签的集合,因此在调用 db.SaveChanges() 之前,您可以将它们添加到联系人的集合中。

public string AddContact(Contact contact)
{
    if (contact != null)
    {
        db.Contacts.Add(contact);

        contact.Emails1.Add(new Email { Email1 = "email@email.com"})
        contact.Emails1.Add(new Email { Email1 = "email2@email.com"})

        contact.Phones1.Add(new Phone1 { PhoneNumber = "1234516123"})

        db.SaveChanges();
        return "Contact Added";
    }
    else
    {
            return "Invalid Record";
    }
}

只要确保你的外键引用设置正确,并且如果联系人对象是一个新联系人,它就会有一个标识列。

【讨论】:

  • 我需要获取最后插入的 ID。在您的示例中,我没有将在电子邮件/电话或标签表中使用的 id_contact ...
  • @jureispro 如果为您的电子邮件表设置了外键到联系人表,实体框架将处理该问题。
  • 感谢@KDilawar 的回答。问题是我从联系人联系人获取数据,所以我无法像你一样将参数传递给contact.Emails1.Add
  • @jureispro - 如果您从电子邮件表到联系人表设置了外键,当您调用保存更改时,实体框架将为您处理填写电子邮件对象的 id_contact。您可以通过在调用 db.SaveChanges() 并查看联系人对象后添加断点来测试这一点。您应该看到联系人对象应该有一个非零 id,一个电子邮件对象的集合,它们的 id_contact 用联系人对象的 id 填充。此外,这仅在您的联系人对象的 id 列是标识列时才有效。
  • 感谢您的回答。如何添加电子邮件或标签/电话?我使用 db.Emails1.Add(contact.Emails) 但它给了我错误
【解决方案2】:

您可以将实体图保存为this article 中关于实体框架的内容:

//Create student in disconnected mode
Student newStudent = new Student() { StudentName = "New Single Student" };

//Assign new standard to student entity
newStudent.Standard = new Standard() { StandardName = "New Standard" };

//add new course with new teacher into student.courses
newStudent.Courses.Add(new Course() { CourseName = "New Course for single student", Teacher = new Teacher() { TeacherName = "New Teacher" } });

using (var context = new SchoolDBEntities())
{
    context.Students.Add(newStudent);
    context.SaveChanges();

    Console.WriteLine("New Student Entity has been added with new StudentId= " + newStudent.StudentID.ToString());
    Console.WriteLine("New Standard Entity has been added with new StandardId= " + newStudent.StandardId.ToString());
    Console.WriteLine("New Course Entity has been added with new CourseId= " + newStudent.Courses.ElementAt(0).CourseId.ToString());
    Console.WriteLine("New Teacher Entity has been added with new TeacherId= " + newStudent.Courses.ElementAt(0).TeacherId.ToString());
}

此代码使用scope_identity() 函数来获取最后添加的记录ID。

所以,据我了解,您已经为您的课程编写了完整的代码。另请注意,您应该处置您的DataContext

【讨论】:

  • 感谢您的回答@VMAtm。我检查了你提到的文章。那篇文章中的导师使用“硬编码”值插入数据库。我的数据是动态添加的,所以我不能使用那个例子。
【解决方案3】:

答案是使用存储过程:

CRUD 使用存储过程::

Entity Framework 能够根据您的 LINQ to Entities 或 Entity SQL 查询为数据库自动构建本机命令,以及构建用于插入、更新或删除数据的命令。您可能想要覆盖这些步骤并使用您自己的预定义存储过程。您可以使用存储过程来获取数据或向一个或多个数据库表添加/更新/删除记录。

这里用于创建、更新、删除操作的存储过程将使用 DbContext 完成。这意味着上下文将在 context.SaveChanges() 上执行存储过程而不是 DDL 语句。

会用

  1. sp_InsertStudentInfo 存储过程将新学生插入 数据库
  2. sp_UpdateStudent 更新学生
  3. sp_DeleteStudent 删除数据库中的学生。

    CREATE PROCEDURE [dbo].[sp_InsertStudentInfo]
        -- Add the parameters for the stored procedure here
        @StandardId int = null,
        @StudentName varchar
    AS
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
         INSERT INTO [SchoolDB].[dbo].[Student]([StudentName],[StandardId])
         VALUES(@StudentName, @StandardId)
    
        SELECT SCOPE_IDENTITY() AS StudentId
    
    END
    
     CREATE PROCEDURE [dbo].[sp_UpdateStudent]
        -- Add the parameters for the stored procedure here
        @StudentId int,
        @StandardId int = null,
        @StudentName varchar
    AS
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
        Update [SchoolDB].[dbo].[Student] 
        set StudentName = @StudentName,StandardId = @StandardId
        where StudentID = @StudentId;
    
    END
    
    CREATE PROCEDURE [dbo].[sp_DeleteStudent]
        -- Add the parameters for the stored procedure here
        @StudentId int
    AS
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
        DELETE FROM [dbo].[Student]
        where StudentID = @StudentId
    
    END
    

首先,将这些存储过程添加到 EDM 中,并确保未选中 Import selected stored procudures and function into the entity model 复选框,因为我们将这些过程直接映射到 Student 实体。

现在模型浏览器会将过程添加到存储模型中,但不会添加到函数导入中

现在,在 EDM 设计器中,右键单击 Student 实体并选择存储过程映射以打开映射详细信息:

在映射详细信息中,您将看到 、 、 。为每个选择适当的存储过程,例如。插入函数选择 sp_InsertStudentInfo 如下图:

sp_InsertStudentInfo 返回新的自动生成的 StudentId。因此,将其与 Student Entity 的 StudentID 进行映射,如下所示:

如下图完成Insert、Update、Delete过程的映射:

现在,我们需要在执行之前对其进行验证,以确保不会出现运行时错误。为此,请在设计器中右键单击 Student 实体并单击 Validate 并确保没有警告或错误:

现在您可以添加、更新和删除学生,如下所示:

using (var context = new SchoolDBEntities())
{
    Student newStudent = new Student() { StudentName = "New Student using SP"};

    context.Students.Add(newStudent);
    //will execute sp_InsertStudentInfo 
    context.SaveChanges();

    newStudent.StudentName = "Edited student using SP";
    //will execute sp_UpdateStudent
    context.SaveChanges();

    context.Students.Remove(newStudent);
    //will execute sp_DeleteStudentInfo 
    context.SaveChanges();
}

上面显示的代码将在每个SaveChanges()上执行以下存储过程:

exec [dbo].[sp_InsertStudentInfo] @StandardId=NULL,@StudentName='New Student using SP'
    go

    exec [dbo].[sp_UpdateStudent] @StudentId=47,@StandardId=NULL,@StudentName='Edited student using SP'
    go

    exec [dbo].[sp_DeleteStudent] @StudentId=47
    go

注意:一旦上下文在添加新学生后调用 SaveChagnes,它会将新的 StudentID 分配给 Student 实体的 StudentID 属性,因为 sp_InsertStudentInfo 返回 StudentId。为了使用该实体对象进行进一步的操作,这是必要的。

在这里查看更多practically.

【讨论】:

  • 感谢您的详细解答。我是 ASP.NET 的初学者,我觉得这很难阅读。无论如何,当我检查你的答案时,你只插入一个数据(不是多个数据),这就是问题所在(至少我认为是这样)......你能帮我创建整个存储过程以匹配我的模型吗?
猜你喜欢
  • 1970-01-01
  • 2019-04-19
  • 1970-01-01
  • 2011-04-21
  • 2020-08-01
  • 1970-01-01
  • 2015-05-30
相关资源
最近更新 更多