0

enter image description hereenter image description hereThis should be really simple but I think I'm having possible issues with my model. I have been working with linq over a year and I should have this simple remove easily done. Please help! It's removing both records from the database when I only want one deleted

I have a database table with these properties.

Email, EmployeeName, StoreId
jsch@m.com,Joe Schneider,9
jsch@m.com,Joe Schneider,8

I need to delete Joe Schneider with storeId 9

So I run this simple query and remove process.

        var temp2 = difference[i];
        var PersonToRemove = db.Permissions.SingleOrDefault(s => s.EmployeeName == temp2 && s.StoreId == Persons.StoreId);
        if (PersonToRemove.EmployeeName != null)
        {
            db.Permissions.Remove(PersonToRemove);
            db.SaveChanges();
        }

I am assuming one is going to say, hey your model is not right and don't put the name as a key, but I can't just be changing the model because other parts of the app are based on this model and would cause huge breaks. Could you give me advise how to edit the linq query to not delete both records?

model

  [Table("Permissions")]
    public class Permissions
    {
        [Key]
        public String EmployeeName { get; set; }

        public string Department { get; set; }
        public int? StoreId { get; set; }

        public String Email { get; set; }
    }
jarlh
  • 42,561
  • 8
  • 45
  • 63
JSkyS
  • 413
  • 6
  • 14
  • 3
    Both record have the same key (EmployeeName). The delete probably delete by looking at the key only. – the_lotus Nov 20 '19 at 19:28
  • 4
    How did you manage to insert duplicate values which is marked as the key – Eldar Nov 20 '19 at 19:29
  • @ EldarI don't know why it allows me to insert duplicate values which is marked key but it shows up that way in ssms. @Suphant Persons.StoreId is being passed as a parameter in my method which in this case the value will be 9, because I want to delete Joe with StoreId 9 – JSkyS Nov 20 '19 at 19:32
  • There is a bug or sth like that. Normally Sql Server doesn't allow duplicate primary key values. `public String EmployeeName { get; set; }` using `String` instead `string` may be causing wrong DDL generation. Check your table with a designer see if it is alright. – Eldar Nov 20 '19 at 19:37
  • 2
    I think you have a bigger issue of duplicate keys, which is causing this issue – zgood Nov 20 '19 at 19:37
  • It might help if you post the table definition as SQL (SSMS can generate this for you), or a screenshot from SSMS showing the primary key. – Stephen Kennedy Nov 20 '19 at 19:40
  • @Eldar I can change then to string instead. What else should I do? I also notice that when I do a query to check which store's he is in, it gives me 2 records with storeId 9. So it recognizes there are two entries but shows the query result as two duplicates, when in SSMS I can see that there is one with storeId 8 and storeId 9 – JSkyS Nov 20 '19 at 19:41
  • I guess i figured it out. You have no `MaxLength` specified may be Sqlserver preventing defining primary keys on NVARCHAR(MAX) columns. Yes i found this https://stackoverflow.com/questions/10555642/varcharmax-column-not-allowed-to-be-a-primary-key-in-sql-server – Eldar Nov 20 '19 at 19:43
  • I have included the image of the table design from SSMS. Should I add another column full of null values and make that the key? I'm not sure. I also added [MaxLength(255)] as Eldar recommended, and it still deletes both records – JSkyS Nov 20 '19 at 19:57
  • 1
    Employee name seems like a bad idea for a Key... people can have the same names. Why don't you just have an Id identity int column? – zgood Nov 20 '19 at 19:59
  • What should I do then @zgood? Can I make my departments table which is all null values a key? – JSkyS Nov 20 '19 at 20:20
  • @JSkyS What does your table actually holds? permissions per employee to the store? – gsharp Nov 20 '19 at 21:01
  • Yes it holds the the which employee has permissions to which store. If they are in the database then they can get access to their information. I am following zgoods recommendation now and creating a new column which auto increments. I am backing up first though – JSkyS Nov 20 '19 at 21:33
  • My initial assumptions were correct about making that email a key in the model. It was made so because of an early work around but had finally caught up to us. As @zgood suggested we added a column with Id Identity int column. – JSkyS Nov 20 '19 at 23:17
  • The new model now includes public int ID { get; set; } as the new key. From the beginning I knew this was an option but was afraid of altering the table on the server side since there was logic in multiple controllers in multiple apps that were utilizing it's data. Whew! – JSkyS Nov 20 '19 at 23:19

2 Answers2

1
[Table("Permissions")]
public class Permissions
{
    [Key]

    public String EmployeeName { get; set; }
}

The problem is here you are defining a primary key which has no length constraint on it. (MaxLength). This leads to EF generate a column with NVARCHAR(MAX). As mentioned here VARCHAR(MAX) columns are not allowed to be primary key. So correct definition should be like below

[Table("Permissions")]
public class Permissions
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)] <--
    [MaxLength(255)] // <---
    public String EmployeeName { get; set; }
}

Edit: You need to recreate the database in order to associated tables initialized with correct settings. Edit 2 : Also you may need a DatabaseGenerated(DatabaseGeneratedOption.None) since its not identity column.

Eldar
  • 9,781
  • 2
  • 10
  • 35
  • I added an image of the details of the table. I also added the MaxLength(255) part but its still deleting both – JSkyS Nov 20 '19 at 19:54
  • You need to recreate the database. – Eldar Nov 20 '19 at 20:43
  • Well I couldn't recreate the database because that's two and a half years worth of creating and modifying tables, but I did recreate the table. Thank you for your input all of you collectively led me to add a ID column that auto increments. I was afraid to do so since it was already live, but now that I did so I had to alter my models and everything worked as expected. Thanks! – JSkyS Nov 20 '19 at 23:27
1

you can set Deleted state on individual entity like so:

var temp2 = difference[i];
var PersonToRemove = db.Permissions.SingleOrDefault(s => s.EmployeeName == temp2 && s.StoreId == Persons.StoreId);
if (PersonToRemove.EmployeeName != null)
{
    db.Entry(PersonToRemove).State = EntityState.Deleted; // do this instead
    db.SaveChanges();
}

EF should then figure out which entity you wanted to delete

UPD I am assuming you are using EF6 and DB-first approach. I am also assuming you've got your DB context class set up with default convention model builder. It seems EF's default object tracking based on Key will not work as your key is not unique (this is a bigger problem, but I understand you're already aware of that).

You might try circumvent that convention by adding custom model builder configuration like so:

class MyDbContext : DbContext { 
    public virtual DbSet<Permissions> Permissions {get;set;}

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Permissions>().HasKey(p => new { p.EmployeeName, p.StoreId});
    }
}

since you didn't share your DbContext definition this is just a snippet but hopefully gives you some ideas to explore. this is the API reference: https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.modelconfiguration.entitytypeconfiguration-1?view=entity-framework-6.2.0

timur
  • 14,239
  • 2
  • 11
  • 32
  • Still both gone timur – JSkyS Nov 20 '19 at 20:18
  • I see you posted column properties outlining that EmployeeName is a Key - can we see corresponding table definition then? I am a bit curious how you can then have two different records with non-unique EmployeeName in your example data - is there a composite key somewhere? – timur Nov 20 '19 at 20:25
  • 1
    Entity Can't decide what you gonna delete from the database. It decides what entry it will mark with deleted flag as you did above. All it comes to generate a SQL statement. And that SQL statement will delete both values because of wrong primary key setup. – Eldar Nov 20 '19 at 20:47