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!
Related
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.
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 company can have many addresses, however each company has a main address.
I am looking to find the best way to create this kind of relation in EF Core.
Below is what I came up with. Is there a better way? Am I way off entirely?
Models
public class Company
{
public int Id { get; set; }
public int MainAddressId { get; set; }
public Address MainAddress { get; set; }
public ICollection<CompanyAddress> CompanyAddresses { get; set; }
// other company info
}
public class Address
{
public int Id { get; set; }
public int CompanyAddressId { get; set; }
public CompanyAddress CompanyAddress { get; set; }
// other address info
}
public class CompanyAddress
{
public int CompanyId { get; set; }
public Company Company { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
public bool IsMain { get; set; }
}
DataContext.cs
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<Company> Companies { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<CompanyAddress> CompanyAddresses { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<CompanyAddress>()
.HasKey(ca => new {ca.CompanyId, ca.AddressId});
builder.Entity<CompanyAddress>()
.HasOne(ca => ca.Company)
.WithMany(ca => ca.CompanyAddresses)
.HasForeignKey(ca => ca.CompanyId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<CompanyAddress>()
.HasOne(ca => ca.Address)
.WithOne(ca => ca.CompanyAddresses)
.HasForeignKey(ca => ca.AddressId)
.OnDelete(DeleteBehavior.Cascade);
}
}
In my opinion, dead on. There are always other ways. But this is straight-forward and easily understood. MainAddress and MainAddressId are redundant. You don't have lazy loading (virtual) so you can easily determine the main address by
dbContext.Companies.FirstOrDefault(p => p.Id = <myCompanyId>);
dbContext.CompanyAddresses.FirstOrDefault(p => p.CompanyId == <myCompanyId> && p.IsMain);
If you go with lazy loading later, just add .Include("Address") to the second query. And yes, you can combine the two.
Work on Asp.net mvc entity framework,face problem on child entities add/update/delete portion.
Here is my relationships
public class Client
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClientId { get; set; }
public string ClientName { get; set; }
public string CompanyAddress1 { get; set; }
public string CompanyAddress2 { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string ContactPerson { get; set; }
public string ContactPersonPhone { get; set; }
public virtual ICollection<Job> Jobs { get; set; }
}
public class Job
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int JobId { get; set; }
public string JobDescription { get; set; }
public decimal EstamationCost { get; set; }
public DateTime EstemateDelevaryTime { get; set; }
public virtual Client Client { get; set; }
}
public class Context : DbContext
{
public DbSet<Job > Jobs { get; set; }
public DbSet<Client> Clients { get; set; }
}
Please take a look at my job entity,it's not contain any parent id just contain the relation ship.
Want to know how to create any entry/update/delete on child entity.I used bellow syntax for create:
[HttpPost]
public async Task<ActionResult> Create(JobManageViewModel model)
{
if (ModelState.IsValid)
{
_unitOfWorkAsync.BeginTransaction();
try
{
var application = model.ToDalEntity();
application.ObjectState = ObjectState.Added;
_jobService.Insert(application);
var changes = await _unitOfWorkAsync.SaveChangesAsync();
_unitOfWorkAsync.Commit();
return RedirectToAction("Index");
}
catch
{
// Rollback transaction
_unitOfWorkAsync.Rollback();
}
}
LoadClientsInViewData();
return View(model);
}
public Job ToDalEntity(Job model)
{
model.JobId = this.JobId;
model.JobDescription = this.JobDescription;
model.EstamationCost = this.EstamationCost;
model.EstemateDelevaryTime = this.EstemateDelevaryTime;
return model;
}
Problem is can not insert client information on Job table.How to insert/update/delete client information on job table
This is a possible approach, but i strongly do not recommend you to do it, because it is really a bad practice, you are going to break SOLID principles, and so on.
But you could do this way:
Configure your relationship tables
I've created two separated classes, one for each table
Client Configuration
public class ClientEntityTypeConfiguration : EntityTypeConfiguration<Client>
{
public ClientEntityTypeConfiguration()
{
HasKey(x => x.ClientId);
Property(x => x.ClientId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.Jobs).WithOptional(x => x.Client);
}
}
Job Configuration
public class JobEntityTypeConfiguration : EntityTypeConfiguration<Job>
{
public JobEntityTypeConfiguration()
{
HasKey(x => x.JobId);
Property(x => x.JobId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasOptional(x => x.Client).WithMany(x => x.Jobs);
}
}
On your context class, you set those classes as your configuration:
public class MyContext : DbContext
{
public DbSet<Client> Clients { get; set; }
public DbSet<Job> Jobs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new ClientEntityTypeConfiguration());
modelBuilder.Configurations.Add(new JobEntityTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
The trick to add your already created client to your Job entity, is that you must set a new instance of Client, and set only the Id, and then tell to your Context that this instance is already exists on database.
var clientAlreadyExists = new Client {ClientId = 1};
context.Clients.Attach(clientAlreadyExists);
job.Client = clientAlreadyExists;
context.Jobs.Add(product);
I recommend you to use Repository Pattern, and do not access your context directly from your Controller, and one more time a say this to you, your approach is a Bad Practice.
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.