I've got the following model and I want ShiftRequest and MissionRequest to have a single table in the DB.
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestDate { get; set; }
public int UserId { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
}
public class ShiftRequest : RequestBase
{
public virtual Column Column { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
I've tried to do it in the override void OnModelCreating(ModelBuilder modelBuilder) method but only one RequestBases table is created:
modelBuilder.Entity<ShiftRequest>().MapSingleType().ToTable("dbo.ShiftRequests");
modelBuilder.Entity<MissionRequest>().MapSingleType().ToTable("dbo.MissionRequest");
What am I doing wrong?
EDIT
Column and Mission are also entities in my model, is that acceptable?
Check the section about TPH in this article. If Mission and Column are complex types you will also find there how to map them. Generally you have to use MapHiearchy and Case methods instead of MapSingleType.
Edit:
Here is the example:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
namespace EFTest
{
public class RequestBase
{
public int Id { get; set; }
public DateTime? RequestedDate { get; set; }
public int UserId { get; set; }
}
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<MissionRequest> MissionRequests { get; set; }
}
public class Column
{
public string Name { get; set; }
}
public class MissionRequest : RequestBase
{
public virtual Mission Mission { get; set; }
}
public class ShiftRequest : RequestBase
{
public Column Column { get; set; }
}
public class TestContext : DbContext
{
public DbSet<RequestBase> Requests { get; set; }
public DbSet<Mission> Missions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ContainerName = "EFTest";
modelBuilder.IncludeMetadataInDatabase = false;
// Example of complex type mapping. First you have to define
// complex type. Than you can access type properties in
// MapHiearchy.
var columnType = modelBuilder.ComplexType<Column>();
columnType.Property(c => c.Name).HasMaxLength(50);
modelBuilder.Entity<Mission>()
.Property(m => m.Id)
.IsIdentity();
modelBuilder.Entity<Mission>()
.HasKey(m => m.Id)
.MapSingleType(m => new { m.Id, m.Name })
.ToTable("dbo.Missions");
modelBuilder.Entity<RequestBase>()
.Property(r => r.Id)
.IsIdentity();
// You map multiple entities to single table. You have to
// add some discriminator to differ entity type in the table.
modelBuilder.Entity<RequestBase>()
.HasKey(r => r.Id)
.MapHierarchy()
.Case<RequestBase>(r => new { r.Id, r.RequestedDate, r.UserId, Discriminator = 0 })
.Case<MissionRequest>(m => new { MissionId = m.Mission.Id, Discriminator = 1 })
.Case<ShiftRequest>(s => new { ColumnName = s.Column.Name, Discriminator = 2 })
.ToTable("dbo.Requests");
}
}
}
Edit 2:
I updated example. Now Mission is entity instead of complex type.
Related
I have two base classes where each base class has inherited subclasses:
[Table("BaseClassOne")]
public class BaseClassOne
{
public Guid Id { get; set; }
public BaseClassTwo BaseTwo { get; set; }
}
[Table("DerivedClassOne")]
public class DerivedClassOne : BaseClassOne
{
public Guid Id { get; set; }
public string Name { get; set; }
}
[Table("BaseClassTwo")]
public class BaseClassTwo
{
public Guid Id { get; set; }
public BaseClassOne BaseOne { get; set; }
}
[Table("DerivedClassTwo")]
public class DerivedClassTwo : BaseClassTwo
{
public Guid Id { get; set; }
}
Now I would like to configure the relationships via model-builder, but without needing a discriminator:
builder
.Entity<BaseClassOne>()
.HasKey(b1 => b1.Id);
builder
.Entity<DerivedClassOne>()
.HasBaseType<BaseClassOne>();
builder
.Entity<BaseClassTwo>()
.HasKey(b2 => b2.Id);
builder
.Entity<DerivedClassTwo>()
.HasBaseType<BaseClassTwo>();
//This is where the problem starts:
builder
.Entity<BaseClassTwo>()
.HasOne(b2 => b2.BaseClassOne)
.WithOne(b1 => b1.BaseClassTwo)
.HasForeignKey<BaseClassTwo>(e => e.Id)
.OnDelete(Rule.Cascade);
I'm trying to run a query like following I get an error that the column m.Discriminator can't be found:
_context.BaseClassesTwo
.Select(b2 => new
{
val1 = b2.name,
val2 = b2.BaseClassOne.name
})
.ToList();
Is this an conceptional problem or do I need to change the configuration? And when, how?
I have an entity as Plan with multiple sub-plans (children), each of which could be null.
For the PlanDto, I am trying to load up a list of all children rather than having a separate property for each child like the entity.
I have already achieved it manually through a foreach loop but now I am trying to do it via AutoMapper, which is failing for some reason.
Entities:
public class Plan
{
public virtual int Id { get; set; }
public DateTime Date { get; set; }
public virtual PlanDetail PlanChild1 { get; set; }
public virtual ObservationCare PlanChild2 { get; set; }
}
public class PlanDetail
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
public class ObservationCare
{
public virtual int Id { get; set; }
public virtual Plan Plan { get; set; }
public virtual string Description { get; set; }
}
DTOs:
public class PlanDto: EntityDto
{
public DateTime Date { get; set; }
public IEnumerable<ChildPlan> ChildPlan { get; set; }
}
public class ChildPlan : EntityDto
{
public ChildPlanType Type { get; set; }
}
public enum ChildPlanType
{
PlanDetail,
ObservationCare
}
AutoMapper config:
configuration.CreateMap<Plan, PlanDto>();
configuration.CreateMap<PlanDetail, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.PlanDetail));
configuration.CreateMap<ObservationCare, ChildPlan>()
.ForMember(dto => dto.Type, options => options.MapFrom(p => ChildPlanType.ObservationCare));
Mapping attempt:
var output = new List<PlanDto>();
var plans = await _planRepository.GetAll().ToList();
foreach (var plan in plans)
{
output.Add(ObjectMapper.Map<PlanDto>(plan));
}
I do not know why ChildPlan DTOs in the output list are always null!
You have to specify the mapping for PlanDto.ChildPlan:
configuration.CreateMap<Plan, PlanDto>()
.ForMember(dto => dto.ChildPlan,
options => options.MapFrom(
p => new object[] { p.PlanChild1, p.PlanChild2 }.Where(c => c != null)));
If you are using Entity Framework Core, you have to use eager-loading:
var plans = await _planRepository.GetAll()
.Include(p => p.PlanChild1)
.Include(p => p.PlanChild2)
.ToList();
There's also a simpler and more efficient way to map a list:
var output = ObjectMapper.Map<List<PlanDto>>(plans);
I have the following classes:
public class A
{
public Guid Id { get; set; } = Guid.NewGuid();
public ICollection<B> AllB { get; set; } = new List<B>();
public B Current { get; set; }
// other properties
}
public class B
{
public int Id { get; set; }
public Guid AId { get; set; }
public A A { get; set; }
// other properties
}
// in the DBContext
public DbSet<A> AllA { get; set; }
public IQueryable<A> AWithAllB
{
get
{
return AllA.Include(c => c.AllB);
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<A>().ToTable("A");
builder.Entity<A>().HasKey(h => h.Id);
builder.Entity<B>().ToTable("B");
builder.Entity<B>().HasKey(h => h.Id);
builder.Entity<B>().HasOne(h => h.A).WithMany().HasForeignKey(r => r.AId);
}
Basically, there are many B for one A.
What I struggle with is the property Current from A.
I want this property to be populated but with a custom logic to find which B to load.
In my case, it will be the instance of B with the highest Id that I want as Current. This logic will change in the future.
How can I achieve this?
I have a probably simple question, I am trying to create many to many relationships using entity framework and fluent api, and my problem is that when i try to do any query or view a object in debug it has always 0 items.
I am using junction table that looks like:
So relations exists, to be sure ive checked:
select candidate.firstname, skillset.name
from candidate
join candidate_skillset on candidate.id = candidate_skillset.candidate_id
join skillset on candidate_skillset.skillset_id = skillset.id
and joined results are displayed.
Now my context looks like:
public class CatalogContexct : DbContext
{
public DbSet<Candidate> Candidates { get; set; }
public DbSet<SkillSet> SkillSets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Candidate>().HasMany(t => t.SkillSets).WithMany(t => t.Candidates)
.Map(m =>
{
m.ToTable("candidate_skillset");
m.MapLeftKey("candidate_id");
m.MapRightKey("skillset_id");
});
modelBuilder.Entity<SkillSet>().ToTable("skillset");
modelBuilder.Entity<Candidate>().ToTable("candidate");
}
}
My left side model candidates:
[Table("candidate")]
public class Candidate
{
public Candidate()
{
this.SkillSets = new HashSet<SkillSet>();
}
[Key]
public int id { get; set; }
[Column("firstname")]
public string Firstname { get; set; }
public int? commendation_id { get; set; }
[ForeignKey("commendation_id")]
public Commendation commendation { get; set; }
public ICollection<SkillSet> SkillSets { get; set; }
}
And my rightside model skillset:
[Table("skillset")]
public class SkillSet : SimpleDictionary
{
public SkillSet()
{
this.Candidates = new HashSet<Candidate>();
}
public virtual ICollection<Candidate> Candidates { get; set; }
}
and that model has a parent class:
public class SimpleDictionary
{
[Key]
public int id { get; set; }
[Column("name")]
public string Name { get; set; }
}
So all should work but when I do for example:
var ca = this._catalog.Candidates
.Include("SkillSets").Include("commendation").
FirstOrDefault(x => x.SkillSets.Any());
Result is null, also when I view object on debug collection of property skillset allays has 0 elements, any idea what could be wrong with it?
I tried this with same structure mentioned here in you question and tried locally . And I am able to get the data with this code . Please try this and let me know if this helps . I just omitted commendation table for simplicity .
var context = new SampleDbContext();
var candidates = context.Candidates
.Include("SkillSets").ToList();
foreach (var candidate in candidates)
{
foreach (var sk in candidate.SkillSets.Where( s1 => s1.Candidates.Count(c=>c.id == candidate.id)>0 ))
{
Console.WriteLine( string.Format(#" Name : {0} Skill :{1}",candidate.Firstname ,sk.Name ) );
}
}
Below is my DbContext and Other Entity Classes
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
public class SampleDbContext : DbContext
{
public SampleDbContext()
: base("name=SampleDBConnection")
{
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Candidate> Candidates { get; set; }
public DbSet<SkillSet> SkillSets { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Candidate>().HasMany(t => t.SkillSets).WithMany(t => t.Candidates)
.Map(m =>
{
m.ToTable("candidate_skillset");
m.MapLeftKey("candidate_id");
m.MapRightKey("skillset_id");
});
modelBuilder.Entity<SkillSet>().ToTable("skillset");
modelBuilder.Entity<Candidate>().ToTable("candidate");
}
}
[Table("candidate")]
public class Candidate
{
public Candidate()
{
this.SkillSets = new HashSet<SkillSet>();
}
[Key]
public int id { get; set; }
[Column("firstname")]
public string Firstname { get; set; }
public int? commendation_id { get; set; }
//[ForeignKey("commendation_id")]
//public Commendation commendation { get; set; }
public ICollection<SkillSet> SkillSets { get; set; }
}
public class SimpleDictionary
{
[Key]
public int id { get; set; }
[Column("name")]
public string Name { get; set; }
}
[Table("skillset")]
public class SkillSet : SimpleDictionary
{
public SkillSet()
{
this.Candidates = new HashSet<Candidate>();
}
public virtual ICollection<Candidate> Candidates { get; set; }
}
}
The output of the query you mentioned and the result of my code both matched I hope this is that you wanted .
I have a problems with retreving data correctly to datagridview. First what i got:
Two classes:
namespace hms.core.Entities
{
public class Osoba : BaseEntity
{
public virtual int Id { get; set; }
public virtual string Imie { get; set; }
public virtual decimal SumaWydatkow { get; set; }
public virtual Dział Dział { get; set; }
}
}
namespace hms.core.Entities
{
public class Dział
{
public virtual int Id { get; set; }
public virtual string Nazwa { get; set; }
public virtual int SumaWydatkowDzialu { get; set; }
public virtual IList<Osoba> Osoby { get; set; }
}
}
And mappings:
namespace hms.data.Mappings
{
public class OsobaMap : ClassMap<Osoba>
{
public OsobaMap()
{
Table("Osoba");
Id(e => e.Id);
Map(e => e.Imie, "Imie").Length(50);
Map(e => e.SumaWydatkow, "SumaWydatkow");
References(e => e.Dział, "IdDzialu").Not.LazyLoad();
}
}
}
namespace hms.data.Mappings
{
public class DziałMapowanie : ClassMap<Dział>
{
public DziałMapowanie()
{
Table("Dział");
Id(e => e.Id);
Map(e => e.Nazwa, "Nazwa").Length(50);
Map(e => e.SumaWydatkowDzialu, "SumaWydatkowDzialu");
HasMany(c => c.Osoby).KeyColumn("IdDzialu").LazyLoad().Cascade.DeleteOrphan().Inverse();
}
}
}
I tried to load data from table Osoba to datagridview, and it's ok, all information are viewing into database but only column "Dział" is showing me in all rows values:
hms.Core.Entities.Dział. How to solve that problem?
I am not familiar with WinForms, but it look like DataGridView does not know how to show your Dział object in columns so it just shows it ToString() representation. I think there should be a way to specify in Dział grid column which property of Dzial to show.