How to force an Exception when trying to delete a Master record that already contains relationship in a Child Record?

Asked

Viewed 226 times

3

I created the following structure in SQL Server:

relação

Using Entityframework with Code-First the classes stayed that way:

[Table("Master")]
public class Master
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
}

[Table("Child")]
public class Child
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public string Description { get; set; }

    [ForeignKey("Master")]
    public int? MasterId { get; set; }
    public virtual Master Master { get; set; }
}

See that MasterId for being null (int?).

Still, setting the Foreign key raised in Child to not delete in cascade (setando In Action) expected that when trying to delete a record on Master, which has already been related to Child, generate a Exception.

Mindful of the fact that I have removed the cascading deletion conventions:

public class DataContext : DbContext
{
    public DataContext()
        : base("Data Source=(local); Initial Catalog=TestFK; Integrated Security=True")
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Master> Masters { get; set; }
    public DbSet<Child> Childs { get; set; }
}

Delete with No Acton

So I tested this hoping to get one Exception, but it did not occur:

class Program
{
    static void Main(string[] args)
    {
        using (var ctx = new DataContext())
        {
            ctx.Masters.Add(new Master { Id = 22 });
            ctx.Childs.Add(new Child { Description = "Teste 1", MasterId = 22 });
            ctx.SaveChanges();

            var versao = ctx.Masters.SingleOrDefault(x => x.Id == 22);
            if (versao != null)
            {
                ctx.Masters.Remove(versao); // <-- esperava que isso geraria uma exception
                ctx.SaveChanges();
            }
        }
    }
}

Otherwise, the record was deleted and the value in Child was set to null.

select em master após o processo

select em child após o processo

How to force this Exception?
Missing any settings? It’s standard behavior?


To conclude, the operation being done directly in the database returns a Exception, as expected.

Operação feita manualmente

  • Why do you want Masterid to be null?

  • Hello @ramaral! Well, of course this is just a small example of my real problem. And MasterId in Child may stay a long time or never receive a reference from Master,

  • There’s the answer to your question. Entityframework behaves like this because, when you indicate that Masterid can be null, you are indicating that you want children without parents. Entityframework has no way of guessing that you only want this in some situations.

  • @ramaral, I get it. But this must have some way to get around, since it is not the default behavior of SQL Server. As I just updated the question showing that an exception is manually generated.

  • Strange! I don’t see why the behavior is different.

1 answer

2


It’s a standard behavior?

Yeah. Notice your Master (the parent record) can be null on the child (it is called the "orphan record"), so there would be no reason to generate an exception since your bank allows this exclusion. What the Entity Framework does is tidy up the records for you to ensure relational consistency.

In this case, making an exception would be a business rule:

class Program
{
    static void Main(string[] args)
    {
        using (var ctx = new DataContext())
        {
            ctx.Masters.Add(new Master { Id = 22 });
            ctx.Childs.Add(new Child { Description = "Teste 1", MasterId = 22 });
            ctx.SaveChanges();

            var versao = ctx.Masters.SingleOrDefault(x => x.Id == 22);

            if (ctx.Childs.Any(c => c.MasterId == versao.Id)) {
                throw new Exception("Não é possível excluir registro pai que tenha filhos.");
            }

            // Comentei porque SingleOrDefault dá exceção quando o registro vem nulo, 
            // então esse condicional não faz muito sentido.
            // if (versao != null)
            // {
                ctx.Masters.Remove(versao); // <-- esperava que isso geraria uma exception
                ctx.SaveChanges();
            // }
        }
    }
}

About the exception

The Entity Framework first defines MasterId in Child as null before excluding Master because you know the relationship and you know there’s a foreign key there.

  • From what I understand, despite "In Action", Entityframework ignores and assumes a "Set Null". Isn’t there a way around this in the Bank? I ask, although you have already said that the solution is in the application. I need to secure that from the database as well.

  • At the bank you can do Trigger, but becomes dependent on technology. The ideal is to solve via business rule. There are people who won’t like what I’m going to say, but the rule could be settled in a Controller or in a Helper.

  • 1

    Okay! Thanks for the help!

  • Gypsy, did you see the change to the question? Can you give an explanation for this?

  • @ramaral, setar null is from Entityframework itself.

  • @ramaral I updated the response.

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.