使用 EF 6 在本地 SQL 数据库中插入记录时出现问题

Having problems inserting records in a local SQL database using EF 6

我是一名学生开发人员,正在尝试开发一个小型数据库客户端系统,用于为 运行 经营小型企业的朋友注册和列出客户端。我认为这将是我尝试将我正在研究的东西变成可以使用的真实东西的绝好机会,即使它只是具有基本功能。

该程序在我的项目中使用本地数据库。我正在使用 Windows Forms,.NET Framework 4.7.2 和 EF6,我的数据库是基于服务的数据库,您可以在 Visual Studio 中添加它。

我创建了一个用于注册客户的表单,但是当我单击该按钮时,它不会将值插入数据库,即使 SQL 插入是 运行ning 就好了?数据库设置了自动增量的 ID 列,所以我只是绕过了在我的代码中设置任何 ID,因为数据库应该自动生成它吗?

这是我 运行 使用一些测试值时的 SQL 输出:

Opened connection at 17/03/2020 17:30:34 -03:00

SELECT Count(*)
FROM INFORMATION_SCHEMA.TABLES AS t
WHERE t.TABLE_SCHEMA + '.' + t.TABLE_NAME IN ('dbo.Clientes')
    OR t.TABLE_NAME = 'EdmMetadata'
-- Executing at 17/03/2020 17:30:34 -03:00
-- Completed in 39 ms with result: 1

Closed connection at 17/03/2020 17:30:34 -03:00
"ZyonCliente.exe" (CLR v4.0.30319: ZyonCliente.exe): Carregado "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Runtime.Serialization\v4.0_4.0.0.0__b77a5c561934e089\System.Runtime.Serialization.dll". Carregamento de símbolos ignorado. O módulo está otimizado e a opção do depurador 'Apenas Meu Código' está habilitada.
Opened connection at 17/03/2020 17:30:35 -03:00
SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[__MigrationHistory] AS [Extent1]
        WHERE [Extent1].[ContextKey] = @p__linq__0
    )  AS [GroupBy1]
-- p__linq__0: 'ZyonCliente1.DAL.ContextoBancoDeDados' (Type = String, Size = 4000)
-- Executing at 17/03/2020 17:30:35 -03:00
-- Failed in 13 ms with error: Invalid object name 'dbo.__MigrationHistory'.

Closed connection at 17/03/2020 17:30:35 -03:00
Opened connection at 17/03/2020 17:30:35 -03:00
SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[__MigrationHistory] AS [Extent1]
    )  AS [GroupBy1]
-- Executing at 17/03/2020 17:30:35 -03:00
-- Failed in 6 ms with error: Invalid object name 'dbo.__MigrationHistory'.

Closed connection at 17/03/2020 17:30:35 -03:00
"ZyonCliente.exe" (CLR v4.0.30319: ZyonCliente.exe): Carregado "EntityFrameworkDynamicProxies-EntityFramework". 
Opened connection at 17/03/2020 17:30:35 -03:00
Started transaction at 17/03/2020 17:30:35 -03:00
INSERT [dbo].[Clientes]([Nome], [Endereco], [Data_de_Nascimento], [Email], [Telefone])
VALUES (@0, @1, @2, @3, @4)
SELECT [ID]
FROM [dbo].[Clientes]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
-- @0: 'testname' (Type = String, Size = 50)
-- @1: 'testaddress' (Type = String, Size = 100)
-- @2: '25/02/2020 17:30:17' (Type = DateTime2)
-- @3: 'testemail' (Type = String, Size = 80)
-- @4: 'testphone' (Type = String, Size = 50)
-- Executing at 17/03/2020 17:30:35 -03:00
-- Completed in 13 ms with result: SqlDataReader

Committed transaction at 17/03/2020 17:30:35 -03:00
Closed connection at 17/03/2020 17:30:35 -03:00

我尝试了几种不同的方法,甚至基于同一个数据库创建了一个全新的项目,但使用直接插入方法(而不是使用 UnitsOfWork 模式)插入值,并使用 BindingSource 组件来处理插入相反,没有任何效果,我不知道为什么,因为似乎 SQL 是 运行ning 但数据库只是拒绝更新?

这是按钮的代码:

