Entity Framework Inheritance share database fields - entity-framework

So, I am using Entity Framework Code First. This is the model:
public class StockMove
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
public class StockMoveOut : StockMove
{
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public int OriginLocalId { get; set; }
public virtual Local OriginLocal { get; set; }
}
public class StockMoveIn : StockMove
{
public int SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
public int DestinationLocalId { get; set; }
public virtual Local DestinationLocal { get; set; }
}
public class StockMoveTransfer : StockMove
{
public int OriginLocalId { get; set; } //should be the same for StockMoveOut
public virtual Local OriginLocal { get; set; }
public int DestinationLocalId { get; set; } //should be the same for StockMoveIn
public virtual Local DestinationLocal { get; set; }
}
public class DataContext : DbContext
{
public DbSet<StockMove> StockMoves { get; set; }
}
I need to refer the fields (OriginLocalId, DestinationLocalId) in StockMoveTransfer to be the same for the StockMoveIn and StockMoveOut, but Entity Framework expect to new fields.
The SQL DDL generated is:
create table [dbo].[StockMoves] (
[Id] [int] not null identity,
[ProductId] [int] not null,
[CustomerId] [int] null,
[OriginLocalId] [int] null,
[SupplierId] [int] null,
[DestinationLocalId] [int] null,
[OriginLocalId1] [int] null, -- should not exists
[DestinationLocalId1] [int] null, -- should not exists
[Discriminator] [nvarchar](128) not null,
primary key ([Id])
);
Well, how can I configure the Entity Framework to point for the existing fields?

Related

Entity Framework - Parent Child relational table

