Entity Framework Creates unwanted relationship between abstract and derived tables - c#

Using code first, I have some abstract classes and some classes derived from those abstracted classes.
// Abstracted Classes
public abstract class Brand
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class Model
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
// Derived Classes
[Table("ComparisonBrand")]
public class ComparisonBrand : Brand
{
public ComparisonBrand()
{
ComparisonValues = new List<ComparisonValue>();
Models = new List<ComparisonModel>();
}
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
public virtual ICollection<ComparisonModel> Models { get; set; }
}
[Table("ComparisonModel")]
public class ComparisonModel : Model
{
public int? BrandId { get; set; }
public int? LogoId { get; set; }
[ForeignKey("BrandId")]
public virtual ComparisonBrand ComparisonBrand { get; set; }
[ForeignKey("LogoId")]
public virtual ComparisonLogo ComparisonBrand { get; set; }
public virtual ICollection<ComparisonValue> ComparisonValues { get; set; }
}
My issue is that the migration generates foreign keys for:
ComparisonModel.Id > Models.Id
ComparisonModel.BrandId > Brands.Id
ComparisonModel.BrandId > ComparisonBrand.Id
Since ComparisonBrand.Id is a FK to Brands.BrandId, I get an error when deleting a Brand record. If I delete the ComparisonModel.BrandId > ComparisonBrand.Id relationship, however, the delete works fine.
How can I prevent a relationship from being formed between both the abstracted table and the derived table (Brands and ComparisonBrand)?

You are using the virtual keyword this causes Lazy Loading. You are telling EF to generate Foreign keys for them through this feature. Drop the virtual and you will not create the keys any longer

Related

EF Core Table-Per-Hierarchy for class with generic

In EF Core 3.1.15 I manage a model with a generic. I would like to store the entities in the same table basis Table-Per-Hierarchy approach (TPH pattern). Below is the model abstracted. The resulting database creates 1 table for Part and descendants with a discriminator (as expected), but instead of 1 table for BaseComputer and descendants it creates a separate table for Computers and a separate table for Laptops (not expected).
namespace EFGetStarted
{
public class BloggingContext : DbContext
{
public DbSet<Computer> Computers { get; set; }
public DbSet<Laptop> Laptops { get; set; }
public DbSet<Part> Parts { get; set; }
}
public abstract class BaseComputer<T> where T : Part
{
[Key]
public int Id { get; set; }
public List<T> Parts { get; set; }
}
public class Computer : BaseComputer<Part>
{
public string ComputerSpecificProperty { get; set; }
}
public class Laptop : BaseComputer<LaptopPart>
{
public string LaptopSpecificProperty { get; set; }
}
public class Part
{
[Key]
public int Id { get; set; }
public string PartName { get; set; }
}
public class LaptopPart : Part
{
public string LaptopSpecificPartProperty { get; set; }
}
}
I tried explicitly specifying the entity as TPH:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseComputer<Part>>()
.HasDiscriminator()
.HasValue<Computer>("Computer")
.HasValue<Laptop>("Laptop");
}
But this fails with the following message:
The entity type 'Laptop' cannot inherit from 'BaseComputer' because 'Laptop' is not a descendant of 'BaseComputer'.
Questions: Is it possible for me to design this model in a TPH pattern? If not, is it because "Laptop is not a descendant of BaseComputer<Part>"? And if that's the case, why is not a considered a descendant and what should I change in the class to make it a descendant?

Creating different tables using using same base abstract class

