【问题标题】:Data not Saving in Database after Creating (CRUD)创建后数据未保存在数据库中 (CRUD)
【发布时间】:2021-11-21 07:51:50
【问题描述】:

我正在开发一个简单的 CRUD 应用程序,其中有两个表:

患者

  • CNIC(varchar 50 和 PK)
  • 名称(varchar 50)

患者疫苗

  • Cnic(varchar 50 和 FK)
  • 疫苗接种名称 (varchar)
  • 疫苗接种日期(varchar)
  • CenterAddress (varchar)

我知道将字符串设置为 PK,FK 不是一个好方法,但这是我的要求。

我有一个 PatientDBContext 类,我在其中执行 CRUD 操作:

public class PatentDBContext
{
    string cs = ConfigurationManager.ConnectionStrings["Myconnection"].ConnectionString;

    public List<Patient> getPatients()
    {
        List<Patient> PatientList = new List<Patient>();
        SqlConnection con = new SqlConnection(cs);

        string query = "SELECT p.CNIC, p.Name, pv.cnic, pv.VaccinationName, pv.VaccinationDate, pv.CenterAddress FROM Patient AS p JOIN PatientVaccines AS pv ON p.CNIC = pv.cnic";

        SqlCommand cmd = new SqlCommand(query, con);

        con.Open();

        SqlDataReader dr = cmd.ExecuteReader();

        while (dr.Read())
        {
            Patient p = new Patient();
         
            p.CNIC = dr["CNIC"].ToString();
            p.Name = dr["Name"].ToString();
            p.VaccinationName = dr["VaccinationName"].ToString();
            //p.VaccinationDate = dr["VaccinationDate"].ToString();
            p.CentreAddress = dr["CenterAddress"].ToString();

            PatientList.Add(p);
        }

        con.Close();

        return PatientList;
    }

