0

I'm having some issues with Entity Framework on .NET Framework. My app is designed as such:

I have my class that inherits from DbContext and I have this property:

public DbSet<User> Users { get; set; } // ...other properties protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<UserManageableZones>() .HasRequired(umz => umz.User) .WithMany(u => u.ManageableZones) .HasForeignKey(umz => umz.Username) .WillCascadeOnDelete(false); } 

My User model is something like this:

[Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] [Required] [MaxLength(128)] [Column("USERNAME")] public string Username { get; set; } = string.Empty; public virtual ICollection<UserManageableZones> ManageableZones { get; set; } public User() { ManageableZones = new HashSet<UserManageableZones>(); } 

As you can see, I have the property ManageableZones, that is the table I want to relate with, with a 1-to-many relationship. So basically one user can have many UserManageableZones (for simplicity's sake, a zone can be related with only 1 user, logically this is incorrect, but I don't care about having a many-to-many relationship).

This is my UserManageableZones:

public virtual User User { get; set; } [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Required] [Column("ID")] public int Id { get; set; } [ForeignKey("User")] [Required] [Column("USERNAME")] public string Username { get; set; } // ...other properties 

Then, I have a class that is basically a projection of the data from my database:

public Class DataContext { public List<User> Users {get; set;} public DataContext() { Users = new List<User>(); } } 

DataContext is the "static" object with which I'll interact with data, adding/deleting/updating and so on. Then I'll save this data back to the database.

To do so, I have a DataAccess class, that basically inits the DataContext by populating all its properties with database's data, and updating database data with the one from the DataContext.

To save the data into the database, I have this, IMO, horrible method that unfortunately I didn't really understand how it works, so I didn't really ever touch it

private void SaveTable<T1, T2>(List<T1> entity, bool canUpdate = true, bool canDelete = true) where T1 : class { PrintCallStack(false); try { AMMContext db; if (AMMContext.DbType == Entity.eDbType.SqLite) db = new AMMContext(AMMContext.LoadConnectionString()); else db = new AMMContext(); using (db) { List<T1> listInDB = (List<T1>)Activator.CreateInstance(typeof(List<T1>)); DbSet<T1> dbSet = null; foreach (PropertyInfo pInfo in typeof(AMMContext).GetProperties()) { if (pInfo.GetValue(db) != null) { if (pInfo.GetValue(db).GetType() == typeof(DbSet<T1>)) { dbSet = (DbSet<T1>)pInfo.GetValue(db); listInDB = dbSet.ToList(); } } } List<T2> idEntityList = null; if (entity != null) idEntityList = entity.Select(y => (T2)GetPrimaryKeyValue(y)).ToList(); //List<T1> updating = entity.Where(x => idListinDb.Contains((T2)GetPrimaryKeyValue(x))).ToList(); //List<T1> adding = entity.Where(x => !idListinDb.Contains((T2)GetPrimaryKeyValue(x))).ToList(); if (canUpdate) dbSet.AddOrUpdate(entity.ToArray()); if (canDelete) { List<T1> deleting = dbSet.ToList().Where(x => !idEntityList.Contains((T2)GetPrimaryKeyValue(x))).ToList(); deleting.ForEach(x => dbSet.Attach(x)); dbSet.RemoveRange(deleting); } db.SaveChanges(); } } catch (Exception e) { try { PrintCallStack(true); Log.ErrorFormat($"DataAccess.Save error, transaction rollback occurred | exc:{e.InnerException.InnerException.Message}"); } catch (Exception ex) { Log.ErrorFormat($"DataAccess.Save error, rollback procedure failure"); StringBuilder sb = new StringBuilder(); sb.Append("For entity type : "); if (entity != null) { foreach (T1 item in entity) { sb.Append(item.GetType().Name); sb.Append(" - "); } } Log.ErrorFormat(sb.ToString()); } } } 

This method is called by the DataAccess all around the code, wrapped inside another method that does something like this

public void Save(bool allFilter = true, bool usersFilter = false, ...other filters) { if (ammUsersFilter | allFilter) SaveTable<User, string>(entity: DataContext.Users, canUpdate: true, canDelete: !initialConditions); // ...same for other filters } 

That's about it. The problem is that the collection of UserManageableZones inside User, it's not getting saved in its table. As it is right now, when the app starts from scratch, the database is created and also the table USER_ZONES is created.

I've tried to manually create a record inside the USER_ZONES table, then boot up the app and saw that when the data is gathered from the database, I actually have that record inside the user. This makes me think that relation wise, my work is correct. But I have no clue about what I'm missing.

Ah, just to mention. Inside my USER table, I have no column related to USER_ZONES, but I think it's correct. That's it.

These are the script generated of both tables

CREATE TABLE AMM_USER ( USERNAME VARCHAR(128) NOT NULL, ... CONSTRAINT PK_AMM_USER PRIMARY KEY (USERNAME) ); CREATE TABLE USER_ZONES ( ID INTEGER NOT NULL, USERNAME VARCHAR(128) NOT NULL, CONSTRAINT PK_USER_ZONES PRIMARY KEY (ID), CONSTRAINT FK_USER_ZONES_AMM_USER_USERNAME FOREIGN KEY (USERNAME) REFERENCES AMM_USER(USERNAME) ); CREATE INDEX FK_USER_ZONES_AMM_USER_USERNAME ON USER_ZONES (USERNAME); CREATE INDEX IX_USER_ZONES_USERNAME ON USER_ZONES (USERNAME); 
3
  • Foreign keys are constraints, not relations. This "data class" is a bad design and the actual cause of any problems. A DbContext is a multi-entity Unit-of-Work, at least two abstraction levels above such CRUD classes. It's meant to hold the entities/objects needed for a specific use case and persist all of them in a single transaction when SaveChanges is called. It's not a database model or connection - it doesn't have to even open a connection until it has to load or save data. A DbContext is meant to be disposed when the use case/scenario/action ends, to discard unwanted changes Commented Jan 27 at 16:22
  • 1
    In other words, SaveTable's SaveChanges can easily perform 36 DELETES and 47 UPDATES on unrelated "tables" if there are any pending changes. That's a problem of SaveTable, not SaveChanges. Commented Jan 27 at 16:26
  • Ye, im not arguing with that. I know it's not an entity framework direct problem. And i agree with you, this infrastructure it's bad, but wasn't my choice and i can't edit it. So my question is: is it possibile to keep this kind of structure and make everything work? @PanagiotisKanavos Commented Jan 27 at 16:33

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.