I have 2 models which have exactly same fields, but I chose to make different models for them because I needed two different tables, one for each.
Earlier everything was working fine when I had two different tables for each model, but then I started using abstract base class because the code inside both the models were same.
Now I have a single table comprised of all the data that I save.
How can I create different tables for those two models.
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleId { get; set; }
public string ruleApplicable { get; set; }
[ForeignKey("ruleId")]
public virtual ruleTable RuleTable { get; set; }
}
The one shown above is my abstract base class.
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Just if someone intrested in ruleTable model.
public class ruleTable
{
[Key]
public string ruleId { get; set; }
public string topic { get; set; }
public string rule { get; set; }
public string example { get; set; }
public virtual ICollection<baseGrammar> BaseGrammar { get; set; }
}
Am also adding context class so as to provide better description
public class english : DbContext
{
public english() : base("name=localServerEng")
{
Database.SetInitializer<DbContext>(null);
Database.SetInitializer<english>(new UniDBInitializer<english>());
}
public virtual DbSet<adjective> adjectiveDb { get; set; }
public virtual DbSet<adverb> adverbDb { get; set; }
public virtual DbSet<alternativeVerb> alternativeVerbDb { get; set; }
public virtual DbSet<antonyms> antonymsDb { get; set; }
public virtual DbSet<article> articleDb { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseIfModelChanges<english>
{
}
public System.Data.Entity.DbSet<StructureSSC.Areas.AreaEnglish.Models.baseGrammar> baseGrammars { get; set; }
}
Screenshot of SQL Server showing 1 table comprising of all columns instead of different tables
This set up will give you 2 tables: (1) adjectives (2) articles
The context should be like this:
public class SomeContext : DbContext
{
public SomeContext()
: base("name=SomeContext")
{
}
public virtual DbSet<article> Articles { get; set; }
public virtual DbSet<adjective> Adjectives { get; set; }
}
public abstract class baseGrammar
{
//... common properties/columns
}
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Please note the naming convention. In .NET class names and property names should follow Pascal Notation. Therefore, they should be:
BaseGrammar
Article
Adjective
RuleApplicable // other properties should follow same convention

Independent child object with references from multiples parents in EF

I'm having quite the issue right now while trying to learn Entity Framework.
Let's say I have this entity:
public class BuildingGroup {
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
And also this entity.
public class Architect {
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
These two entities are completely unrelated. Here's the Building entity:
public class Building {
public int ID { get; set; }
public string Address { get; set; }
}
My problem happens when I try to add a building to, say a BuildingGroup. In my domain model, I can modify the equivalent collection of buildings, by adding, modifying or removing buildings. However, when I try to update BuildingGroup through a repository, the buildings will not be updated.
public void Update(BuildingGroup buildingGroup) {
var buildingGroupEntity = _context.BuildingGroups.Single(b => b.ID == buildingGroup.ID);
// This will not map the Building collection
context.Entry(buildingGroupEntity).CurrentValues.SetValues(buildingGroup);
// My attempt at mapping the buildings
buildingGroupEntity.Buildings.Clear();
buildingGroup.Buildings.ToList().ForEach(b => buildingGroupEntity.Buildings.Add(_context.Buildings.Single(x => x.ID == b.ID)));
_context.Entry(buildingGroupEntity).State = EntityState.Modified;
}
This fails if the building were not saved in the database prior to the call to Update(), which is normal since buildings can live independently. It must also be done for every child collection of BuildingGroup (if there were more), and for child collections of these children, well...
I have noticed other people use a foreign key constraint in the child object (here, Building), but I can't really do that since many unrelated entities can point to a building: I'd have a lot of navigation properties.
Is there a graceful way to manage referencing objects that can also live independently from those who hold references to them?
If all the entities have to exist independently, yet have relationships with each other, it's better to use many-to-many relationship.
Change your model classes as follows, the Building should contain a couple of collections for architects and groups.
public class BuildingGroup
{
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
public class Architect
{
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
public class Building
{
public int ID { get; set; }
public string Address { get; set; }
public virtual ICollection<Architect> Architects { get; set; }
public virtual ICollection<BuildingGroup> BuildingGroups { get; set; }
}
If you use entity type configuration, you could define the relationship as follows:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Building>().HasMany(it => it.Architects).WithMany(it => it.BuildingsBeingWorkedOn);
modelBuilder.Entity<Building>().HasMany(it => it.BuildingGroups).WithMany(it => it.Buildings);
base.OnModelCreating(modelBuilder);
}
}

Composite Key and using Multiple Column Attributes in Entity Framework 6

I am trying to create a composite key using two fields when using code first to existing fields in a table in a db;
[Key,Column("driverId", Order=0)]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
[Key,Column("type", Order=1)]
[JsonProperty(PropertyName="typeid")]
public int Type { get; set; }
Now when I try to run a new migration i get the following error;
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
DriversToVehicle_Driver_Target_DriversToVehicle_Driver_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
The DriversToVehicle table is as follows;
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid")]
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public Driver Driver { get; set; }
[Column("vehicleid")]
public int VehicleID { get; set; }
[ForeignKey("VehicleID")]
public Vehicle Vehicle { get; set; }
}
Extending this question, originally a single key on the ID, i.e.
[Column("driverId")]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
Now moving forward, how will this effect the other entities linking to it (by this i mean code first in the classes)? will ef automatically sort this out? or do I now need to have both keys in other entities when linking to this class?
e.g. as before I would have had
public virtual Driver myDriver;
Obviously now instead of linking on the ID alone it needs to be linked with the Type as well.
Thanks in advance.
EDIT FOR ANSWER
Ok, I extracted the Type out to a seperate class. The main issue is now How do i mark the foreign key as also being a composite key?
I have the following classes
public partial class DriverType
{
[Column("Id")]
[JsonProperty(PropertyName = "drivertypeid")]
public override int ID { get; set; }
[JsonProperty(PropertyName = "drivertype")]
public string Name { get; set; }
}
Then in the Driver I have the following (reduced for brevity);
public partial class Driver : AuditableEntity<int>
{
[Key,Column("driverId", Order=0)]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
[Key,Column("type", Order=1)]
[ForeignKey("DriverType")]
[JsonProperty(PropertyName="drivertypeid")]
public int DriverTypeId { get; set; }
public DriverType DriverType { get; set; }
How do I then add it to the DriverToVehicle class please? So far I have
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid", Order=0), ForeignKey("Driver")]
public int DriverID { get; set; }
public Driver Driver { get; set; }
[Column("type", Order = 1), ForeignKey("Driver")]
public int DriverTypeId { get; set; }
[ForeignKey("DriverTypeId")]
public DriverType DriverType { get; set; }
}
This doesnt look right to me though?
Since your Drivers table's Primary Key is now (DriverId, Type), you can no longer reference your drivers by DriverId alone - you must reference them by both DriverId and Type. Therefore, your DriversToVehicle table needs to look something like this:
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid")]
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public Driver Driver { get; set; }
[Column("DriverType")]
public int DriverType { get; set; }
[ForeignKey("type")]
public int DriverType { get; set; }
[Column("vehicleid")]
public int VehicleID { get; set; }
[ForeignKey("VehicleID")]
public Vehicle Vehicle { get; set; }
}
However, as #hopeless states above, you may not need to model this join table if you correctly model your Driver and Vehicle types correctly.
HTH.

Entity framework, issue saving data in many-to-many relationship

I have issue saving data in many-to-may relationship between two tables that breaks by introducing another table in between, containing primary keys of both. I have code first existing database approach along with repository pattern and unit of work in MVC application
and here is my model classes
Navigation_Functions
public class Navigation_Functions
{
public Navigation_Functions()
{
}
[Key]
public int Function_ID { get; set; }
[StringLength(250)]
[Required(ErrorMessage = "Required Title")]
[Display(Name = "Function Title")]
public string FunctionName { get; set; }
[Required(ErrorMessage = "Required Hierarchy Level")]
[Display(Name = "Hierarchy Level")]
public int Hierarchy_Level { get; set; }
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
}
Navigation_FunctionController Model
public class Navigation_FunctionController
{
public Navigation_FunctionController()
{
}
[Key]
public int ControllerID { get; set; }
[StringLength(250)]
[Required]
public string ControllerName { get; set; }
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
Junction Model
[Table("Navigation_FunctionInController")]
public class Navigation_FunctionInController
{
public Navigation_FunctionInController()
{
}
[Key]
public int FunctionInController_ID { get; set; }
[Key]
[ForeignKey("Navigation_Functions")]
public int Function_ID { get; set; }
[Key]
[ForeignKey("Navigation_FunctionController")]
public int ControllerID { get; set; }
public Navigation_FunctionController Navigation_FunctionController { get; set; }
public Navigation_Functions Navigation_Functions { get; set; }
}
I have generic repository for CRUD operation
public void InsertEntity(TEntity obj)
{
_DbSet.Add(obj);
}
My ViewModel
public class FunctionsNavigation_ViewModel
{
public Navigation_Functions _Navigation_Functions { get; set; }
public Navigation_FunctionController _Navigation_FunctionController { get; set; }
}
public void CreateFunctionNavigation(FunctionsNavigation_ViewModel _obj)
{
using (var _uow = new FunctionsNavigation_UnitOfWork())
{
try
{
var _navigationFunction = _obj._Navigation_Functions;
_navigationFunction.Navigation_FunctionController = new List<Navigation_FunctionController>();
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
_uow.Navigation_Functions_Repository.InsertEntity(_navigationFunction);
_uow.Save();
}
catch
{
}
}
}
if I remove following line from above code then it save new Navigation_Functions
_navigationFunction.Navigation_FunctionController.Add(_obj._Navigation_FunctionController);
following is screen shot from debug code.
I am wondering if my ViewModel are correct? secondly How Entity Framework knows that it need to put primary keys of two tables in Navigation_FunctionInController?
When your model looks like this...
public class Navigation_Functions
{
...
public ICollection<Navigation_FunctionController> Navigation_FunctionController { get; set; }
}
public class Navigation_FunctionController
{
...
public ICollection <Navigation_Functions> Navigation_Functions { get; set; }
}
...so without a Navigation_FunctionInController class, the junction table in the database (Navigation_FunctionInController) is not represented in the class model. If you have this model code first, EF will create a junction table itself. If you work database first, EF won't create a junction class and in the diagram you will see a pure many to many association, like this: *--*. But this only happens if the junction table only contains the two foreign keys, both of which comprise a compound primary key.
In your model you have an explicit junction class (probably because the table has a primary key field besides the two foreign keys). This means that the many to many association turns into a 1:n:1 association. The class model should essentially look like this...
public class NavigationFunction
{
...
public ICollection<NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionController
{
...
public ICollection <NavigationFunctionInController> NavigationFunctionInControllers { get; set; }
}
public class NavigationFunctionInController
{
public int FunctionInControllerID { get; set; }
[ForeignKey("NavigationFunction")]
public int FunctionID { get; set; }
[ForeignKey("NavigationFunctionController")]
public int ControllerID { get; set; }
public NavigationFunctionController NavigationFunctionController { get; set; }
public NavigationFunction NavigationFunction { get; set; }
}
Note that the foreign key fields don't have to be part of the primary key (also not that I prefer the singular name Navigation_Function and plural names for collections, and no underscores in names).
So what happens in your code is that you have a 1:n:1 association, but you try to manage it like a m:n association by adding items directly to _navigationFunction.Navigation_FunctionController(s) . But EF doesn't track that collection, and the items are not saved.
Instead you have to create a junction class instance...
var nfic = new NavigationFunctionInController
{
NavigationFunction = obj.NavigationFunction,
NavigationFunctionController = obj.Navigation_FunctionController
};
...and save it through your repositories and UoW.

Categories

Resources