private void button1_Click(object sender, EventArgs e)
{
    if (nomeTextBox.Text.Length == 0)
    {
        MessageBox.Show("Campo nome não pode estar vazio", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    else
    {
        using (UnitsOfWork.UnitOfWork uow = new UnitsOfWork.UnitOfWork())
        {
            Cliente novocliente = new Cliente();
            novocliente.Nome = nomeTextBox.Text;
            novocliente.Endereco = enderecoTextBox.Text;
            novocliente.Data_de_Nascimento = data_de_NascimentoDateTimePicker.Value;
            novocliente.Email = emailTextBox.Text;
            novocliente.Telefone = telefoneTextBox.Text;

            uow.Clientes.Add(novocliente);
            uow.Save();
            uow.Dispose();
        }
    }
}

我的 UnitsOfWork 代码是:

using System;
using ZyonCliente1.Model;

namespace ZyonCliente1.UnitsOfWork
{
    public class UnitOfWork : IUnitOfWork
    {
        private ZyonCliente1.DAL.ContextoBancoDeDados _context;

        public UnitOfWork(ZyonCliente1.DAL.ContextoBancoDeDados context)
        {
            _context = context;
        }

        // Delete this default constructor if using an IoC container
        public UnitOfWork()
        {
            _context = new ZyonCliente1.DAL.ContextoBancoDeDados();
        }

        public Repositories.IClienteRepository Clientes
        {
            get { return new ZyonCliente1.Repositories.ClienteRepository(_context); }
        }

        public void Save()
        {
            _context.SaveChanges();
        }

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

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_context != null)
                {
                    _context.Dispose();
                    _context = null;
                }
            }
        }
    }
}

还有我的DbContext

namespace ZyonCliente1.DAL
{
    using System;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Linq;
    using System.Diagnostics;
    using ZyonCliente1.Model;
    //Database context file
    public partial class ContextoBancoDeDados : DbContext
    {
        public ContextoBancoDeDados()
            : base("name=ClientesModel")
        {
        }

        public virtual DbSet<Cliente> Clientes { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.Log = (query) => Debug.Write(query);
        }
    }
}

也不确定是否需要它,但这是我的实体 class 代码:

namespace ZyonCliente1.Model
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;

    [Table("Clientes")]
    public partial class Cliente
    {
        public int ID { get; set; }

        [Required]
        [StringLength(50)]
        public string Nome { get; set; }

        [StringLength(100)]
        public string Endereco { get; set; }

        public DateTime? Data_de_Nascimento { get; set; }

        [StringLength(80)]
        public string Email { get; set; }

        [StringLength(50)]
        public string Telefone { get; set; }
    }
}

连接字符串:

<connectionStrings>
    <add name="ClientesModel" connectionString="data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=|DataDirectory|\ClientesDatabase.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
      providerName="System.Data.SqlClient" />
    <add name="ZyonCliente1.Properties.Settings.ClientesDatabaseConnectionString"
      connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\ClientesDatabase.mdf;Integrated Security=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

我也尝试过在 Id 上使用 [Key],但它也不起作用。 我不确定是否需要共享存储库等其他东西的代码,因为它们只是 UnitOfWork 和存储库的普通模式,但我可以在此处包含它们或为您共享我的项目的 zip伙计们。我真的不知道在这里做什么希望这里有人可以帮助我吗?我已经为此苦思了两天了。

首先,在深入研究工作单元、存储库等之前,从最简单的事情开始会有所帮助。这只会使确定问题根源的尝试变得复杂。

首先,如果为 AutoIncrement 设置了数据库,那么您将需要告诉 EF 该键是一个标识列。

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }

[密钥]不是必需的,但建议使用。 EF 将通过约定而不是配置来解决此类细节,但 IMO 依赖约定会导致开发人员在阅读时对他们正在阅读的内容做出假设,而当某些事情不可避免地无法按预期工作时,假设会导致怀疑。

在处理 IDisposableusing 块时,您不需要,也不应显式调用 Dispose()using 负责。

所以我建议从您的初始代码开始,以确保您在正确的地方寻找东西:

private void button1_Click(object sender, EventArgs e)
{
    if (nomeTextBox.Text.Length == 0)
    {
        MessageBox.Show("Campo nome não pode estar vazio", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    using (var context = new ContextoBancoDeDados("ClientesModel"))
    {
        Cliente novocliente = new Cliente
        {
            Nome = nomeTextBox.Text;
            Endereco = nomeTextBox.Text;
            Data_de_Nascimento = data_de_NascimentoDateTimePicker.Value;
            Email = emailTextBox.Text;
            Telefone = telefoneTextBox.Text;
        };  
        context.Clientes.Add(novocliente);
        context.SaveChanges();
    }
}

如果可行,则逐渐考虑重新考虑您的模式,但只添加真正合理的内容。除了有人说它是 "best practice" 之外,它还需要有其他原因。工作单元和存储库等模式非常有用,但前提是它们在您的代码中服务于某个目的,例如促进单元测试。如果您没有这样的要求,那么他们只会让事情变得更复杂。

如果您没有看到数据,那么我怀疑您的连接字符串可能指向与您正在检查的不同的数据库。