EntityFramework: Model 1:0..1 relationship with fluent api + conventions - entity-framework

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);
}
}

Related

configure related entities with the same key

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])

Stop empty strings at the database level with EF code first

Consider the following POCO entity for Entity Framework Code First:
public class Foo
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
}
Which will generate the following table:
CREATE TABLE [dbo].[Foo] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (100) NOT NULL,
CONSTRAINT [PK_dbo.Foo] PRIMARY KEY CLUSTERED ([Id] ASC)
);
Now, I understand that the default behavior of EF is to convert empty strings to null. So even if I explicitly feed it an empty string I will get a validation exception, which is perfect. The following code will throw a DbEntityValidationException:
var f = new Foo { Name = "" };
context.Foos.Add(f);
context.SaveChanges();
But, the problem is if I have an external application which accesses the database directly, I can perform the following query and it succeeds:
insert into dbo.Foo(Name)
values ('')
The best solution is arguably to not allow anyone to connect directly to the database and force them through a business layer. In reality however this may not always be possible. Especially if, say, I myself am importing external data via an SSIS package.
My best understanding says that applications should be set up to reject as much bad data at the lowest level possible. In this case this would mean the at database level. So if were creating the database the old fashioned way, I would add a constraint to check (Name <> '') and stop dirty data from ever being inserted in the first place.
Is there a way to get EF Code First to generate this constraint for me, or some other way to get it to enforce a non-empty-string (minimum length 1) at the database level - preferably using an attribute? Or is my only recourse to add the constraint manually in a migration?
There is MinLength attribute but it does not enforce the constraint on database level, you should add this constraint using migration I think.
public partial class test : DbMigration
{
public override void Up()
{
Sql("ALTER TABLE [dbo].[YOUR_TABLE] ADD CONSTRAINT " +
"[MinLengthConstraint] CHECK (DATALENGTH([your_column]) > 0)");
}
public override void Down()
{
Sql("ALTER TABLE [dbo].[YOUR_TABLE] DROP CONSTRAINT [MinLengthConstraint]");
}
}
You can add sql code generators for EF to generate these codes for MinLength attribute, I'll give you a simplified hint here:
First mark properties with MinLength
public class Test
{
public int Id { get; set; }
[MinLength(1)]
public string Name { get; set; }
}
Add MinLenghtAttribute to conventions and provide the value, which is the Length :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<MinLengthAttribute, int>(
"MinLength",
(property, attributes) => attributes.Single().Length));
}
the generated code for migration will be:
CreateTable(
"dbo.Tests",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(
annotations: new Dictionary<string, AnnotationValues>
{
{
"MinLength",
new AnnotationValues(oldValue: null, newValue: "1")
},
}),
})
.PrimaryKey(t => t.Id);
Override the SqlServerMigrationSqlGenerator to use this convention in order to generate the constraint sql code:
public class ExtendedSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(AddColumnOperation addColumnOperation)
{
base.Generate(addColumnOperation);
AddConstraint(addColumnOperation.Column, addColumnOperation.Table);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
base.Generate(createTableOperation);
foreach (var col in createTableOperation.Columns)
AddConstraint(col, createTableOperation.Name);
}
private void AddConstraint(ColumnModel column, string tableName)
{
AnnotationValues values;
if (column.Annotations.TryGetValue("MinLength", out values))
{
var sql = string.Format("ALTER TABLE {0} ADD CONSTRAINT " +
"[MinLengthConstraint] CHECK (DATALENGTH([{1}]) >= {2})"
,tableName, column.Name, values.NewValue);
Generate(new SqlOperation(sql));
}
}
}
the code above contains generation for AddColumn and CreateTable operations you must add codes for AlterColumn, DropTable and DropColumns as well.
Register the new code generator:
internal sealed class Configuration : DbMigrationsConfiguration<TestContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
SetSqlGenerator("System.Data.SqlClient", new ExtendedSqlGenerator());
}
}

TPH Discriminator not on base class / table

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.