I have an Organization entity table
public class Organization
{
public int OrganizationId { get; set; }
public string Name { get; set; }
public int OrganizationTypeId { get; set; }
public OrganizationType OrganizationType { get; set; }
public ICollection<OrganizationRelation> OrganizationRelations { get; set; }
}
then I have my relational table with a self-referencing parent column
public class OrganizationRelation
{
public int OrganizationRelationId { get; set; }
public int OrganizationId { get; set; }
public int? ParentOrganizationId { get; set; }
public Organization Organization { get; set; }
public Organization ParentOrganization { get; set; }
}
public class OrganizationRelationModelConfiguration : IEntityTypeConfiguration<OrganizationRelation>
{
public void Configure(EntityTypeBuilder<OrganizationRelation> builder)
{
builder.HasKey(c => c.OrganizationRelationId);
builder.Property(c => c.OrganizationRelationId).ValueGeneratedOnAdd();
builder.Property(c => c.OrganizationId).IsRequired();
builder.Property(c => c.ParentOrganizationId);
builder.HasOne(r => r.Organization).WithMany().HasForeignKey(fk => fk.OrganizationId);
builder.HasOne(r => r.ParentOrganization).WithMany().HasForeignKey(fk => fk.ParentOrganizationId);
builder.ToTable("OrganizationRelation", "dbo");
}
}
When I deploy my db with migration, I see this table created:
CREATE TABLE [mdo].[OrganizationRelation](
[OrganizationRelationId] [int] IDENTITY(1,1) NOT NULL,
[OrganizationId] [int] NOT NULL,
[ParentOrganizationId] [int] NULL,
[OrganizationId1] [int] NULL,
CONSTRAINT [PK_OrganizationRelation] PRIMARY KEY CLUSTERED
(
[OrganizationRelationId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
I'm using EF 5.0
I don't get it why is creating the column OrganizationId1
You didn't map OrganizationRelation.ParentOrganization to Organization.OrganizationRelations, so EF is adding an additional FK to OrganizationRelation for the second navigation property.
But that model seems overly complex. Why not just
public class Organization
{
public int OrganizationId { get; set; }
public string Name { get; set; }
public int OrganizationTypeId { get; set; }
public int? ParentOrganizationId { get; set; }
public Organization ParentOrganization { get; set; }
public ICollection<Organization> ChildOrganizations{ get; } = new HashSet<Organization>();
}
which creates
CREATE TABLE [Organization] (
[OrganizationId] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
[OrganizationTypeId] int NOT NULL,
[ParentOrganizationId] int NULL,
CONSTRAINT [PK_Organization] PRIMARY KEY ([OrganizationId]),
CONSTRAINT [FK_Organization_Organization_ParentOrganizationId] FOREIGN KEY ([ParentOrganizationId]) REFERENCES [Organization] ([OrganizationId]) ON DELETE NO ACTION
);
CREATE INDEX [IX_Organization_ParentOrganizationId] ON [Organization] ([ParentOrganizationId]);
?
To factor a foreign key out into a separate table, you just introduce a 1-1 dependent table, where the dependent table's FK and PK are the same as the main table's PK.
So to do the same thing with a separate entity would look like this:
public class Organization
{
public int OrganizationId { get; set; }
public string Name { get; set; }
public int OrganizationTypeId { get; set; }
public OrganizationRelation OrganizationRelation { get; set; }
public ICollection<OrganizationRelation> ChildOrganizations { get; } = new HashSet<OrganizationRelation>();
}
public class OrganizationRelation
{
public int OrganizationId { get; set; }
public int ParentOrganizationId { get; set; }
public Organization Organization { get; set; }
public Organization ParentOrganization { get; set; }
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationRelation>().HasKey(c => c.OrganizationId);
builder.Entity<OrganizationRelation>()
.HasOne(r => r.Organization)
.WithOne(o => o.OrganizationRelation)
.HasForeignKey(nameof(OrganizationRelation), nameof(OrganizationRelation.OrganizationId));
builder.Entity<OrganizationRelation>()
.HasOne(r => r.ParentOrganization)
.WithMany(o => o.ChildOrganizations)
.HasForeignKey(r => r.ParentOrganizationId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<OrganizationRelation>().ToTable("OrganizationRelation", "dbo");
base.OnModelCreating(builder);
}

Entity Framework Many to Many Relationship 3 Classes

Hi I have the three classes below. I'm trying achieve a many to many mapping with three classes. I have achieved many to many relationship between two classes but I'm trying to get another class in the mix. Below is the classes I have and a class representation of the relationship I'm trying to achieve.
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmployeeId { get; set; }
public ICollection<Department> Departments { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public string DepartmentCode { get; set; }
public string Description { get; set; }
public ICollection<Person> Members { get; set; }
}
public class Role
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Any hints on how to achieve the below would be helpful.
public class PersonRoleDepartment
{
public int PersonID { get; set; }
public int DepartmentID{ get; set; }
public int RoleID { get; set; }
}
public class Person
{
public int Id { get; set; }
public ICollection<PersonDepartmentRole> DepartmentRoles { get; set; }
}
public class Department
{
public int Id { get; set; }
public ICollection<PersonDepartmentRole> PersonRoles { get; set; }
}
public class Role
{
public int Id { get; set; }
public ICollection<PersonDepartmentRole> PersonDepartments { get; set; }
}
public class PersonDepartmentRole
{
[Key, Column( Order = 0 )]
public int PersonId { get; set; }
[ForeignKey( "PersonId" )]
[Required]
public virtual Person Person { get; set; }
[Key, Column( Order = 1 )]
public int DepartmentId { get; set; }
[ForeignKey( "DepartmentId" )]
[Required]
public virtual Department Department { get; set; }
[Key, Column( Order = 2 )]
public int RoleId { get; set; }
[ForeignKey( "RoleId" )]
[Required]
public virtual Role Role { get; set; }
}
Fluent API config:
var pdrConfig = modelBuilder.Entity<PersonDepartmentRole>()
.HasKey( pdr => new
{
pdr.PersonId,
pdr.DepartmentId,
pdr.RoleId
} );
pdrConfig.HasRequired( pdr => pdr.Department )
.WithMany( d => d.PersonRoles )
.HasForeignKey( pdr => pdr.DepartmentId );
pdrConfig.HasRequired( pdr => pdr.Person )
.WithMany( p => p.DepartmentRoles )
.HasForeignKey( pdr => pdr.PersonId );
pdrConfig.HasRequired( pdr => pdr.Role )
.WithMany( r => r.PersonDepartments )
.HasForeignKey( pdr => pdr.RoleId );
Note: all references from any of the primary entities to the others should route through your junction entity PersonDepartmentRole - if not, data inconsistencies can arise (e.g. I add a Department to a Person as well as a corresponding PersonDepartmentRole record to the DB, but then I remove the Department from the Person entity - the PersonDepartmentRole entity still remains)
Instead, if you want all departments for a person:
db.Person.DepartmentRoles.Select( dr => dr.Department ).Distinct()
In this construct, can a person be in a department but have no role? Or, can a department play a role but has no people? If not, how could this be accomplished

Entity Framework: field in table pointing to ID in same table (ParentID)

I've got the following model of my Request table:
public class Request
{
[Key]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 2)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
public string Memo { get; set; }
public DateTime DeadLine { get; set; }
**[ForeignKey("Label_ID, ID")]
[InverseProperty("Request")]
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }**
[ForeignKey("Label_ID")]
public virtual Label Label { get; set; }
[ForeignKey("RequestFrom")]
public int RequestFrom_UserID { get; set; }
public virtual ApplicationUser RequestFrom { get; set; }
[ForeignKey("RequestTo")]
public int RequestTo_UserID { get; set; }
public virtual ApplicationUser RequestTo { get; set; }
[ForeignKey("RequestAbout")]
public int? RequestAbout_UserID { get; set; }
public virtual ApplicationUser RequestAbout { get; set; }
public int? Project_ID { get; set; }
[ForeignKey("Label_ID, Project_ID")]
public virtual Project Project { get; set; }
}
}
The Parent_ID must be pointing to another Request by Label_ID and ID (composite key)
After creating a new add-migration I got the following error:
The ForeignKeyAttribute on property 'Parent_ID' on type 'iMaSys.Models.Request' is not valid. The navigation property 'Label_ID, ID' was not found on the dependent type 'iMaSys.Models.Request'. The Name value should be a valid navigation property name.
I know how to successfully point to other tables as you can see in RequestAbout_UserID. That migration was successfull. Only after adding Parent_ID I can't seem to solve this error.
Any help?
Best regards, Janno
You can use multiple ForeignKeyAttributes to refer to one parent, but you should also use the ColumnAttribute to indicate the column order of the key parts. But you've got an auto reference here, so there's a slight problem. The standard way to do this by data annotations would be:
public class Request
{
[Key]
[ForeignKey("Parent")]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("Parent")]
[Column(Order = 1)] // to refer to ID
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }
But this throws
The configured column orders for the table 'Requests' contains duplicates. Ensure the specified column order values are distinct.
You can fix this by changing a column index:
public class Request
{
[Key]
[ForeignKey("Parent")]
[Column(Order = 0)]
public int Label_ID { get; set; }
[Key]
[Column(Order = 2)] // Changed
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("Parent")]
[Column(Order = 1)]
public int Parent_ID { get; set; }
public virtual Request Parent { get; set; }
And EF accepts it. The absolute column indexes don't matter, the relative order is what counts.
Still... I wouldn't like the somewhat obscure feature that column 1 refers to column 2. The code doesn't explain itself. I would prefer fluent mapping:
modelBuilder.Entity<Request>()
.HasRequired(p => p.Parent)
.WithMany()
.HasForeignKey(p => new { p.Label_ID, p.Parent_ID })

