EF model with list of objects - c#

I'll create app to saving gym results.
I have class Exercise:
public class Exercise
{
public Guid Id { get; protected set; }
public string Name { get; protected set; }
public Category Category { get; protected set; }
}
and class TrainingPlan which contain List of Exercise :
public class TrainingPlan
{
public Guid Id { get; protected set; }
public string Name { get; protected set; }
public IEnumerable<Exercise> Exercises { get; protected set; }
}
I create EntityFramework DbContext:
public class GymContext : DbContext
{
public GymContext(DbContextOptions<GymContext> options) : base(options) { }
public DbSet<Exercise> Exercises { get; set; }
public DbSet<TrainingPlan> TrainingPlans { get; set; }
}
And then I used command add-migration and update-database. For table with exercise, EF was added additional field with TrainingPlanId. So now I can assignee Exercise only to one TrainingPlan. But I want assigne Exercise for a few Plans, what is the best solution for this case?

Thx for help, I use many to many.
It's solve, I crate new class:
public class TrainingPlanExercise
{
public Guid TrainigPlanId { get; protected set; }
public TrainingPlan TrainigPlan { get; protected set; }
public Guid ExerciseId { get; protected set; }
public Exercise Exercise { get; protected set; }
}
And in TrainingPlan I replace List of Exercise to List of TrainingPlanExercise.
My EntityFramework DbContext now look that:
public class GymContext : DbContext
{
public GymContext(DbContextOptions<GymContext> options) : base(options) { }
public DbSet<Exercise> Exercises { get; set; }
public DbSet<TrainingPlan> TrainingPlans { get; set; }
public DbSet<TrainingPlanExercise> TrainingPlanExercises { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TrainingPlanExercise>()
.HasKey(e => new { e.ExerciseId, e.TrainigPlanId });
modelBuilder.Entity<TrainingPlanExercise>()
.HasOne(x => x.TrainigPlan)
.WithMany(x => x.Exercises)
.HasForeignKey(x => x.TrainigPlanId);
}
}
Unfortunately I'm stuck again. When I want get from DB single record TrainingPlan using this method:
public TrainingPlan Get(Guid id)
=> _context.TrainingPlans.Include(x => x.Exercises).Single(x => x.Id == id);
I receive record with null Exercise in List TrainingPlanExercise.
Should I add something more to DbContext or is there other solution?
BTW. I using EF core.

Related

Need many to many relationship for multiple properties on one entity EF Core 2.2