Code First: TPT inheritance - Specify a different name for the primary key column in each table

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");
}

EF 4 Code First - Combine Views and Tables

I researched this question for days and cannot seem to find an option I feel good about; however, here is a link to a very similar question:
Add Calculated field to Model
Ultimately, I have the same question, but I am hoping for a better solution.
Consider the following DB Tables:
CREATE TABLE [Contact](
[ContactID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[ContactName] [varchar](80) NOT NULL,
[Email] [varchar](80) NOT NULL,
[Title] [varchar](120) NOT NULL,
[Address1] [varchar](80) NOT NULL,
[Address2] [varchar](80) NOT NULL,
[City] [varchar](80) NOT NULL,
[State_Province] [varchar](50) NOT NULL,
[ZIP_PostalCode] [varchar](30) NOT NULL,
[Country] [varchar](50) NOT NULL,
[OfficePhone] [varchar](30) NOT NULL,
[MobilePhone] [varchar](30) NOT NULL)
CREATE TABLE [Blog](
[BlogID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogName] [varchar](80) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
CREATE TABLE [Post](
[PostID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[BlogID] [int] NOT NULL, -- FK to BlogTable
[Entry] [varchar](8000) NOT NULL,
[CreatedByID] [int] NOT NULL, -- FK to ContactTable
[ModifiedByID] [int] NOT NULL -- FK to ContactTable
)
I now would like to use views for loading "common" lookup/calculated info. Every time we display a post on the site, we want to know the name of the person who created the post and who last modified it. These are two fields that are stored in separate tables from the post table. I could easily use the following syntax (assuming Lazy/eager loading was applied and CreatedBy was a property, of type Contact, based on CreatedByID): currentPost.CreatedBy.Name;
The problem with that approach is the number of Db calls and also the large record retrieved for contact, but we are only using Name 99% in this situation. I realize the DB schema above is tiny, but this is just a simplified example and the real contact table has about 50 fields.
To manage this type of situation in the past (prior to using EF), I have typically built out "detail" views for the tables I will use. The "detail" views contain common lookup/calculated fields so that it only takes 1 call to the DB to efficiently get all the info I need (NOTE: We also use indexing on our SQL views to make this extremely efficient for reading) Here is a list of views that I will commonly use (as they will contain "look up" fields from related tables):
ALTER VIEW [icoprod].[BlogDetail]
AS
SELECT B.[BlogID],
B.[BlogName],
B.[BlogDescription],
B.[CreatedByID],
B.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
(SELECT COUNT(*) FROM Post P WHERE P.BlogID = B.BlogID) AS PostCount
FROM Blog AS B
JOIN Contact AS CREATEDBY ON B.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON B.ModifiedByID = MODIFIEDBY.ContactID
ALTER VIEW [icoprod].[PostDetail]
AS
SELECT P.[PostID],
P.[BlogID],
P.[Entry],
P.[CreatedByID],
P.[ModifiedByID],
CREATEDBY.[ContactName] AS CreatedByName,
MODIFIEDBY.[ContactName] AS ModifiedByName,
B.Name AS BlogName
FROM Post AS P
JOIN Contact AS CREATEDBY ON P.CreatedByID = CREATEDBY.ContactID
JOIN Contact AS MODIFIEDBY ON P.ModifiedByID = MODIFIEDBY.ContactID
JOIN Blog AS B ON B.BlogID = P.BlogID
Here is an overview of my "POCO" objects:
public class Blog
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Post
{
public int ID { get; set; }
public string Name { get; set; }
public int CreatedByID { get; set; }
public DateTime ModifiedByID { get; set; }
}
public class Contact
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Title { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string MobilePhone { get; set; }
}
public class BlogDetails : Blog
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public int PostsCount { get; set; }
}
public class PostDetails : Post
{
public string CreatedByName { get; set; }
public string ModifiedByName { get; set; }
public string BlogName { get; set; }
}
The reason I like this approach is that it allows me to retrieve information from the database based on tables or views AND if I load a view, the view contains all the "table" information which would allow me to load from a view but save to a table. IMO, this gives me the best of both worlds.
I have used this approach in the past, but typically, I just loaded information from the DB using datarows or info from stored procs or even used subsonic activerecord pattern and mapped fields after loading from the DB. I am really hoping I can do something in EF that lets me load these objects without creating another layer of abstraction.
Here is what I have tried to use for configuration (using Fluent API and code-first EF):
public class PostConfiguration : EntityTypeConfiguration<Post>
{
public PostConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("PostID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Post");
});
}
}
public class BlogConfiguration : EntityTypeConfiguration<Blog>
{
public BlogConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("BlogID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Blog");
});
}
}
public class ContactConfiguration : EntityTypeConfiguration<Contact>
{
public ContactConfiguration()
: base()
{
HasKey(obj => obj.ID);
Property(obj => obj.ID).
HasColumnName("ContactID").
HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).
IsRequired();
Map(m =>
{
m.ToTable("Contact");
});
}
}
public class PostDetailsConfiguration : EntityTypeConfiguration<PostDetails>
{
public PostDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.PostDetails");
});
}
}
public class BlogDetailsConfiguration : EntityTypeConfiguration<BlogDetails>
{
public BlogDetailsConfiguration()
: base()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("icoprod.BlogDetails");
});
}
}
At this point, I have tried to use a view containing all of the information from the table with "extended" information and when I try this I get the dreaded 3032 error (error sample here). Then I tried to have the view ONLY contain the Primary key of the table and the "extended" properties (e.g. [Entry] is not in PostDetails view). When I try this, I get the following error:
All objects in the EntitySet 'DBContext.Post' must have unique primary keys. However, an instance of type 'PostDetails' and an instance of type 'Post' both have the same primary key value, 'EntitySet=Post;ID=1'.
So I have played with leaving off MapInheritedProperties a bit, but with no luck. I continue to get a similar error.
Does anyone have a suggestion on how to "extend" a base/table object and load info from a view? Again, I believe there is a big performance gain by doing this. The article I referenced at the beginning of this question has 2 potential solutions, but 1 requires too many DB hits (just to get some common lookup info) and the other requires an additional layer of abstraction (and I would really like to go directly to my POCO's from the DB, without writing any mapping).
Lastly, thank you to everyone who answers these types of questions. I applaud everyone who has contributed to responses over the years. I think too many of us developers take this information for granted!!
Loading record from view and saving it to table will not work with code mapping - Blog entity will always be loaded from table and saved to table and BlogDetail entity will always be loaded from view and saved to view - so you must have updatable view or instead of trigger to support this scenario. If you use EDMX you can also map custom SQL / Stored procedure executed for insert, update and delete to force saving to table but this feature is not available in code mapping. Anyway it is not your biggest problem.
You can use your view and you can map it to class as you did but you must not map the inheritance. The reason is the way how inheritance works. Inheritance says that entity is either parent or child (which can act as parent). There can never be database record which can be be both parent (I mean only parent) or child. It is even not possible in .NET because to support this scenario you need two instances - on of parent type and one of child type. These two instances are not equivalent because pure parent cannot be cast to child (it is not a child). And here comes the biggest problem. Once you map inheritance the key must be unique in the whole inheritance hierarchy. So you can never have two instances (one for parent and one for child) with the same key.
As a workaround don't derive BlogDetail from mapped entity (Blog). Either use third not mapped class as parent for both or interface. Also don't use MapInheritedProperties to make your BlogDetail completely unrelated to Blog.
Another workaround is not mapping BlogDetail at all. In such case you can use your code as is and instead of using a view create simple reusable query with projection:
var blogDetails = from b in context.Blogs
where ...
select new BlogDetail
{
Name = b.Name,
CreatedByID = b.CreatedByID,
...
CreatedByName = b.CreatedBy.Name // You need navigation property
...
};
In both cases if you need to save Blog you must create new instance and fill it from BlogDetail. After that you attach it to context, set it to modified state and save changes.