I have so strange question connected with realization of dictionary (handbook, etc.) with description of, for example, work types. Let me explain.
Let me have 2 classes:
public class Order
{
public Guid Id { get; set; }
public ICollection<WorkType> WorkTypes { get; set; }
}
public class PurchaseOrder
{
public Guid Id { get; set; }
public ICollection<WorkType> WorkTypes { get; set; }
}
where
public class WorkType
{
public int Id { get; set; }
public string Description { get; set; }
}
and my context is:
public class MyDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<PurchaseOrder> { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
When I make a migration, I get 2 additional columns in my WorkType table: OrderId and PurchaseOrderId.
So, I don't want to have them in this table, of course. Is it possible to make this N:M relation without FK?
Possible I can have WorkType collections in many classes.
Thank you.
P.S. I use EF Core 6.0.6
Related
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?
I try to create the following database design with EF Core 3.1 (code-first)
Entity "Recipe" can have a list of type "Resource" and a single type of "NutritionFacts"
Entity "Ingredient" can have a single "NutritionFacts"
Entity "Instruction" can have a list of type "Resource"
But I found no way to implement this without having multiple "NutritionFacts" or "Resource" tables. (RecipeNutritionFacts-/IngredientNutritionFacts)
And I also don't want to blow up my "Recipe" or "Ingredient" tables with the columns from the "NutritionFacts-/Resource" entities. (owned types)
Goal: I would like to reuse the tables "Resource" and "NutritionFacts" in multiple entities.
If I delete a "Resource" from the collection in entity "Recipe" or "Instrucion" then the corresponding "Resource" entity should be also deleted. (recipe.Resource.remove(x))
Same for a "NutritionFacts" from entity "Recipe" or "Ingredient" (recipe.NutritionFacts = null)
I already tried several combinations with owned types, etc... but with no success.
Any ideas for a good implementation to reach this goal?
Example Classes:
public class NutritionFacts
{
public int NutritionFactsId { get; set; }
public decimal Kcal { get; set; }
public decimal Fat { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
}
public class Ingredient
{
public int IngredientId { get; set; }
public string Title { get; set; }
public virtual NutritionFacts NutritionFacts { get; set; }
}
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public virtual NutritionFacts NutritionFacts { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Instruction
{
public int InstructionId { get; set; }
public string Title { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
Example Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// variant-1 owned
modelBuilder.Entity<Ingredient>().OwnsOne(x => x.NutritionFacts).HasKey(r => r.UniqueIdentifier);
modelBuilder.Entity<Entities.Recipe>().OwnsOne(x => x.NutritionFacts).HasKey(r => r.UniqueIdentifier);
// variant-2
modelBuilder.Entity<Entities.Recipe>()
.HasOne(p => p.NutritionFacts).WithOne();
modelBuilder.Entity<Ingredient>()
.HasOne(p => p.NutritionFacts).WithOne();
}
I have existing tables including the mapper table. I have to set the Entity Framework annotations. I am confused about how to achieve that. there are three tables,
Model (ModelId, ModelName),
Department (DepartmentId, DepartmentName)
ModelDepartmentMapper (ModelDepartmentMapperId, ModelId, DepartmentId, ModelStatus)
I have created the classes as:
public class Model
{
public int ModelId { get; private set; }
public string ModelName { get; private set; }
public virtual ICollection<Department> Departments { get; private set; }
}
public class Department
{
public int DepartmentId { get; private set; }
public string DepartmentName { get; private set; }
public virtual ICollection<Model> Models { get; private set; }
}
public class JoinModelDepartment
{
public int JoinModelDepartmentId { get; private set; }
public Guid ModelId { get; private set; }
public Guid DepartmentId { get; private set; }
public int ModelStatus { get; private set; }
}
And in DBContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Model>()
.HasMany<Department>(s => s.Departments)
.WithMany(c => c.Models)
.Map(cs =>
{
cs.MapLeftKey("ModelGuid");
cs.MapRightKey("DepartmentGuid");
cs.ToTable("JoinModelDepartment");
});
}
Please guide me how to add ModelStatus in the mapper table. Do I need to create it manually or there is a way to do this?
What I would tend to do is move these kinds of mappings into their own class files. Theres a brilliant tutorial found here:
https://www.entityframeworktutorial.net/code-first/move-configurations-to-seperate-class-in-code-first.aspx
This lets you map out each of the tables individually and it will build the database off of them.
So in your case you would have a Model, Department and a ModelDepartmentMapper class file. From there you can set all the relevant columns and how they are linked to another table as well as column types IE VARCHAR etc
I'm trying to have multiple tables with the same schema, within the same database, using Entity Framework.
For example, if I have the classes below, and I login to the SQL Server database, I can only see a table that is named something like dbo.Schema.
Is there a way to have multiple tables with the same schema?
class Context1 : DbContext
{
public DbSet<Schema> table1 { get; set; }
}
class Context2 : DbContext
{
public DbSet<Schema> table2 { get; set; }
}
class Schema
{
[Key]
public int EntryId { get; set; }
}
Is there a way to have multiple tables with the same schema?
You can either use Data Annotations or Fluent API to configure the table and schema name.
Suppose you have the following model:
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Using Data Annotations, you could name it blogging.blogs:
[Table("blogs", Schema = "blogging")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Using Fluent API, you can override OnModelCreating method to name it blogging.blogs:
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.ToTable("blogs", schema: "blogging");
}
}
You can simple do like this with multiple tables.
public partial class AdventureWorksEntities : DbContext
{
public AdventureWorksEntities()
: base("name=AdventureWorksEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Address> Addresses { get; set; }
public virtual DbSet<AddressType> AddressTypes { get; set; }
public virtual DbSet<Contact> Contacts { get; set; }
public virtual DbSet<ContactType> ContactTypes { get; set; }
public virtual DbSet<CountryRegion> CountryRegions { get; set; }
public virtual DbSet<StateProvince> StateProvinces { get; set; }
}
in this code we can add more table from same database. There is no need to create another class and inherit DbContext.
or you can do Add Item into project-> New Item->Data->ADO.NET Entity Data Model.
This will generate same code with your selected tables.
Thanks
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);
}
}