I have an entity for Users and an entity for Projects.
I need to be able to assign multiple users to 3 different list properties on my project entity. I have been able to do this successfully for one property (the many to many relationship) by a join entity. I could specify the UserType on the Users table and just use the one property, but I may run into scenarios where Users may perform more than one role (type) and then that wouldn't work.
I thought I could just put the UserType on the join table (entity) but I'm at a loss as to how to build that entity in my DBContext.
Here is what I have that's working with one property defined:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity:
public class User : IInt32Identity
{
public int Id { get; set; }
public string UserEmail { get; set; }
...
public List<ProjectUsers> ProjectUsers { get; set; }
public DateTime UserCreatedDate { get; set; }
public DateTime UserLastLoggedInDate { get; set; }
public DateTime UserModifiedDate { get; set; }
}
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
And my OnModelCreating() in my DBContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ProjectUsers>()
.HasKey(bc => new { bc.UserId, bc.ProjectId });
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.User)
.WithMany(b => b.ProjectUsers)
.HasForeignKey(bc => bc.UserId);
modelBuilder.Entity<ProjectUsers>()
.HasOne(bc => bc.Project)
.WithMany(c => c.ProjectUsers)
.HasForeignKey(bc => bc.ProjectId);
}
That all works fine as I said above, but here's what I would like:
ProjectEntity:
public class Project : IInt32Identity
{
public int Id { get; set; }
public string ProjectName { get; set; }
...
public bool ProjectActive { get; set; }
public List<ProjectFile> ProjectFiles { get; set; }
public List<ProjectUsers> ProjectClients { get; set; }
public List<ProjectUsers> ProjectBuilders { get; set; }
public List<ProjectUsers> ProjectDesigners { get; set; }
public DateTime ProjectCreatedDate { get; set; }
public DateTime ProjectModifiedDate { get; set; }
}
UserEntity is the same.
JoinEntity:
public class ProjectUsers
{
public int UserId { get; set; }
public User User { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
public string UserType { get; set; }
}
Where I'm lost is on the OnModelBinding() code and I'm also not sure if EF would be smart enough to populate the lists correctly based on that UserType meta property.
Any help or guidance would be greatly appreciated.
TIA
It might seem possible to treat ProjectUser as the base class/entity, and create different class/entity/type for ProjectClient, ProjectBuilder and ProjectDesigner that are inherited from ProjectUser. And then you create tables for each type and one-to-many relationship to the project. This is typically called Table Per Type (TPT) approach.
However, TPT is not yet implemented in EF Core.
You can still achieve it using Table Per Hierarchy (TPH), but you will have just one list in the project for all project users, where UserId, ProjectId and UserType become the complex key. Project clients, builders and designers will be calculated properties off that one project user list.
Entities
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectUser> ProjectUsers { get; set; }
public IEnumerable<ProjectUser> ProjectClients => this.ProjectUsers
.Where(x => x.UserType == "Client");
public IEnumerable<ProjectUser> ProjectBuilders => this.ProjectUsers
.Where(x => x.UserType == "Builder");
public IEnumerable<ProjectUser> ProjectDesigners => this.ProjectUsers
.Where(x => x.UserType == "Designer");
}
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<ProjectUser> UserProjects { get; set; }
}
public class ProjectUser
{
public int UserId { get; set; }
public virtual User User { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public string UserType { get; set; }
}
Configurations
public class ProjectConfiguration : IEntityTypeConfiguration<Project>
{
public void Configure(EntityTypeBuilder<Project> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Name).IsRequired();
builder.HasIndex(x => x.Name).IsUnique();
builder.Ignore(x => x.ProjectBuilders);
builder.Ignore(x => x.ProjectClients);
builder.Ignore(x => x.ProjectDesigners);
builder.ToTable("Project");
}
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Email).IsRequired();
builder.HasIndex(x => x.Email).IsUnique();
builder.ToTable("User");
}
}
public class ProjectUserConfiguration : IEntityTypeConfiguration<ProjectUser>
{
public void Configure(EntityTypeBuilder<ProjectUser> builder)
{
builder.HasKey(x => new { x.ProjectId, x.UserId, x.UserType });
builder.Property(x => x.UserType).IsRequired();
builder.HasOne(x => x.Project)
.WithMany(x => x.ProjectUsers)
.HasForeignKey(x => x.ProjectId);
builder.HasOne(x => x.User)
.WithMany(x => x.UserProjects)
.HasForeignKey(x => x.UserId);
}
}
The virtual keyword is there for lazy loading support. If you're not doing lazy loading, you don't have to have virtual there. Also you have to [NotMapped] those 3 calculated properties, which is the same as using .Ignore in fluent API's speaking.
DbContext
public class AppDbContext : DbContext
{
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new ProjectUserConfiguration());
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer("Data Source=.\\SQLEXPRESS;Initial Catalog=DL.SO.ProjectUsersDemo;Integrated Security=True;MultipleActiveResultSets=False;");
}
}
Nothing special here. After you add the migration and update the database, yours should look like
After seeding the database with sample data, although it's hard to show here, you can see those 3 lists are filled with correct data:

One-to-one relationship Entity Framework

I have two tables one with a list of clients and the other whether they are active or not. I want to link them Entity Framework, however, I am struggling. The two tables were already setup and have to primary keys or foreign keys.
namespace DataWarehouse.Models
{
public class DatabaseList
{
[Key]
public string STARDB { get; set; }
public int DBClientID { get; set; }
public string ClientName { get; set; }
public DatabaseStatus DatabaseStatus { get; set; }
public ICollection<PayComponents> PayComponents { get; set; }
= new List<PayComponents>();
}
public class DatabaseStatus
{
[Key]
public string STARDB { get; set; }
public string STATUS { get; set; }
public DatabaseList DatabaseList { get; set; }
}
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
}
public DbSet<DatabaseList> DatabaseList { get; set; }
public DbSet<DatabaseStatus> Status { get; set; }
public DbSet<PayComponents> PayComponents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DatabaseList>()
.HasOne(p => p.DatabaseStatus)
.WithOne(i => i.DatabaseList)
.HasForeignKey<DatabaseStatus>(k => k.STARDB);
}
}
}
I was hoping that Entity Framework would see the columns STARDB and notice that it is the same in both tables and match them that way. All I want to is to add the Status column from DatabaseStatus into the Databaselist table.
Thanks.
Managed to figure it out. My database was setup properly. However, I forgot the include statement in my Repository.cs class.
public IEnumerable<DatabaseList> GetAllClients()
{
_logger.LogInformation("Get all clients was called");
var clients = _ctx.DatabaseList
.Include(d => d.DatabaseStatus)
.OrderBy(p => p.ClientName)
.ToList();
return clients;
}
Still new to C# so a bit of learning curve!

