I want to use purpose-specific id types (e.g. PersonId for Person class). It requires me to map my custom class (PersonId) to an int and use it as primary key
public class PersonId
{
int Id;
}
public class Person
{
PersonId Id;
// ...
}
Can I map this classes in EF?
Putting a class as the primary key isn't possible. With Entity Framework primary keys must be a primitive type or a byte[].
If you want to do something like that for something else than primary key you could make the PersonId class a Complex type.
[ComplexType]
public class PersonId
{
int Id;
}
or with the fluent API
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<PersonId>();
}
but you won't be able to use that Id as the primary key of your entity
Related
I have an entity type MyEntity that has a primary key string MyEntityCode
I want to make a second entity MyEntityInfo that are extended properties that some MyEntity's are logically associated.
That makes the relationship between these entities one-to-one, with one end optional -- MyEntity logically optionally has a MyEntityInfo, without a navigation property, and MyEntityInfo is required to have a single MyEntity (with a navigation property).
I want to encode this in SQL as MyEntityInfo having a primary key BaseEntityCode that's also a foreign key to MyEntity's MyEntityCode.
How do I configure this encoding in EF6 fluent configuration API.
Sample code
public class MyEntity {
public string MyEntityCode {get; set;}
public int SomeProperty {get; set;}
}
public class MyEntityInfo {
public MyEntity BaseEntity {get; set;}
public string BaseEntityCode {get; set;}
public int OtherInfo {get; set;}
}
public MyEntityConfiguration : EntityConfiguration<MyEntity> {
public MyEntityConfiguration(){
HasKey(e => e.MyEntityCode);
}
}
I thought I could configure MyEntityInfo as
public MyEntityInfoConfiguration : EntityConfiguration<MyEntityInfo> {
public MyEntityInfoConfiguration(){
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional().WithForeignKey(e => BaseEntityCode);
}
}
but WithOptional() doesn't allow chaining to WithForeignKey
Doing the same but with WithMany() so that a foreign key is possible, the multiplicity constraint of one is violated:
Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
I thought I could configure MyEntityInfo as
public class MyEntityInfoConfiguration : EntityTypeConfiguration<MyEntityInfo>
{
public MyEntityInfoConfiguration(){
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional().WithForeignKey(e => BaseEntityCode);
}
}
Well, almost, just remove the WithForeignKey call!
public class MyEntityInfoConfiguration : EntityTypeConfiguration<MyEntityInfo>
{
public MyEntityInfoConfiguration()
{
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional();
}
}
Entity Framework 6 has only one implementation of 1:1 associations: the primary key of the dependent entity (here: MyEntityInfo) is the foreign key to the principal entity (here: MyEntity).
There is no WithForeignKey method because with your proposed mapping (without WithForeignKey) EF knows all it needs to know now for the only implementation of 1:1 it has in store.
The produced database model shows the primary key/foreign key dual role of BaseEntityCode:
CREATE TABLE [dbo].[MyEntities] (
[MyEntityCode] [nvarchar](128) NOT NULL,
[SomeProperty] [int] NOT NULL,
CONSTRAINT [PK_dbo.MyEntities] PRIMARY KEY ([MyEntityCode])
)
CREATE TABLE [dbo].[MyEntityInfoes] (
[BaseEntityCode] [nvarchar](128) NOT NULL,
[OtherInfo] [int] NOT NULL,
CONSTRAINT [PK_dbo.MyEntityInfoes] PRIMARY KEY ([BaseEntityCode])
)
...
ALTER TABLE [dbo].[MyEntityInfoes]
ADD CONSTRAINT [FK_dbo.MyEntityInfoes_dbo.MyEntities_BaseEntityCode]
FOREIGN KEY ([BaseEntityCode]) REFERENCES [dbo].[MyEntities] ([MyEntityCode])
How do you set up Entity Framework clustered index and foreign keys?
public class WorkDay {
public int Id { get;set;}
public DateTime Date { get;set;}
public Keyword Kw {get;set;}
}
public class Keyword {
public int Id { get;set;}
public string Name {get;set;}
}
I want to add index of Date and kw for the WorkDay entity, but cannot say how.
builder.Entity<WorkDay>().HasIndex(item => new { item.Date, item.Keyword });
this will give me error due to the fact that the mapping is only done for simple types
builder.Entity<WorkDay>().HasIndex(item => new { item.Date, item.Keyword.Id });
gives me error
The properties expression 'item => new <>f__AnonymousType21`2(Date = item.Date, Id = item.Keyword.Id)' is not valid. The expression should represent a property
What is the correct way?
Create a property for the foreign key (e.g. WorkDay.KeywordId) and reference that when defining the index.
I have the following classes generated from an edmx model:
public partial class A
{
public int Id { get; set; }
public virtual B B { get; set; }
}
public partial class B
{
public int Id { get; set; }
public virtual A A { get; set; }
}
The existing db doesn't use the EF default which expects A.Id to be the primary key of table B:
CREATE TABLE [dbo].[B] (
[Id] INT IDENTITY (1, 1) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[A] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[BId] INT NULL,
CONSTRAINT [fk] FOREIGN KEY ([BId]) REFERENCES [dbo].[B] ([Id])
);
With an edmx model, I can explicitly configure the multiplicity of each end, but I haven't found how to get the equivalent model using the fluent-api. When I do something like the following and generate a new db, the foreign key gets placed in table A instead of table B.
modelBuilder.Entity<A>().HasOptional(a => a.B).WithRequired(b => b.A);
I'm guessing I need to use a convention, but so far I've been unable to get the desired output.
UPDATE:
The closest solution I've found so far is to use the following which generates the correct SQL in the db:
modelBuilder.Entity<A>()
.HasOptional(a => a.B)
.WithOptionalDependent(b => b.A)
.Map(c => c.MapKey("BId"));
However, it's conceptually modeled as a 0..1:0..1 relationship and I haven't found how to set a CASCADE delete rule that deletes B when A is deleted.
I wasn't able to find a direct solution, but using the following code seems to meet my requirements of preserving the existing schema and creating a conceptual model that has the same multiplicities & delete behaviors as my original edmx model.
I'd still be interested in any solutions that don't require updating the conceptual model during the post-processing IStoreModelConvention.
{
var overridesConvention = new OverrideAssociationsConvention();
modelBuilder.Conventions.Add(overridesConvention);
modelBuilder.Conventions.Add(new OverrideMultiplictyConvention(overridesConvention));
}
private class OverrideAssociationsConvention : IConceptualModelConvention<AssociationType>
{
...
public List<AssociationEndMember> MultiplicityOverrides { get; } = new List<AssociationEndMember>();
public void Apply(AssociationType item, DbModel model)
{
if (multiplicityOverrides.Contains(item.Name))
{
// Defer actually updating the multiplicity until the store model is generated
// so that foreign keys are placed in the desired tables.
MultiplicityOverrides.Add(item.AssociationEndMembers.Last());
}
if (cascadeOverrides.Contains(item.Name))
{
item.AssociationEndMembers.Last().DeleteBehavior = OperationAction.Cascade;
}
}
}
private class OverrideMultiplictyConvention : IStoreModelConvention<EdmModel>
{
private readonly OverrideAssociationsConvention overrides;
public OverrideMultiplictyConvention(OverrideAssociationsConvention overrides)
{
this.overrides = overrides;
}
public void Apply(EdmModel item, DbModel model)
{
overrides.MultiplicityOverrides.ForEach(o => o.RelationshipMultiplicity = RelationshipMultiplicity.One);
}
}
I'm having an issue with discriminators in TPH inheritance with Entity Framework v6.1.1
I would expect that the discriminator should fall on the table that is representative of the base class. However, it appears EF is trying to map the discriminator column to the table mapped to the derived class
E.g.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Staff : Person
{
public decimal? Salary { get; set; }
}
public class MyContext : DbContext
{
public MyContext()
: base("MyConnectionString") {}
public virtual IDbSet<Person> Persons { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.Map(x => x.ToTable("Person"))
.Map<Staff>(x => x.ToTable("Staff").Requires("PersonTypeId").HasValue(1));
}
}
I am also using an existing schema too - i.e:
CREATE TABLE Person
(
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Name VARCHAR(50) NULL,
PersonTypeId INT NOT NULL
)
CREATE TABLE Staff
(
Id INT NOT NULL REFERENCES Person(Id),
Salary DECIMAL NULL
)
GO
However, when I try to add a new Staff, I encounter the following exception:
'System.Data.SqlClient.SqlException : Cannot insert the value NULL into column 'PersonTypeId', table 'MyDb.dbo.Person'; column does not allow nulls. INSERT fails.
The statement has been terminated'
It appears that it is trying to insert the discriminator (incorrectly) to the derived table. Hope someone can help.
So it appears that I've mis-understood the TPH setup for Entity Framework.
In my case, I'm wanting to map derived types to separate tables, which is an example of Table per Type - http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
Discriminators are redundant in this context.
I've posted my problem on codeplex http://entityframework.codeplex.com/workitem/2087.
There are also some questions posted here but they are not successfully answered.
See
Mapping TPT in EF Code First 4.1 w/ Different Primary Keys
Entity Framework 4 - TPT Inheritance in Features CTP5 (code first): rename foreign key column on inherited table
How can I use TPT inheritance models when primary keys have different names?
Is it now possible to have different column names for the primary keys when using TPT?
May be with 6.1.0
In TPT you're essentially do not want to declare the key in the subclasses, you'd miss the point otherwise.
If you must have a different Id name, just make proxy properties in the subclasses mapping to the base Id one.
public class BaseEntity
{
public int Id { get; set; }
}
public abstract class SubEntity : BaseEntity
{
public BaseId
{
get => Id;
set => Id = value;
}
}
Consider marking the sub fields as NotMapped, which in case you shouldn't include them in your LINQ queries.
With EF 6.4 I was able to use the ColumnAttribute to rename the Primary Key column in the dependent class
[Table("Person")]
public class Person
{
[Key]
public virtual int PersonId { get; set; }
// Person atributes...
}
[Table("Employee")]
public class Employee : Person
{
[Column("EmployeeId")] // <- Name of the primary Key column in the Database
public override int PersonId { get; set }
// Employee Attributes
}
Look at this code snip. Its work correct for me:
public partial class Person
{
// Any other PK name can thrown an exception
public int ID { get; set; }
}
public partial class Employee : Person
{
// Hide base class ID
private new int ID { get; set }
// Define derived class ID (that wrapped inherited ID)
[NotMapped]
public int EmployeeID
{
get { return base.PersonID; }
set { base.PersonID = value; }
}
}
Now, we must rename the inherited ID (with fluent API) for database table:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.Property(e => e.ID)
.HasColumnName("EmployeeID");
}