如何使用EF Code First避免外表重复行

时间:2019-02-11 16:34:31

标签: c# sql-server entity-framework entity-framework-6

在我的应用程序中,Movies存储在数据库中。这些Movies有一个Name和一个Director

众所周知,与其他人具有相同Name的任何人都是同一个人。我想在应用程序中实现此约束。

我的模特是:

public class MovieContext: DbContext
{
    public MovieContext(): base("name=testDB") { }
    public DbSet<Movie> Movies { get; set; }
    public DbSet<Director> Revenues { get; set; }
}

public class Movie
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Director Director { get; set; }
}

public class Director
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

我的申请是

static void Main()
{

        Director directorA = new Director()
        {
            Name = "A"
        };


        Movie movie1 = new Movie()
        {
            Name = "foo",
            Director = directorA
        };

        Movie movie2 = new Movie()
        {
            Name = "bar",
            Director = directorA
        };
    using (var context = new MovieContext())
    {
        context.Movies.Add(movie1);
        context.SaveChanges();
    }
    using (var context = new MovieContext())
    {
        context.Movies.Add(movie2);
        context.SaveChanges();
    }
}      

(我将上下文放置在两个插入之间以模拟程序的两次不同运行)。

运行此命令会在Directors表中创建两行:

Id  Name
1   A
2   A

由于导演1和导演2的名字相同,所以他们是同一个人,因此我宁愿只一行。

我尝试为此使用索引,并修改了Director模型:

public class Director
{
    [Key]
    public int Id { get; set; }
    [Index(IsUnique =true), StringLength(30)]
    public string Name { get; set; }
}

但是,这会在运行时抛出,因为EF尝试两次插入同一行。显然,这不是正确的方法。

在使用Entity Framework插入数据时,确保相同的异物以相同的ID引用的正确方法是什么?

3 个答案:

答案 0 :(得分:1)

我认为您所描述的只是更新条目。如果已经使用特定名称创建了董事,并且使用相同名称创建了新董事,则应该限制创建,因为具有该名称的董事已经存在,但是如果表中包含“ email”之类的其他列,则该字段应更新,如果导演已经退出了吗?

如果是这种情况,为什么不只将Name作为表的主键?

<?php

require_once('sendgrid-php/sendgrid-php.php');

$name = $_POST["name"];
$email = $_POST["email"];
$header = $_POST["header"];
$message = $_POST["message"];

$from = new SendGrid\Email(null, $email);
$subject = $header;
$to = new SendGrid\Email(null, "MY EMAIL ADDRESS");
$content = new SendGrid\Content("text/plain", $message);
$mail = new SendGrid\Mail($from, $subject, $to, $content);

$apiKey = 'MY API KEY';
$sg = new \SendGrid($apiKey);

$response = $sg->client->mail()->send()->post($mail);
if($response->statusCode() == 202){
    echo "Email sent successfully";
}else{
    echo "Email could not be sent";
}

echo $response->statusCode();
var_dump($response->headers());
echo $response->body(); 

为什么有ID?

答案 1 :(得分:1)

正确的方法是,在第二个操作中,您将从上下文context.Directors.FirstOrDefault (d => d.Name == "A")中找到导演对象并使用返回的对象,而不是尝试重新使用您事先创建的对象。

答案 2 :(得分:0)

更新的答案

然后您要首先将Director插入数据库。 insert语句将使用生成的ID更新内存中的模型,并且您插入的影片不会在数据库中创建新导演。

static void Main()
{
    Director directorA = new Director()
    {
        Name = "A"
    };

    using (var context = new MovieContext())
    {
        context.Directors.Add(directorA);
        context.SaveChanges(); // Here the object directorA will be updated with the right ID. So movie inserts wont create a new Director
    }

    Movie movie1 = new Movie()
    {
        Name = "foo",
        Director = directorA
    };

    Movie movie2 = new Movie()
    {
        Name = "bar",
        Director = directorA
    };

    using (var context = new MovieContext())
    {
        context.Movies.Add(movie1);
        context.SaveChanges();
    }
    using (var context = new MovieContext())
    {
        context.Movies.Add(movie2);
        context.SaveChanges();
    }
}  

您可以使用方法AddOrUpdate。第一个参数是检查其唯一性的字段。因此,如果找到名称,则将更新现有条目,如果不存在,则将插入新模型。

using (var context = new MovieContext())
{
    context.Movies.AddOrUpdate(m => m.Name, movie1);
    context.SaveChanges();
}