EF 7: How to load related entities in a One-to-many relationship

I have the following code.
Why are my navigation properties (Requirement in Course, and Courses in Requirement) are null?
public class Course : AbsEntity {
[Key]
public string Title { get; set; }
public string Term { get; set; }
public int Year { get; set; }
public string CourseId { get; set; }
public double GradePercent { get; set; }
public string GradeLetter { get; set; }
public string Status { get; set; }
public int ReqId { get; set; }
public Requirement Requirement { get; set; }
}
public class Requirement : AbsEntity {
[Key]
public int ReqId { get; set; }
public string ReqName { get; set; }
public ICollection<Course> Courses { get; set; }
}
// In DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().HasOne(c => c.Requirement).WithMany(r => r.Courses).HasForeignKey(c => c.ReqId);
modelBuilder.Entity<Requirement>().HasMany(r => r.Courses).WithOne(c => c.Requirement);
}
First thing is you don't need to configure your relationship twice, you just need to do it one time:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().HasOne(c => c.Requirement)
.WithMany(r => r.Courses)
.HasForeignKey(c => c.ReqId);
}
Second thing is if you are doing a query and you are expecting to lazy load the related properties, I'm afraid is not going to be possible. EF 7 doesn't support lazy loading yet. As you can see there is a backlog item tracking Lazy Loading. So, if you need to load a related entity, you should use explicit loading using Include method:
var query= ctx.Courses.Include(c=>c.Requirement).Where(...)...;

Asp .NET MVC - Entity Framework many to many relationship, insert data

I have the model:
public class Movie
{
[Key]
public int MovieId { get; set; }
public string Title{ get; set; }
public ICollection<Actor> Actors { get; set; }
}
public class Actor
{
[Key]
public int AtorId { get; set; }
public string Nome { get; set; }
public ICollection<Movie> Movies { get; set; }
}
and the context:
public class MoviesContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
public DbSet<Actor> Actors { get; set; }
public MoviesContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MoviesContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Movie>().
HasMany(m => m.Actors).
WithMany(a => a.Movies).
Map(
m =>
{
m.MapLeftKey("MovieId");
m.MapRightKey("ActorId");
m.ToTable("ActorsMovies");
});
}
}
The table "ActorsMovies" is already created on sql server. how do I insert data into this table with entity framework? To insert data on the table Movies, for example, i've used the code db.Movies.Add(movie) and db.SaveChanges()
}
movie.Actors.Add(actor);
db.SaveChanges();
This should allow you to update the many to many table.

Changing column name convention in code-first EF4

I'm using EF4 with CodeFirst.
public class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<XXX>();
modelBuilder.Conventions.Add<XXX>());
}
}
What convention should I add or remove to make all DB identifiers (like column names, table names, stored procedures etc) to be in uppercase (or unquoted)?
Case-sentitive (quoted) identifiers in Oracle DB greatly suck in user-friendliness.
Consider the Product and Customer class
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public int Stock { get; set; }
}
public class Customer {
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDbContext : DbContext {
public DbSet<Product> Product { get; set; }
public DbSet<Customer> Customer { get; set; }
public MyDbContext() { }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingEntitySetNameConvention>();
modelBuilder.Conventions.Add<TableNameUppercaseConvention>();
base.OnModelCreating(modelBuilder);
}
}
public class TableNameUppercaseConvention : IConfigurationConvention<Type, EntityTypeConfiguration> {
public void Apply(Type typeInfo, Func<EntityTypeConfiguration> configuration) {
configuration().ToTable(typeInfo.Name.ToUpper());
}
}
EDIT:
I tried to do something like this, but it seems that doesn't work, idk why
public class ColumnUppercaseConvention : IConfigurationConvention<MemberInfo, PrimitivePropertyConfiguration> {
public void Apply(MemberInfo memberInfo, Func<PrimitivePropertyConfiguration> configuration) {
configuration().ColumnName = memberInfo.Name.ToUpper();
}
}
my best approach was
public class Customer {
[Column(Name = "ID")]
public int Id { get; set; }
[Column(Name = "NAME")]
public string Name { get; set; }
}
sorry, I can't help right now, but I'll keep trying.
I don't know how can you do this with conventions but take a look at StoreNameAttribute. You can apply on context, entities and properties.
Data Annotations in Entity Code First

Categories

Resources