    public bool AddPatient(Patient pat)
    {
        SqlConnection con = new SqlConnection();
        SqlCommand cmd = new SqlCommand("spAddPatient", con);
        cmd.CommandType = CommandType.StoredProcedure;

        cmd.Parameters.AddWithValue("@CNIC", pat.CNIC);
        cmd.Parameters.AddWithValue("@Name", pat.Name);
        cmd.Parameters.AddWithValue("@VaccinationName", pat.VaccinationName);
        cmd.Parameters.AddWithValue("@VaccinationDate", pat.VaccinationDate);
        cmd.Parameters.AddWithValue("@CenterAddress", pat.CentreAddress);

        con.Open();
        int i = cmd.ExecuteNonQuery();
        con.Close();

        if (i > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public bool UpdatePatient(Patient pat)
    {
        SqlConnection con = new SqlConnection();
        string query = "UPDATE PatientVaccines SET  VaccinationName = @VaccinationName, VaccinationDate = @VacinationDate, CenterAddress = @CenterAddress WHERE Cnic = @Cnic";

        SqlCommand cmd = new SqlCommand(query, con);
        //cmd.CommandType = CommandType.StoredProcedure;

        cmd.Parameters.AddWithValue("@CNIC", pat.CNIC);
        //cmd.Parameters.AddWithValue("@Name", pat.Name);
        cmd.Parameters.AddWithValue("@VaccinationName", pat.VaccinationName);
        cmd.Parameters.AddWithValue("@VaccinationDate", pat.VaccinationDate);
        cmd.Parameters.AddWithValue("@CenterAddress", pat.CentreAddress);

        con.Open();
        int i = cmd.ExecuteNonQuery();
        con.Close();

        if (i > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

错误是此类在getPatient() 函数中我将其注释掉p.VaccinationDate 显示错误,我无法将类型字符串隐式转换为DateTime,如何将其转换为DateTime?

我有另一个函数名称AddPatient()现在显示任何错误或错误,但是当我在输入记录后单击提交按钮时它不执行任何操作。

HomeController

public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        PatentDBContext db = new PatentDBContext();
        List<Patient> obj = db.getPatients();

        return View(obj);
    }

    public ActionResult Create()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Create(Patient pat)
    {
        try
        {
            if (ModelState.IsValid == true)
            {
                PatentDBContext context = new PatentDBContext();
                bool check = context.AddPatient(pat);

                if (check == true)
                {
                    TempData["InsertMessage"] = "Data Inserted..";
                }
                else
                {
                    TempData["FailureMessage"] = "Data Not Inserted";
                }

                ModelState.Clear();

                return RedirectToAction("Index");
            }
            return View();
        }
        catch
        {
            return View();
        }
    }

    public ActionResult Edit(string Cnin)
    {
        PatentDBContext context = new PatentDBContext();

        //string str = Cnin.ToString();
        var row = context.getPatients().Find(model => model.CNIC = Cnin);
        return View(row);
    }
}

这里我也无法将隐式类型字符串转换为布尔值

var row = context.getPatients().Find(model => model.CNIC = Cnin);

最后这是我的存储过程:

ALTER PROCEDURE [dbo].[spAddPatient]
    (@CNIC varchar(50),
     @Name varchar(50),
     @VaccinationName varchar(50),
     @VaccinationDate varchar(50),
     @CenterAddress varchar(50))
AS
BEGIN
    INSERT INTO Patient (CNIC, Name)
    VALUES (@CNIC, @Name)

    INSERT INTO PatientVaccines (Cnic, VaccinationName, VaccinationDate, CenterAddress)
    VALUES (@Cnic, @VaccinationName, @VaccinationDate, @CenterAddress)
END

【问题讨论】:

  • 在任何情况下都不要将日期保存为字符串。这是本书中最常见的错误之一。 始终使用适当的数据类型 - Date 如果您不关心时间,DateTime2 如果您不关心时区,或者 DateTimeOffset 如果您关心。另外,请阅读Can we stop using AddWithValue() already?

标签: c# sql-server asp.net-mvc crud


【解决方案1】:

我很确定你是 very new 在这项技术中,因为有一些 basic mistake。我在下面提到了一些common mistake

  1. 不应使用 varchar 作为primary key 而应使用int
  2. 使用int 列在父表和子表中建立关系。
  3. 不要将varchar 用于date field,而应使用DateTime

redesigned两张表如下:

病床

CREATE TABLE [dbo].[Patient](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [CNIC] [varchar](50) NOT NULL,
    [Name] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Patient] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

我介绍了设置为IDENTITY 的新列Id,这样 列将自动获取值,如 1、2、3

患者疫苗表

CREATE TABLE [dbo].[PatientVaccines](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [VaccinationName] [varchar](50) NULL,
    [VaccinationDate] [datetime] NULL,
    [CenterAddress] [varchar](50) NULL,
    [PatientId] [int] NOT NULL,
 CONSTRAINT [PK_PatientVaccines] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

我介绍两个新专栏IdPatientId。当您插入患者时,Id 字段将自动获得一个数字,并且该 id 将作为 PatientId 插入到 PatientVaccines Table 中,以便您可以找到关系数据。我还用datetime 代替VaccinationDate

外键约束

ALTER TABLE [dbo].[PatientVaccines]  WITH CHECK ADD  CONSTRAINT [FK_PatientVaccines_Patient] FOREIGN KEY([PatientId])
REFERENCES [dbo].[Patient] ([Id])

这是一个constraint or rules,它将限制您插入非关系数据。例如:您没有有 ID 为 101 的患者记录,但您尝试插入带有 PatientId 101PatientVaccines 记录,那么此规则将限制您这样做。

这是两张表的Sql图

通过上述操作,您需要更新您的Stored Procedure,如下所示:

CREATE PROCEDURE [dbo].[spAddPatient]
    (@CNIC varchar(50),
     @Name varchar(50),
     @VaccinationName varchar(50),
     @VaccinationDate datetime,
     @CenterAddress varchar(50))
AS
BEGIN
    INSERT INTO Patient (CNIC, Name)
    VALUES (@CNIC, @Name)

    INSERT INTO PatientVaccines (PatientId, VaccinationName, VaccinationDate, CenterAddress)
    VALUES (@@Identity, @VaccinationName, @VaccinationDate, @CenterAddress)
END

这是完整的 C# 代码,我在其中做了一些更正

public class PatentDBContext
{
    string cs = ConfigurationManager.ConnectionStrings["Myconnection"].ConnectionString;
    public List<Patient> getPatients()
    {
        List<Patient> PatientList = new List<Patient>();
        SqlConnection con = new SqlConnection(cs);

        string query = "SELECT p.CNIC, p.Name, pv.VaccinationName, pv.VaccinationDate, pv.CenterAddress FROM Patient AS p JOIN PatientVaccines AS pv ON p.Id = pv.PatientId";
        SqlCommand cmd = new SqlCommand(query, con);
        con.Open();
        SqlDataReader dr = cmd.ExecuteReader();

        while (dr.Read())
        {
            Patient p = new Patient();
            p.CNIC = dr["CNIC"].ToString();
            p.Name = dr["Name"].ToString();
            p.VaccinationName = dr["VaccinationName"].ToString();
            p.VaccinationDate = Convert.ToDateTime(dr["VaccinationDate"]);
            p.CenterAddress = dr["CenterAddress"].ToString();

            PatientList.Add(p);
        }

        con.Close();
        return PatientList;
    }

    public bool AddPatient(Patient pat)
    {
        SqlConnection con = new SqlConnection(cs);
        SqlCommand cmd = new SqlCommand("spAddPatient", con);
        cmd.CommandType = CommandType.StoredProcedure;

        cmd.Parameters.AddWithValue("@CNIC", pat.CNIC);
        cmd.Parameters.AddWithValue("@Name", pat.Name);
        cmd.Parameters.AddWithValue("@VaccinationName", pat.VaccinationName);
        cmd.Parameters.AddWithValue("@VaccinationDate", pat.VaccinationDate);
        cmd.Parameters.AddWithValue("@CenterAddress", pat.CenterAddress);

        con.Open();
        int i = cmd.ExecuteNonQuery();
        con.Close();

        if (i > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public bool UpdatePatient(Patient pat)
    {
        SqlConnection con = new SqlConnection(cs);
        string query = "UPDATE PatientVaccines SET  VaccinationName = @VaccinationName, VaccinationDate = @VaccinationDate, CenterAddress = @CenterAddress WHERE PatientId = ( Select Id from Patient where Cnic = @Cnic)";

        SqlCommand cmd = new SqlCommand(query, con);

        cmd.Parameters.AddWithValue("@CNIC", pat.CNIC);
        //cmd.Parameters.AddWithValue("@Name", pat.Name);
        cmd.Parameters.AddWithValue("@VaccinationName", pat.VaccinationName);
        cmd.Parameters.AddWithValue("@VaccinationDate", pat.VaccinationDate);
        cmd.Parameters.AddWithValue("@CenterAddress", pat.CenterAddress);

        con.Open();
        int i = cmd.ExecuteNonQuery();
        con.Close();

        if (i > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

【讨论】:

  • 1/3 您的回答存在一些问题,首先是假设代理键足以确保数据完整性。如果要添加代理键,则必须在曾经是主键的列上使用唯一约束(或唯一索引),否则可能在不同的代理键值上有重复数据。>>>跨度>
  • 2/3 接下来 - 在第二个表中,您使用了DateTime,这不是一个严重错误,但您可以使用DateTime2 做得更好。此外,在存储过程中,您没有更改参数类型以匹配列的类型,最糟糕的是 - 您使用 @@Identity 获取新插入的标识值,这是自 @987654360 以来的错误@ 是数据库范围的范围,这意味着它可能会为您提供在完全不同的表中创建的标识值(如果它恰好插入在 insert 和 select 语句之间)。在这种情况下,您应该使用scope_identity()。>>>
  • 3/3 了解更多信息,请阅读 HockeyGeekGirl 的 Why You Should Never Use DATETIME Again! 和您的 Use the right tool to get identity values back after an insert
  • 抱歉,当我找到第三个链接时,我的最后一条评论太旧了,无法编辑 - 所以这里是:Does a table with a surrogate key require a unique constraint on a natural key to be in 1NF?
  • 让我们添加共同点 - 不要使用addwithvalue。我还认为 VaccinationDate 应该是 DATE - 通常没有人关心确切的时间。
【解决方案2】:

我认为你的存储过程不正确,你可以事先在数据库中测试一下。

// here you should use operator== instead of аssignment operator=
// Have in mind that .Find will throw an error if model with given Cnin is not found
var row = context.getPatients().Find(model => model.CNIC == Cnin);

How to convert a string to datetime object

Create a stored procedure

一般建议,您可以通过谷歌搜索您得到的错误并找到有关它们的信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 2021-10-27
    相关资源
    最近更新 更多