Entity Framework : A referential integrity constraint violation occurred when I do EntityState.Modified

6.5k views Asked by At

I create a website in ASP.NET MVC 4. It's been so long that I have this error : A referential integrity constraint violation occurred. It's when i tried to update a entry in my db. Well when someone want to buy a product, i need to change the user who buy.

I have two one to many relationship.

EDIT This my User class :

public class User
{
    private string email, password, firstname, lastname;
    private Adress shippingAdress, billingAdress;
    private bool isConnected;
    private List<Product> products;
    //private List<Auction> auctions;
    private long idShippingA, idBillingA;

    public User()
    {
        products = new List<Product>();
    }

   /* public List<Auction> Auctions
    {
        get { return auctions; }
        set { auctions = value; }
    }

    public void AddAuction(Auction auction)
    {
        if (auction != null)
            auctions.Add(auction);
    }*/

    public long IdBillingA
    {
        get { return idBillingA; }
        set
        {
            if (value < 0)
                throw new ArgumentException("The id of the billing adress should not be negative");
            idBillingA = value;
        }
    }

    public long IdShippingA
    {
        get { return idShippingA; }
        set
        {
            if (value < 0)
                throw new ArgumentException("The id of the shipping adress should not be negative");
            idShippingA = value;
        }
    }

    public bool IsConnected
    {
        get { return isConnected; }
        set { isConnected = value; }
    }

    public virtual List<Product> Products
    {
        get { return products; }
        set
        {
            if (value == null)
                throw new ArgumentNullException("The list of product should not be null");
            products = value;
        }
    }

    public Adress BillingAdress
    {
        get { return billingAdress; }
        set
        {
            if (value == null)
                throw new ArgumentNullException("The billing adress should not be null");
            billingAdress = value;
        }
    }

    public Adress ShippingAdress
    {
        get { return shippingAdress; }
        set
        {
            if (value == null)
                throw new ArgumentNullException("The shipping adress should not be null");
            shippingAdress = value;
        }
    }

    public string Password
    {
        get { return password; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The password should not be null or empty");
            password = value;
        }
    }

    public string Email
    {
        get { return email; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The email should not be null or empty");
            email = value;
        }
    }

    public string Lastname
    {
        get { return lastname; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The lastname should not be null or empty");
            lastname = value;
        }
    }

    public string Firstname
    {
        get { return firstname; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The fistname should not be null or empty");
            firstname = value;
        }
    }
}
    }

EDIT This is my Product Class :

public class Product
{
    private long id, strategyId;
    private User userWhoSell;
        private User userWhoBuy;
    private string userWhoSellId, userWhoBuyId, name, description, urlPicture, isBought;

    public string IsBought
    {
        get { return isBought; }
        set { isBought = value; }
    }
    private SellStrategy strategy;
    private float price;
    private string strategyString;
    public Product()
    {
        isBought = "F";
    }

    public float Price
    {
        get { return price; }
        set 
        {
            if (value < 0)
                throw new ArgumentException("The price should not be negative");
            price = value; 
        }
    }

    public string StrategyString
    {
        get { return strategyString; }
        set 
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The strategy string should not be null, empty or with white space");
            strategyString = value; 
        }
    }

    public long StrategyId
    {
        get { return strategyId; }
        set 
        {
            if (value < 0)
                throw new ArgumentException("The strategy id should not be negative");
            strategyId = value; 
        }
    }

    public SellStrategy Strategy
    {
        get { return strategy; }
        set 
        {
            if (value == null)
                throw new ArgumentNullException("The strategy should not be null");
            strategy = value; 
        }
    }

    public string SellerId
    {
        get { return userWhoSellId; }
        set 
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The user id should not be null, empty or with white space");
            userWhoSellId = value; 
        }
    }

    public string BuyerId
    {
        get { return userWhoBuyId; }
        set 
        {
            userWhoBuyId = value; 
        }
    }

    public string UrlPicture
    {
        get { return urlPicture; }
        set 
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The picture's url should not be null, empty or with white space");
            urlPicture = value; 
        }
    }

    public long Id
    {
        get { return id; }
        set 
        {
            if (value < 0)
                throw new ArgumentException("The id should not be negative");
            id = value;
        }
    }

    public string Description
    {
        get { return description; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The description should not be null, empty or with white space");
            description = value;
        }
    }

    public string Name
    {
        get { return name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("The name should not be null, empty or with white space");
            name = value;
        }
    }

    public virtual User Buyer
    {
        get { return userWhoBuy; }
        set
        {
            userWhoBuy = value;
        }
    }

    public virtual User Seller
    {
        get { return userWhoSell; }
        set 
        {
            if (value == null)
                throw new ArgumentNullException("The user should not be null");
            userWhoSell = value; 
        }
    }
}

