I am learning EF
I wanted to play with the different types of inheritance using code first.
I initially started off with a few classes and ran my application. I saw the database get created with all my classes represented as tables.
However, when I add new classes or fields and run the application again, I do not see the changes in my database schema.
I have used the "DropCreateDatabaseAlways", so I don't understand why my database is not being updated with the proper schema as I add fields and classes. Can someone explain what I am doing wrong?
Initial Code:
namespace Domain
{
public class Good
{
public int Id { get; set; }
public string Name { get; set; }
public double BaseValue { get; set; }
}
public class Manufacturer {
public int Id {get; set;}
public ICollection<ManufacturingCapability> ManufacturingCapabilities { get; set; }
}
public class ManufacturingCapability {
public int Id { get; set; }
public ICollection<ManufacturingInput> Inputs { get; set; }
public ICollection<ManufacturingOutput> Outputs { get; set; }
public TimeSpan CycleTime { get; set; }
}
public class ManufacturingInput {
public int Id { get; set; }
public Good Good { get; set; }
public uint Quantity { get; set; }
}
public class ManufacturingOutput {
public int Id { get; set; }
public Good Good { get; set; }
public uint Quantity { get; set; }
}
public class ManufacturingDbContext :DbContext {
public DbSet<Good> Goods { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public ManufacturingDbContext() : base("name=EFLearnConnectionString") {
Database.SetInitializer<ManufacturingDbContext>(new DropCreateDatabaseAlways<ManufacturingDbContext>());
}
}
}
namespace EFLearning {
class Program {
static void Main(string[] args) {
using (var manufacturingDbContext = new ManufacturingDbContext()) {
var good = new Good() { Name = "Water" };
manufacturingDbContext.Goods.Add(good);
manufacturingDbContext.SaveChanges();
}
}
}
}
Database Tables in Management Studio after running:
Added code:
public class Station : Manufacturer {
public string Name { get; set; }
public Vector3 Location { get; set; }
}
and I added this to context:
public DbSet<Station> Stations { get; set; }
Database Tables in Management Studio after running:
Your strategy for domain design to station and manufactor entity is Table per Hierarchy.so stations properties are now in manufactors table.
go to this link for more information.
if you you want Station entity have a seperate table must use of Table per Type
go to this link for Table per Type
Related
I am working on a Restaurant Application. I have a restaurant model and a table model.
namespace Restaurant.Models
{
[Table("Restaurant")]
public class RestaurantModel
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("telephone_number")]
public int TelephoneNumber { get; set; }
[NotMapped]
public List<TableModel> Tables;
public RestaurantModel()
{
Tables = new List<TableModel>();
}
}
}
namespace Restaurant.Models
{
[Table("Table")]
public class TableModel
{
[Key]
[Column("id")]
public int Id { get; set; }
[ForeignKey("restaurant_id")]
[Required] [NotNull]
public int RestaurantId { get; set; }
[Column("available_seats")]
public int AvailableSeats { get; set; }
[Column("is_indoors")]
public bool IsIndoors { get; set; }
}
}
I have a dependency between Restaurant and Table:
Here are the columns and keys that Entity Framework has created for me via my context:
Lastly, here's my Context class:
namespace Restaurant.Data
{
public class RestaurantContext : DbContext
{
public RestaurantContext(DbContextOptions<RestaurantContext> options) : base(options)
{
}
public DbSet<RestaurantModel> Restaurants { get; set; }
public DbSet<TableModel> Tables { get; set; }
public DbSet<GuestModel> Guests { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RestaurantModel>().ToTable("Restaurant");
modelBuilder.Entity<TableModel>().ToTable("Table");
modelBuilder.Entity<GuestModel>().ToTable("Guest");
modelBuilder.Entity<TableModel>()
.HasOne<RestaurantModel>();
}
}
}
When I retrieve a restaurant, I want the corresponding tables to be retrieved inside of the TableModel List. Currently, when I retrieve a Restaurant, it will not retrieve any corresponding Tables. This makes sense to me, as I have not properly connected the relationship for EntityFramework to recognize it. I have tried to look online how to do it, consulting guides on setting up Foreign Key relationships and such. I cannot find the information I am looking for, due to a lack of basic knowledge. The answers I can find do not make sense to me because I do not understand what they are doing or how they are doing it.
Could anyone point me in the right direction or tell me what I am doing wrong?
add relations to your classes
[Table("Restaurant")]
public class Restaurant
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("telephone_number")]
public int TelephoneNumber { get; set; }
public virtual ICollection<Table> Tables { get; set; }
}
}
[Table("Table")]
public class Table
{
[Key]
[Column("id")]
public int Id { get; set; }
public int? RestaurantId { get; set; }
public virtual Restourant Restaurant { get; set; }
[Column("available_seats")]
public int AvailableSeats { get; set; }
[Column("is_indoors")]
public bool IsIndoors { get; set; }
}
}
and since you are using Net core 5+ I don' t think that you any navigation attributes or fluent APIs
Delete old migration folde and make a clean migration to db
after this you can try this code for test
var restourant= context.Restourants.Include(r=> r.Tables).FirstOrDefault(r=>r.Id==id);
it should return a restourant with a list of tables
I recently needed to change the type of three properties in a model class from string to an ICollection custom type as shown below.
There are also three classes (SpecialType, TypeToAdd, TypeToRemove) that were added that have a primary key ID, name (string), qty (int) and a FK to corresponding Subscriptions_Regular_Id.
I ran the migration, then update-database to script, but when I ran the script in SSMS console it left off the three ICollection properties below. It did create the three dependent tables with their foreign keys back to the parent, but I can't understand why it's not creating these three ICollection properties. Something simple I'm overlooking I'm sure and wanted to get some input if anyone might have a suggestion.
public class Subscriptions
{
[Key]
public int Subscriptions_Regular_Id { get; set; }
public string CustomerNumber { get; set; }
public string Type { get; set; }
public int TypeQty { get; set; }
public ICollection<SpecialType> SpecialType { get; set; }
public ICollection<TypeToAdd> TypeToAdd { get; set; }
public ICollection<TypeToRemove> TypeToRemove { get; set; }
}
For context:
Subscriptions can have many SpecialTypes, TypeToAdd, and TypeToRemove
SpecialTypes, TypeToAdd, TypeToRemove can be tied to only one Subscription.
Thanks in advance for any input.
====== EDIT ======
Adding 3 ICollection classes:
public class TypeToAdd
{
[Key]
public int TypeToAddId { get; set; }
public string TypeToAdd { get; set; }
public int Qty { get; set; }
public int Subscriptions_Regular_Id { get; set; }
public Subscriptions Subscriptions { get; set; }
}
The other two classes are the same as above other than the first two property names (they are TypeToRemove and SpecialType).
but I can't understand why it's not creating these three ICollection properties
Collection Navigation Properties are implemented using seperate tables with foreign keys. Relational databases don't have multi-valued attributes, so that's just how related collections are implemented in an RDBMS.
Try to change the classes to this
public class Subscription
{
[Key]
public int Id { get; set; }
public string CustomerNumber { get; set; }
public string Type { get; set; }
public int TypeQty { get; set; }
[InverseProperty(nameof(TypeToAdd.Subscription))]
public ICollection<TypeToAdd> TypeToAdds { get; set; }
[InverseProperty(nameof(SpecialType.Subscription))]
public virtual ICollection<SpecialType> SpecialTypes { get; set; }
[InverseProperty(nameof(TypeToRemove.Subscription))]
public ICollection<TypeToRemove> TypeToRemoves { get; set; }
}
public class TypeToAdd
{
[Key]
public int Id { get; set; }
public string TypeToAdd { get; set; }
public int Qty { get; set; }
public int SubscriptionId { get; set; }
[ForeignKey(nameof(SubscriptionId))]
[InverseProperty("TypeToAdds")]
public virtual Subscription Subscription { get; set; }
}
SpecialType and TypeToRemove classes should be configured the same way as TypeToAdd.
I'm working on a trucking API using Entity Framework (EF) Core. Basic CRUD operations are working fine using the repository pattern. There is an error in
configurations I am implementing, however.
I want to obtain multiple trailers and trucks associated with single load, reflecting the one-to-many relationship.
public class LoadConfiguration : IEntityTypeConfiguration<Load>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<Load> builder)
{
builder.Property(p=>p.Id).IsRequired();
builder.HasOne(t=>t.Customer).WithMany().HasForeignKey(p=>p.CustomerId);
builder.Property(p=>p.LoadedFrom).IsRequired();
builder.HasMany(p=>p.Trailer).WithOne().HasForeignKey(t=>t.TrailerId);
builder.HasMany(p=>p.Truck).WithOne().HasForeignKey(t=>t.TruckId);
builder.Property(p=>p.Destination).IsRequired();
}
}
public class Truck:BaseEntity
{
public int PlateNo { get; set; }
public string ModelName { get; set; }
public Location StateCode { get; set; }
public int PollutionCertificateValidity { get; set; }
public int DateOfPurchase { get; set; }
public int FitnessCertificateValidity { get; set; }
}
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public Trailer Trailer { get; set; }
public int TrailerId { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
public string Destination { get; set; }
}
public class Trailer:BaseEntity
{
public int TrailerCapacity { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
A one-to-many relationship is defined by using navigation collections, that has the capacity to hold many Trucks and Trailers. You can choose the collection type freely, but I would suggest ICollection generic type.
Modify your Load class as follows:
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public string Destination { get; set; }
// navigation collections
public ICollection<Trailer> Trailers { get; set; }
public ICollection<Truck> Trucks { get; set; }
}
You will then be able to set up the relationship in your LoadConfiguration class by using
the pluralized name:
builder.HasMany(p=>p.Trailers).WithOne();
builder.HasMany(p=>p.Trucks).WithOne();
.. even though EF Core will be smart enough to figure out the relation by convention so the fluent configuration is redundant.
I have two EF models -
public class What
{
[Key]
public int primary_key_What { get; set; }
public int another_column_What { get; set; }
public virtual ICollection<Why> Whys { get; set; }
}
And
public class Why
{
[Key]
public int primary_key_Why { get; set; }
public int some_column_Why { get; set; }
public virtual What What { get; set; }
}
The problem is, I have to use another_column_What and some_column_Why for navigating between the two. As you can see none of them are keys or declared unique in the database, also their names are different.
I've tried all the ways I could imagine of and found on search, but none of them works. How and in which model mapping do I use to say, navigate between What and Why using another_column_What and some_column_Why columns.
So whenever a query is generated by EF, it will compare another_column_What with some_column_Why?
Very unfortunately, changing the database architecture (or even column names) is not an option here.
Can anyone please help me with it?
This should work:
Db schema:
What model:
[Table("what")]
public class what
{
[Key]
[Column("primary_key_what")]
public int primary_key_what { get; set; }
[Column("another_column_what")]
public int another_column_what { get; set; }
public virtual ICollection<why> whys { get; set; }
}
Why model:
[Table("why")]
public class why
{
[Key]
[Column("primary_key_why")]
public int primary_key_why { get; set; }
[ForeignKey("what")]
[Column("some_column_why")]
public int some_column_why { get; set; }
public virtual what what { get; set; }
}
Context:
public class Context : DbContext
{
public virtual DbSet<what> what { get; set; }
public virtual DbSet<why> why { get; set; }
public Context() : base("name=SqlConnection")
{
}
}
Main:
static void Main(string[] args)
{
using (var context = new Context())
{
var results = from w in context.what
select w;
foreach (var what in results)
{
Console.WriteLine("what.primary_key_what = {0}", what.primary_key_what);
Console.WriteLine("what.another_column_what = {0}", what.another_column_what);
Console.WriteLine("what has {0} whys", what.whys.Count);
foreach (var why in what.whys)
{
Console.WriteLine("Why.primary_key_why = {0}", why.primary_key_why);
Console.WriteLine("Why.some_column_why = {0}", why.some_column_why);
}
}
}
}
What data:
Why data:
Output:
You can see my previous question which related with many to many relation but with auto generated mapping table.
I have 2 model, HrTraining and HrPerson. Any people can be assigned to one or more Trainings. You can see my model as below
public class HrTraining
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
public class HrMapTrainingPerson
{
public int Id { get; set; }
public string Status { get; set; }
public int HrTrainingId { get; set; }
public int HrPersonId { get; set; }
public virtual HrTraining HrTraining { get; set; }
public virtual HrPerson HrPerson { get; set; }
}
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
How can i take all training objects which assingned to a person with efficient way.
So you want to find a person, and get all the trainings assigned to it? There are lot of ways.. but using your models, this could be something like this
var trPersons = dbContext.HrPerson.Find(idPerson).HrMapTrainingPerson.ToList();
foreach(var trPerson in trPersons) {
var training = trPerson.HrTraining;
//do what you want, here you can get trPerson.HrTraining.Name for instance
}