Using Include() method EF Code First Approach - entity-framework

I'm new to EF code first approach,
The assignment I'm trying to cover here is to include master and child records,
My problem statement is unable to include child inside the master model, rather master can be included in the child records.
ie..,
Below expression is possible, but the master is repated inside the child records
var orderItems = _context.OrderItems.Include(o => o.Order).ToList();
where as the below code throws exception,
var orders = _context.Orders.Include(o => o.OrderItems).ToList();
Exception :
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.SqlServer.dll but was not handled in user code
Additional information: A specified Include path is not valid. The
EntityType 'Dsms.Data.EF.OrderModel' does not declare a navigation
property with the name 'OrderItems'.
Please advise
//Model classes
public abstract class BaseModel : IDisposable
{
public BaseModel()
{
DrivingSchoolId = Guid.NewGuid();
UserId = Guid.NewGuid();
IsActive = true;
CreatedOn = DateTime.Now;
ModifiedOn = DateTime.Now;
}
[Display(Name = "Driving School")]
public Guid DrivingSchoolId { get; set; }
[Display(Name = "User")]
public Guid UserId { get; set; }
[Display(Name = "Active")]
public bool IsActive { get; set; }
[Display(Name = "Created By")]
public Guid CreatedBy { get; set; }
[Display(Name = "Created On")]
public DateTime CreatedOn { get; set; }
[Display(Name = "Modified By")]
public Guid ModifiedBy { get; set; }
[Display(Name = "Modified On")]
public DateTime ModifiedOn { get; set; }
public void Dispose()
{
}
}
public class OrderModel : BaseModel
{
public OrderModel()
{
OrderId = Guid.NewGuid();
}
[Key]
[Column(Order = 1)]
public Guid OrderId { get; set; }
[Display(Name = "Customer")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Customer'")]
public Guid CustomerId { get; set; }
[Display(Name = "Bill Number")]
[DataType(DataType.Text)]
//[Required(ErrorMessage = "Please fill the 'Bill Number'")]
[MaxLength(25, ErrorMessage = "Maximum character lenght of 'Bill Number' is 25.")]
public string BillNumber { get; set; }
[Display(Name = "Age Proof")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Age Proof'")]
public Guid AgeProofGeneralItemId { get; set; }
[Display(Name = "Address Proof")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Address Proof'")]
public Guid AddressProofGeneralItemId { get; set; }
[Display(Name = "Status")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Status'")]
public Guid StatusGeneralItemId { get; set; }
[Display(Name = "Training Instructor")]
[DataType(DataType.Text)]
public Guid TrainingInstructorId { get; set; }
[Display(Name = "Training Vehicle")]
[DataType(DataType.Text)]
public Guid TrainingVehiclesGeneralItemId { get; set; }
[Display(Name = "Notes")]
[DataType(DataType.MultilineText)]
[MaxLength(500, ErrorMessage = "Maximum character lenght of 'Notes' is 500.")]
public string Notes { get; set; }
[Display(Name = "Total")]
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please fill the 'Total'")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Total Amount'")]
public decimal Total { get; set; }
[Display(Name = "Discount")]
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
[DataType(DataType.Text)]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Discount Amount'")]
public decimal Discount { get; set; }
//EF Navigation properties starts
public virtual CustomerModel Customer { get; set; }
public virtual DrivingSchoolModel DrivingSchool { get; set; }
public virtual GeneralItemModel AgeProofGeneralItem { get; set; }
public virtual ICollection<GeneralItemModel> AgeProofGeneralItems { get; set; }
public virtual GeneralItemModel AddressProofGeneralItem { get; set; }
public virtual ICollection<GeneralItemModel> AddressProofGeneralItems { get; set; }
public virtual GeneralItemModel StatusGeneralItem { get; set; }
public virtual ICollection<GeneralItemModel> StatusGeneralItems { get; set; }
public virtual InstructorModel TrainingInstructor { get; set; }
public virtual ICollection<InstructorModel> TrainingInstructors { get; set; }
public virtual GeneralItemModel TrainingVehicleGeneralItem { get; set; }
public virtual ICollection<GeneralItemModel> TrainingVehicleGeneralItems { get; set; }
public virtual ICollection<OrderItemModel> OrderItems { get; set; }
public virtual ICollection<OrderTransactionModel> OrderTransactions { get; set; }
}
public class OrderItemModel : BaseModel
{
public OrderItemModel()
{
OrderItemId = Guid.NewGuid();
NoOfTrainingDays = 1;
}
[Key]
[Column(Order = 1)]
public Guid OrderItemId { get; set; }
[Display(Name = "Order")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Order'")]
public Guid OrderId { get; set; }
[Display(Name = "Service")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Service'")]
public Guid ServiceId { get; set; }
[Display(Name = "Training Days")]
[DataType(DataType.Text)]
public int NoOfTrainingDays { get; set; }
[Display(Name = "Service Cost")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please fill the 'Service Cost'")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Service Cost'")]
public decimal ServiceCost { get; set; }
[Display(Name = "Total Service Cost")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please fill the 'Total Service Cost'")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Service Cost'")]
public decimal TotalServiceCost { get; set; }
//EF Navigation properties starts
public virtual OrderModel Order { get; set; }
public virtual ServiceModel Service { get; set; }
public virtual ICollection<OrderFormModel> OrderForms { get; set; }
}
public class OrderTransactionModel : BaseModel
{
public OrderTransactionModel()
{
OrderTransactionId = Guid.NewGuid();
}
[Key]
[Column(Order = 1)]
public Guid OrderTransactionId { get; set; }
[Display(Name = "Order")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Order'")]
public Guid OrderId { get; set; }
[Display(Name = "Payment Type")]
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please select the 'Payment Type'")]
public Guid PaymentTypeGeneralItemId { get; set; }
[Display(Name = "Credited Amount")]
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please fill the 'Credited Amount'")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Credited Amount'")]
public decimal Credited { get; set; }
[Display(Name = "Debited Amount")]
[DataType(DataType.Text)]
[Required(ErrorMessage = "Please fill the 'Debited Amount'")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Please enter valid 'Debited Amount'")]
public decimal Debited { get; set; }
[Display(Name = "Notes")]
[DataType(DataType.MultilineText)]
[MaxLength(500, ErrorMessage = "Maximum character lenght of 'Notes' is 500.")]
public string Notes { get; set; }
//EF Navigation properties starts
public virtual ICollection<OrderModel> Orders { get; set; }
public virtual GeneralItemModel PaymentTypeGeneralItem { get; set; }
public virtual ICollection<GeneralItemModel> PaymentTypeGeneralItems { get; set; }
}
//EF's DB Config's
public class OrderConfiguration : EntityTypeConfiguration<OrderModel>
{
public OrderConfiguration()
{
this.ToTable("Orders", Constants.Database.DEFAULT_DB_SCHEMA);
this.HasKey(o => o.OrderId);
this.HasRequired(o => o.Customer).WithMany().HasForeignKey(o => o.CustomerId).WillCascadeOnDelete(false);
this.HasRequired(o => o.DrivingSchool).WithMany().HasForeignKey(o => o.DrivingSchoolId).WillCascadeOnDelete(false);
this.HasRequired(o => o.AgeProofGeneralItem).WithMany().HasForeignKey(o => o.AgeProofGeneralItemId).WillCascadeOnDelete(false);
this.HasRequired(o => o.AddressProofGeneralItem).WithMany().HasForeignKey(o => o.AddressProofGeneralItemId).WillCascadeOnDelete(false);
this.HasRequired(o => o.StatusGeneralItem).WithMany().HasForeignKey(o => o.StatusGeneralItemId).WillCascadeOnDelete(false);
this.HasRequired(o => o.TrainingInstructor).WithMany().HasForeignKey(o => o.TrainingInstructorId).WillCascadeOnDelete(false);
this.HasRequired(o => o.TrainingVehicleGeneralItem).WithMany().HasForeignKey(o => o.TrainingVehiclesGeneralItemId).WillCascadeOnDelete(false);
this.Property(o => o.CustomerId).IsRequired().HasColumnOrder(2);
this.Property(o => o.DrivingSchoolId).IsRequired().HasColumnOrder(3);
this.Property(o => o.BillNumber).HasMaxLength(25).IsRequired().HasColumnOrder(4);
this.Property(o => o.AgeProofGeneralItemId).IsRequired().HasColumnOrder(5);
this.Property(o => o.AddressProofGeneralItemId).IsRequired().HasColumnOrder(6);
this.Property(o => o.StatusGeneralItemId).IsRequired().HasColumnOrder(7);
this.Property(o => o.TrainingInstructorId).IsOptional().HasColumnOrder(8);
this.Property(o => o.TrainingVehiclesGeneralItemId).IsOptional().HasColumnOrder(9);
this.Property(o => o.Total).IsRequired().HasColumnOrder(10);
this.Property(o => o.Discount).IsOptional().HasColumnOrder(11);
this.Property(o => o.Notes).HasMaxLength(500).IsOptional().HasColumnOrder(12);
this.Property(o => o.IsActive).IsRequired().HasColumnOrder(13);
this.Property(o => o.CreatedBy).IsRequired().HasColumnOrder(14);
this.Property(o => o.CreatedOn).IsRequired().HasColumnOrder(15);
this.Property(o => o.ModifiedBy).IsOptional().HasColumnOrder(16);
this.Property(o => o.ModifiedOn).IsOptional().HasColumnOrder(17);
this.Ignore(o => o.UserId);
this.Ignore(o => o.AgeProofGeneralItems);
this.Ignore(o => o.AddressProofGeneralItems);
this.Ignore(o => o.StatusGeneralItems);
this.Ignore(o => o.TrainingInstructors);
this.Ignore(o => o.TrainingVehicleGeneralItems);
this.Ignore(o => o.OrderItems);
this.Ignore(o => o.OrderTransactions);
}
}
public class OrderItemConfiguration : EntityTypeConfiguration<OrderItemModel>
{
public OrderItemConfiguration()
{
this.ToTable("OrderItems", Constants.Database.DEFAULT_DB_SCHEMA);
this.HasKey(oi => oi.OrderItemId);
this.HasRequired(oi => oi.Order).WithMany().HasForeignKey(oi => oi.OrderId).WillCascadeOnDelete(false);
this.HasRequired(oi => oi.Service).WithMany().HasForeignKey(oi => oi.ServiceId).WillCascadeOnDelete(false);
this.Property(oi => oi.OrderId).IsRequired().HasColumnOrder(2);
this.Property(oi => oi.ServiceId).IsRequired().HasColumnOrder(3);
this.Property(oi => oi.NoOfTrainingDays).IsOptional().HasColumnOrder(4);
this.Property(oi => oi.ServiceCost).IsRequired().HasColumnOrder(5);
this.Property(oi => oi.TotalServiceCost).IsRequired().HasColumnOrder(6);
this.Property(oi => oi.IsActive).IsRequired().HasColumnOrder(7);
this.Property(oi => oi.CreatedBy).IsRequired().HasColumnOrder(8);
this.Property(oi => oi.CreatedOn).IsRequired().HasColumnOrder(9);
this.Property(oi => oi.ModifiedBy).IsOptional().HasColumnOrder(10);
this.Property(oi => oi.ModifiedOn).IsOptional().HasColumnOrder(11);
this.Ignore(oi => oi.DrivingSchoolId);
this.Ignore(oi => oi.UserId);
this.Ignore(oi => oi.OrderForms);
}
}
public class OrderTransactionConfiguration : EntityTypeConfiguration<OrderTransactionModel>
{
public OrderTransactionConfiguration()
{
this.ToTable("OrderTransactions",Constants.Database.DEFAULT_DB_SCHEMA);
this.HasKey(ot => ot.OrderTransactionId);
this.HasRequired(ot => ot.Orders).WithMany().HasForeignKey(ot => ot.OrderId).WillCascadeOnDelete(false);
this.HasRequired(ot => ot.PaymentTypeGeneralItem).WithMany().HasForeignKey(ot => ot.PaymentTypeGeneralItemId).WillCascadeOnDelete(false);
this.Property(ot => ot.OrderId).IsRequired().HasColumnOrder(2);
this.Property(ot => ot.PaymentTypeGeneralItemId).IsRequired().HasColumnOrder(3);
this.Property(ot => ot.Credited).IsOptional().HasColumnOrder(4);
this.Property(ot => ot.Debited).IsOptional().HasColumnOrder(5);
this.Property(ot => ot.Notes).HasMaxLength(500).IsOptional().HasColumnOrder(6);
this.Property(ot=> ot.IsActive).IsRequired().HasColumnOrder(7);
this.Property(ot=> ot.CreatedBy).IsRequired().HasColumnOrder(8);
this.Property(ot=> ot.CreatedOn).IsRequired().HasColumnOrder(9);
this.Property(ot=> ot.ModifiedBy).IsOptional().HasColumnOrder(10);
this.Property(ot=> ot.ModifiedOn).IsOptional().HasColumnOrder(11);
this.Ignore(ot => ot.DrivingSchoolId);
this.Ignore(ot => ot.UserId);
this.Ignore(ot => ot.PaymentTypeGeneralItems);
}
}

The problem you are experiencing is caused by the fluent configuration:
OrderConfigiration
this.Ignore(o => o.OrderItems);
With this line you are telling EF to ignore OrderItems property, or in other words, OrderItems is not a navigation property - exactly what the Include error message says.
OrderItemConfiguration
this.HasRequired(oi => oi.Order).WithMany().HasForeignKey(oi => oi.OrderId).WillCascadeOnDelete(false);
Here you tell EF that the association is unidirectional with only reference navigation property at the many side and no inverse collection navigation at the one side.
In general, always configure the relationship only in one place and make sure it reflects the exact presence/absence of the navigation / FK properties.
So to fix the issue, remove the first line (with Ignore) and modify the second as follows:
this.HasRequired(oi => oi.Order)
.WithMany(o => o.OrderItems) // <- here
.HasForeignKey(oi => oi.OrderId)
.WillCascadeOnDelete(false);

Related

problems with include in EF Core 6 against an existing database

I have this Model which is a mapping of existing tables in a SAP database (where there is not a single relationship defined between the tables) that I want to attack via EF Core and LINQ
I have coded the relationships many-to-many
modelBuilder.Entity<Actividad>().ToTable("OCLG");
modelBuilder.Entity<Proyecto>().ToTable("OPRJ");
modelBuilder.Entity<Empleado>().ToTable("OHEM");
modelBuilder.Entity<Usuario>().ToTable("OUSR");
modelBuilder.Entity<Fase>().ToTable("[#ITAPROYSEG]");
modelBuilder.Entity<Actividad>()
.HasKey(a => new { a.U_Proy, a.U_Fase, a.AttendUser});
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Proyecto)
.WithMany(p => p.Actividades)
.HasForeignKey(a => a.U_Proy);
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Fase)
.WithMany(f => f.Actividades)
.HasForeignKey(a => a.U_Fase);
modelBuilder.Entity<Actividad>()
.HasOne(a => a.Usuario)
.WithMany(u => u.Actividades)
.HasForeignKey(a => a.AttendUser);
}
And the relationship one-to-one
public class Empleado
{
public int EmpId { get; set; }
[Key]
public string U_CodEmpl { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string NombreCompleto=>$"{FirstName} {MiddleName} {LastName}";
public Usuario Usuario { get; set; }
}
public class Usuario
{
[Key]
[Column(TypeName ="smallint")]
public int Internal_K { get; set; }
[ForeignKey("Empleado")]
public string User_Code { get; set; }
public Empleado Empleado { get; set; }
public List<Actividad> Actividades { get; set; }
}
When I query this in database directly
I get 479 rows
Now I try it in my Solution
void GetActividadesByEmpleadoYAñoRaw(string codigoSAPEmpleado, int año)
{
var empleado=_context.Empleados.Include(e=>e.Usuario).FirstOrDefault(e=>e.U_CodEmpl==codigoSAPEmpleado);
When I debug here I get this
Include gives me the Usuarioassociated but If I try to receive the Actividades associated to the Usuario
void GetActividadesByEmpleadoYAñoRaw(string codigoSAPEmpleado, int año)
{
var empleado = _context.Empleados.Include(e => e.Usuario).ThenInclude(u => u.Actividades).FirstOrDefault(e => e.U_CodEmpl == codigoSAPEmpleado);
I get only 143 rows
If I try a FromRaw query
var empleado = _context.Empleados.Include(e => e.Usuario).ThenInclude(u => u.Actividades).FirstOrDefault(e => e.U_CodEmpl == codigoSAPEmpleado);
var actividades = _context.Actividades
.FromSqlRaw(#"select AttendUser, U_Proy, U_Fase, Recontact, Durtype, Duration
from oclg
where AttendUser=" + empleado.Usuario.Internal_K + " and year(recontact)=" + año +
"order by Recontact, U_Proy, U_Fase").ToList();
Any idea, please?
Thanks

Calling Include in Entity Framework Core 2.0 makes the entity null if the property you are trying to include is null

I am getting an EF Core Entity like this:
Audit audit = _auditRepo.Audits
.Include(a => a.Status)
.Include(a => a.AuditType)
.Include(a => a.Office)
.Include(a => a.LeadOffice)
.Include(a => a.External)
.Include(a => a.External).ThenInclude(e => e.AuditResult)
.Include(a => a.External).ThenInclude(e => e.PreparerType)
.Include(a => a.External).ThenInclude(e => e.Auditee)
.Where(a => a.AuditID == id)
.FirstOrDefault();
My audit comes back null because it has no office. If I have an office it the audit gets populated.
If I don't have an office but I comment out:
.Include(a => a.Office)
the audit also comes back populated.
Here is my Audit entity :
[Table("audit")]
public class Audit
{
private string _auditAcnCd;
private string _title;
private string _summary;
[Key]
[Column("audit_id")]
public int AuditID { get; set; }
[Required(ErrorMessage = "ACN Required")]
[Display(Name="ACN:")]
[Column("audit_acn_cd")]
public string AuditAcnCd
{
get
{
return _auditAcnCd;
}
set
{
_auditAcnCd = value?.Trim();
}
}
[Required(ErrorMessage = "Title Required")]
[Display(Name = "Title:")]
[Column("audit_report_title_tx")]
public string Title
{
get
{
return _title;
}
set
{
_title = value?.Trim();
}
}
[StringLength(maximumLength: 1000, ErrorMessage = "Max Length: 1000")]
[Display(Name = "Summary:")]
[Column("audit_summary_tx")]
public string Summary
{
get
{
return _summary;
}
set
{
_summary = value?.Trim();
}
}
[Required(ErrorMessage = "Issuer Required")]
[Display(Name="Issuer:")]
[Column("audit_issuer_tx")]
public string Issuer { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Priority must be a number.")]
[Display(Name = "Priority:")]
[Column("audit_priority_cd")]
public short? Priority { get; set; }
[Display(Name = "Lead Office:")]
[Column("audit_lead_office_id")]
public short? LeadOfficeID { get; set; }
#region Navigation Properties
[Required(ErrorMessage = "Audit Type Required.")]
[Display(Name = "Audit Type:")]
[Column("audit_audit_type_id")]
public short AuditTypeID { get; set; }
[Display(Name = "Audit Type:")]
public AuditType AuditType { get; set; }
[Column("audit_status_id")]
public int StatusID { get; set; }
public Status Status { get; set; }
[Required(ErrorMessage = "Office is Required.")]
[Display(Name = "Offices:")]
[Column("audit_office_id")]
public short? OfficeID { get; set; }
// [ForeignKey("OfficeID")]
public Office Office { get; set; }
[ForeignKey("AuditID")]
public External External { get; set; }
public IEnumerable<AuditLog> AuditLogs { get; set; }
public IEnumerable<Finding> Findings { get; set; }
public IEnumerable<Assignment> Assignments { get; set; }
[Column("audit_update_staff_id")]
public short UpdateStaffID { get; set; }
[Column("audit_fsa_office_id")]
[Display(Name = "FSA Audit Lead:")]
public int? FsaLeadOfficeId { get; set; }
[Display(Name = "FSA Audit Lead:")]
[ForeignKey("FsaLeadOfficeId")]
public FSAOffice FsaLeadOffice { get; set; }
[ForeignKey("LeadOfficeID")]
public Office LeadOffice { get; set; }
}
Here is my Office entity:
[Table("office")]
public class Office
{
private string _OfficeCd;
private string _OfficeNm;
private string _OfficeOrganizationCd;
[Key]
[Column("office_id")]
public short OfficeID { get; set; }
[Required(ErrorMessage = "Numeric Code is required")]
[StringLength(2, ErrorMessage = "Max Length is two")]
[Display(Name = "Office Numeric Code:")]
[Column("office_cd")]
public string OfficeCd
{
get
{
return _OfficeCd;
}
set
{
_OfficeCd = value?.Trim();
}
}
[Required(ErrorMessage = "Office Name is required")]
[Display(Name = "Office Name:")]
[Column("office_nm")]
public string OfficeNm
{
get
{
return _OfficeNm;
}
set
{
_OfficeNm = value?.Trim();
}
}
[Required(ErrorMessage = "Office Abbreviation is required")]
[Display(Name = "Office Abbreviation:")]
[Column("office_organization_cd")]
public string OfficeOrganizationCd
{
get
{
return _OfficeOrganizationCd;
}
set
{
_OfficeOrganizationCd = value?.Trim();
}
}
[Display(Name = "Status:")]
[Column("office_active_cd")]
public string OfficeActiveCd { get; set; }
[Display(Name = "Parent Office:")]
[Column("office_parent_id")]
public short? OfficeParentId { get; set; }
[Display(Name = "Parent Office:")]
[ForeignKey("OfficeParentId")]
public Office ParentOffice { get; set; }
public List<StaffOffice> StaffOffices { get; set; }
}
All my other properties work ok. Do I need a configure this in onModelCreating?
The explanation is simple - there is discrepancy between your entity model and the database.
When the FK is nullable like your OfficeId, the relationship is optional, EF Core expects that the column can be null and uses left outer join when retrieving the related data (requested via Include).
But at some point you seem to added [Required] attribute to the FK property:
[Required(ErrorMessage = "Office is Required.")] // <-- the problem
[Display(Name = "Offices:")]
[Column("audit_office_id")]
public short? OfficeID { get; set; }
// [ForeignKey("OfficeID")]
public Office Office { get; set; }
Note that the [Required] attribute and IsRequired() fluent API take precedence over the data type (of course you cannot make non nullable type nullable, but the other way around is possible). The effect is that now EF considers the OfficeID FK to be required (i.e. non nullable column in the database) and performs inner join, which of course filters the result in case the record contains null FK value.
The solution is to remove that attribute. And in general, always keep the model and the database in sync. Anytime you change something in the model, add new migration. In case it's empty, remove it, otherwise see what database changes EF assumes based on your model metadata/configuration.

Why am I getting Invalid column name on field names that don't exist in my model?

I am querying an entity with EF 5.0 Code First / DbContext. I am getting the following errors:
Invalid column name 'RetainedEarningsAccount_Key'.
Invalid column name 'PLSummaryAccount_Key'.
However, neither of these fields exist in the model. I assume some default configuration/mapping is happening here, but I don't know what it is. Here is the model class in question:
public GLEntity()
{
this.AccessEntities = new HashSet<AccessEntity>();
this.BankAccountGLAccounts = new HashSet<BankAccountGLAccount>();
this.BatchRestrictionEntities = new HashSet<BatchRestrictionEntity>();
this.BudgetVersions = new HashSet<BudgetVersion>();
this.CalendarCrossRefs = new HashSet<CalendarCrossRef>();
this.ConsolDetails = new HashSet<ConsolDetail>();
this.ConsolHeaders = new HashSet<ConsolHeader>();
this.DefaultBudgetVersions = new HashSet<DefaultBudgetVersion>();
this.Departments = new HashSet<Department>();
this.ExpenseCodeDetails = new HashSet<ExpenseCodeDetail>();
this.GLAccounts = new HashSet<GLAccount>();
this.Journals = new HashSet<Journal>();
this.PostingRules = new HashSet<PostingRule>();
this.Processes = new HashSet<Process>();
this.Properties = new HashSet<Property>();
this.RecurringJournals = new HashSet<RecurringJournal>();
this.RecurringTransactionGLs = new HashSet<RecurringTransactionGL>();
this.EntitiesAlternate = new HashSet<GLEntity>();
this.GLIntercompaniesDestinationEntities = new HashSet<GLIntercompany>();
this.GLIntercompaniesSourceEntities = new HashSet<GLIntercompany>();
this.TransGLs = new HashSet<TransGL>();
}
public System.Guid Key { get; set; }
public string EntityID { get; set; }
public string Description { get; set; }
public Nullable<short> CurrentFiscalYear { get; set; }
public Nullable<short> CurrentPrd { get; set; }
public string EntityType { get; set; }
public string AllowPostingYN { get; set; }
public string NextJournal { get; set; }
public Nullable<System.Guid> CompanyKey { get; set; }
public Nullable<System.Guid> RetainedEarningsAcctKey { get; set; }
public Nullable<System.Guid> PLSummaryAcctKey { get; set; }
public string AccountingType { get; set; }
public string UserCreated { get; set; }
public System.DateTime Created { get; set; }
public string UserEdited { get; set; }
public Nullable<System.DateTime> Edited { get; set; }
public Nullable<System.Guid> AlternateEntityKey { get; set; }
public string TrackJobs { get; set; }
public string TrackUnits { get; set; }
public virtual ICollection<AccessEntity> AccessEntities { get; set; }
public virtual ICollection<BankAccountGLAccount> BankAccountGLAccounts { get; set; }
public virtual ICollection<BatchRestrictionEntity> BatchRestrictionEntities { get; set; }
public virtual ICollection<BudgetVersion> BudgetVersions { get; set; }
public virtual ICollection<CalendarCrossRef> CalendarCrossRefs { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<ConsolDetail> ConsolDetails { get; set; }
public virtual ICollection<ConsolHeader> ConsolHeaders { get; set; }
public virtual ICollection<DefaultBudgetVersion> DefaultBudgetVersions { get; set; }
public virtual ICollection<Department> Departments { get; set; }
public virtual ICollection<ExpenseCodeDetail> ExpenseCodeDetails { get; set; }
public virtual ICollection<GLAccount> GLAccounts { get; set; }
public virtual ICollection<Journal> Journals { get; set; }
public virtual ICollection<PostingRule> PostingRules { get; set; }
public virtual ICollection<Process> Processes { get; set; }
public virtual ICollection<Property> Properties { get; set; }
public virtual ICollection<RecurringJournal> RecurringJournals { get; set; }
public virtual ICollection<RecurringTransactionGL> RecurringTransactionGLs { get; set; }
public virtual ICollection<GLEntity> EntitiesAlternate { get; set; }
public virtual GLEntity EntityAlternate { get; set; }
public virtual ICollection<GLIntercompany> GLIntercompaniesDestinationEntities { get; set; }
public virtual ICollection<GLIntercompany> GLIntercompaniesSourceEntities { get; set; }
public virtual ICollection<TransGL> TransGLs { get; set; }
public virtual GLAccount RetainedEarningsAccount { get; set; }
public virtual GLAccount PLSummaryAccount { get; set; }
}
And here is the mapping:
public GLEntity_Mapping()
{
this.HasKey(t => t.Key);
this.ToTable("tblEntity");
this.Property(t => t.Key).HasColumnName("KeyGUID");
this.Property(t => t.EntityID).HasColumnName("EntityID").IsUnicode(false).HasMaxLength(10);
this.Property(t => t.Description).HasColumnName("Description").IsUnicode(false).HasMaxLength(50);
this.Property(t => t.CurrentFiscalYear).HasColumnName("CurrentFiscalYear");
this.Property(t => t.CurrentPrd).HasColumnName("CurrentPrd");
this.Property(t => t.EntityType).HasColumnName("EntityType").IsUnicode(false).IsFixedLength().HasMaxLength(1);
this.Property(t => t.AllowPostingYN).HasColumnName("AllowPostingYN").IsUnicode(false).IsFixedLength().HasMaxLength(1);
this.Property(t => t.NextJournal).HasColumnName("NextJournal").IsUnicode(false).HasMaxLength(20);
this.Property(t => t.CompanyKey).HasColumnName("Company");
this.Property(t => t.RetainedEarningsAcctKey).HasColumnName("RetainedEarningsAcct");
this.Property(t => t.PLSummaryAcctKey).HasColumnName("PLSummaryAcct");
this.Property(t => t.AccountingType).HasColumnName("AccountingType").IsUnicode(false).IsFixedLength().HasMaxLength(1);
this.Property(t => t.UserCreated).HasColumnName("UserCreated").IsRequired().IsUnicode(false).HasMaxLength(50);
this.Property(t => t.Created).HasColumnName("Created");
this.Property(t => t.UserEdited).HasColumnName("UserEdited").IsUnicode(false).HasMaxLength(50);
this.Property(t => t.Edited).HasColumnName("Edited");
this.Property(t => t.AlternateEntityKey).HasColumnName("AlternateEntity");
this.Property(t => t.TrackJobs).HasColumnName("TrackJobs").IsUnicode(false).IsFixedLength().HasMaxLength(1);
this.Property(t => t.TrackUnits).HasColumnName("TrackUnits").IsUnicode(false).IsFixedLength().HasMaxLength(1);
this.HasOptional(t => t.Company).WithMany(t => t.Entities).HasForeignKey(d => d.CompanyKey);
this.HasOptional(t => t.EntityAlternate).WithMany(t => t.EntitiesAlternate).HasForeignKey(d => d.AlternateEntityKey);
}
Why am I getting an error on non-existent properties?
Because of the abbreviation Acct instead of Account in these properties...
public Nullable<System.Guid> RetainedEarningsAcctKey { get; set; }
public Nullable<System.Guid> PLSummaryAcctKey { get; set; }
...EF conventions do not recognize the properties as foreign keys for
public virtual GLAccount RetainedEarningsAccount { get; set; }
public virtual GLAccount PLSummaryAccount { get; set; }
You can either rename the FK properties to RetainedEarningsAccountKey and PLSummaryAccountKey. EF should recognize them correctly then according to the rule "FK property name = Navigation property name + primary key name of target entity".
Or define the properties as FKs with Fluent API in your GLEntity_Mapping:
this.HasOptional(t => t.RetainedEarningsAccount)
.WithMany(a => a.SomeInverseCollection1)
.HasForeignKey(t => t.RetainedEarningsAcctKey);
this.HasOptional(t => t.PLSummaryAccount)
.WithMany(a => a.SomeInverseCollection2)
.HasForeignKey(t => t.PLSummaryAcctKey);
SomeInverseCollection1/2 are the related collections in GLAccount or use WithMany() without lambda parameter if there are no inverse collections in that class.

EF Fluent API One-to-One relationship

I'm trying to configure an optional-required simple relationship, but EF doesn't seem to scaffold the right migration:
public class Applicant
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int ContactInfoId { get; set; }
public string PreferredCultureId { get; set; }
public virtual ApplicantContact Contact { get; set; }
public virtual Culture PreferredCulture { get; set; }
}
public ApplicantConfiguration()
{
HasKey(a => a.Id);
Property(a => a.FirstName).IsRequired().HasMaxLength(50);
Property(a => a.LastName).IsRequired().HasMaxLength(50);
HasOptional(a => a.Contact)
.WithRequired(c => c.Applicant)
.WillCascadeOnDelete();
HasOptional(a => a.PreferredCulture)
.WithMany()
.HasForeignKey(a => a.PreferredCultureId);
}
CreateTable(
"Main.Applicants",
c => new
{
Id = c.Int(nullable: false, identity: true),
FirstName = c.String(nullable: false, maxLength: 50),
LastName = c.String(nullable: false, maxLength: 50),
ContactInfoId = c.Int(nullable: false),
PreferredCultureId = c.String(maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("General._Cultures", t => t.PreferredCultureId)
.Index(t => t.PreferredCultureId);
Why is ContactInfoId is not being generated as a foreign key and nullable, as it is the optional side of the relationship ?
In your domain class try
public class Applicant
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ApplicantContact Contact { get; set; }
public virtual Culture PreferredCulture { get; set; }
}
public class ContactInfo
{
// whatever contact info fields you have
}
public class Culture
{
// culture fields
}
then in your context have
public DbSet<ContactInfo> ContactInfos { get; set; }
public DbSet<Applicant> Applicants { get; set; }
public DbSet<Culture> Cultures { get; set; }
The Id fields should get automatically if they are int.

How can I force my DB Initializer to create a table for an ENUM?

I have the following code for the Model, and also for the initializer.
However the status property is created as an INT and I would like it to be a foreign key to a STATUS Table.
Is this possible, or I need to remove the ENUM and create a class?
public class Applicant
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
public int ApplicantID { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(20, MinimumLength = 3, ErrorMessage="Name should not be longer than 20 characters.")]
[Display(Name = "First and LastName")]
public string name { get; set; }
[Required(ErrorMessage = "Telephone number is required")]
[StringLength(10, MinimumLength = 3, ErrorMessage = "Telephone should not be longer than 20 characters.")]
[Display(Name = "Telephone Number")]
public string telephone { get; set; }
[Required(ErrorMessage = "Skype username is required")]
[StringLength(10, MinimumLength = 3, ErrorMessage = "Skype user should not be longer than 20 characters.")]
[Display(Name = "Skype Username")]
public string skypeuser { get; set; }
public byte[] photo { get; set; }
public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
}
public class ApplicantPosition
{
[Key]
[Column("ApplicantID", Order = 0)]
public int ApplicantID { get; set; }
[Key]
[Column("PositionID", Order = 1)]
public int PositionID { get; set; }
public virtual Position Position { get; set; }
public virtual Applicant Applicant { get; set; }
[Required(ErrorMessage = "Applied date is required")]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Date applied")]
public DateTime appliedDate { get; set; }
public int StatusValue { get; set; }
public Status Status
{
get { return (Status)StatusValue; }
set { StatusValue = (int)value; }
}
}
public class ApplicationPositionHistory
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
public int ApplicationPositionHistoryID { get; set; }
public ApplicantPosition applicantPosition { get; set; }
public Status oldStatus { get; set; }
public Status newStatus { get; set; }
[StringLength(500, MinimumLength = 3, ErrorMessage = "Commebnts should not be longer than 500 characters.")]
[Display(Name = "Comments")]
public string comments { get; set; }
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[Display(Name = "Date")]
public DateTime dateModified { get; set; }
}
public enum Status
{
Applied,
AcceptedByHR,
AcceptedByTechnicalDepartment,
InterviewedByHR,
InterviewedByTechnicalDepartment,
InterviewedByGeneralManager,
AcceptedByGeneralManager,
NotAccepted
}
public class HRContext : DbContext
{
public DbSet<Position> Positions { get; set; }
public DbSet<Applicant> Applicants { get; set; }
public DbSet<ApplicantPosition> ApplicantsPositions { get; set; }
public DbSet<ApplicationPositionHistory> ApplicationsPositionHistory { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Position>().ToTable("Position");
modelBuilder.Entity<Applicant>().ToTable("Applicant");
modelBuilder.Entity<ApplicantPosition>().ToTable("ApplicantPosition");
modelBuilder.Entity<ApplicationPositionHistory>().ToTable("ApplicationsPositionHistory");
modelBuilder.Entity<Position>().Property(c => c.name).IsRequired();
modelBuilder.Entity<Applicant>().Property(c => c.name).IsRequired();
modelBuilder.Entity<ApplicantPosition>().Property(c => c.appliedDate).IsRequired();
base.OnModelCreating(modelBuilder);
}
}
If you want Status to be a table created automatically you must create it a class.
Other way is implementing custom database initializer and manually execute SQL to create table, fill it with data and create referential constraint from related tables.
Btw. Enum is not an entity and if you work with enum you should not model it as a table. Check constraint should be used in database to limit values for Status column (again you must create constraint manually in custom initializer).