EDIT this is my context :

public class Context : DbContext
{
    public Context(string connString)
        : base(connString) { }

    public DbSet<User> Users { get; set; }
    public DbSet<Adress> Adress { get; set; }
    public DbSet<Product> Products { get; set; }

    //public DbSet<Auction> Auctions { get; set; }

    public DbSet<SellStrategy> Strategies { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<User>().HasKey<string>(u => u.Email);
        modelBuilder.Entity<Adress>().HasKey<long>(a => a.Id);
        modelBuilder.Entity<Product>().HasKey<long>(p => p.Id);
        modelBuilder.Entity<Auction>().HasKey<long>(au => au.Id);

        modelBuilder.Entity<Product>()
     .HasRequired(p => p.Seller)
     .WithMany(u => u.Products)
     .HasForeignKey(p => p.SellerId)
     .WillCascadeOnDelete(false);
        // Otherwise you might get a "cascade causes cycles" error

        modelBuilder.Entity<Product>()
           .HasOptional(p => p.Buyer)
           .WithMany() // No reverse navigation property
           .HasForeignKey(p => p.BuyerId)
           .WillCascadeOnDelete(false);

       // modelBuilder.Entity<Auction>().HasMany<User>(au => au.Users).WithMany(u => u.Auctions);
       // modelBuilder.Entity<Auction>().HasRequired(au => au.Product).WithMany().WillCascadeOnDelete(false);

        modelBuilder.Entity<User>().HasRequired(u => u.BillingAdress).WithMany().HasForeignKey(u => u.IdBillingA).WillCascadeOnDelete(false);
        modelBuilder.Entity<User>().HasRequired(u => u.ShippingAdress).WithMany().HasForeignKey(u => u.IdShippingA).WillCascadeOnDelete(false);
        modelBuilder.Entity<User>().Ignore(u => u.IsConnected);



        modelBuilder.Entity<Product>().HasRequired<SellStrategy>(p => p.Strategy).WithMany().WillCascadeOnDelete(false);
        modelBuilder.Entity<Product>().Ignore(p => p.StrategyString);
        modelBuilder.Entity<Product>().Ignore(p => p.Price);

        modelBuilder.Entity<SellStrategy>().Property(s => s.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        modelBuilder.Entity<AuctionSelling>().HasKey<long>(a => a.Id);
        modelBuilder.Entity<AuctionSelling>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("AuctionSelling");
        });
        modelBuilder.Entity<DirectSelling>().HasKey<long>(d => d.Id);
        modelBuilder.Entity<DirectSelling>().Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("DirectSelling");
        });
        modelBuilder.Entity<SellStrategy>().Property(s => s.SoldDate).IsOptional();
    }
}

EDIT And when I try to update my product :

public void UpDateProduct(Product product)
    {
        using(Context context = new Context(connectionString))
        {
            try
            {
                Product p = GetById(product.Id);
                //context.Products.Attach(p);
                p.BuyerId = product.BuyerId;
                p.Buyer = product.Buyer;
                p.IsBought = "T";

                context.Entry(p).State = EntityState.Modified;
                context.SaveChanges();
            }
            catch (Exception ex)
            {
            }
        }
    }

This is the line

context.Entry(p).State = EntityState.Modified;

that provoke the error.

This is the full error

A referential integrity constraint violation occurred: The property value(s) of 'User.Email' on one end of a relationship do not match the property value(s) of 'Product.BuyerId' on the other end.

When I do just context.saveChanges, nothing occurs... I don't know what to do... I want to cry ^^... Thank you a lot in advance !

1

There are 1 answers

4
Gert Arnold On BEST ANSWER

This is caused by setting reference navigation properties in the constructor. Specifically:

userWhoSell = new User();
userWhoBuy = new User();

This means that a Product starts out having two dummy User objects that you have to replace for them to become meaningful. Otherwise EF may try to save these dummy objects. I'm pretty sure that by...

context.Entry(p).Entity.Buyer = product.Buyer;

...you actually set such an empty User object, which has a key value no email, not the key value Product.BuyerId you apparently set before.

I'd say: remove most of these initializations in the entities' constructors. Initializing collections makes sense. Sometimes setting default values does (but not primary key value), but reference navigation properties never. See also: EF codefirst : Should I initialize navigation properties?

Side note: neither would I use property setters for validations. That's what data annotations are for. These exceptions may interfere with EF when trying to materialize entities from the database.