How to use CodeFirst (EntityFramework, Microsoft) to make nullable complex column

I've got a definition like below and essentially, I want to create this in the EmailAccount class:
public EmailUser? EmailUserAccountInfo {get;set;}
the compiler gives me an error about non-nullable types. My goal is I want to make the EmailUser optional. I'm kind of confused because I can set EmailUserAccountInfo = null directly.
var r = new EmailAccount()
{
EmailUserAccountInfo = null,
Id = 1001
};
public class EmailAccount
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public EmailUser EmailUserAccountInfo { get; set; }
}
public class EmailUser
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public EmailAccount EmailAcount { get; set; }
public string EmailAddress { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
public string City { get; set; }
public string State { get; set; }
public int Temperature { get; set; }
public string WeatherString { get; set; }
public ImageDetail ImageOfUser { get; set; }
}
You can do this if you add a foreign key and you mark that nullable:
public class EmailAccount
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
// Foreign key
public int? EmailUserAccountInfoId { get; set; }
// Navigation property
public virtual EmailUser EmailUserAccountInfo { get; set; }
}
See this document about naming conventions for Code-First. (Scroll down to Relationship Convention)

Entity Framework's DbModel: How to map a one to many relationship using a connection table?

I'm trying to map via DbModel this relationship present on the database.
CREATE TABLE core.Institutes
(
ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(128) NOT NULL,
OldID INT NULL
)
GO
CREATE TABLE core.InstitutePlaces
(
FKInstituteID INT NOT NULL PRIMARY KEY REFERENCES core.Institutes(ID),
FKPlaceID INT NOT NULL REFERENCES core.Places(ID)
)
GO
CREATE TABLE core.Places
(
ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(128) NOT NULL,
FKParentID INT NULL REFERENCES core.Places(ID),
OldID INT NULL
)
GO
on this model
public class Place
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public Place Parent { get; set; }
}
public class Institute
{
public int Id { get; set; }
public string Name { get; set; }
public Place Place { get; set; }
}
we're using something like this to do the mapping
modelBuilder.Entity<Institutes.Institute>().HasOptional(i => i.Place);
but it doesn't work :(
This scenario is perfectly managed by the EDML file, so the problem is only about the mapping.
Something like this will give you (almost) the desired schema with one caveat: Code First does not create a 1:1 relationship in entity splitting scenarios which your desired schema (creating a 1:* association using a join table) is a special case of it.
public class Place
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public Place Parent { get; set; }
}
public class Institute
{
[DatabaseGenerated(DatabaseGenerationOption.None)]
public int Id { get; set; }
public string Name { get; set; }
public int? PlaceId { get; set; }
public Place Place { get; set; }
}
public class Context : DbContext
{
public DbSet<Place> Places { get; set; }
public DbSet<Institute> Institutes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Institute>().Map(mc =>
{
mc.Properties(p => new { p.Id, p.Name });
mc.ToTable("Institutes");
})
.Map(mc =>
{
mc.Properties(p => new { p.Id, p.PlaceId });
mc.ToTable("InstitutePlaces");
});
modelBuilder.Entity<Place>()
.HasOptional(p => p.Parent)
.WithMany()
.HasForeignKey(p => p.ParentId);
}
}
I had to switch off identity generation due to a bug that I explained here.