Just wondering if it was possible to add Data Annotations to a class referenced from a class library which has no reference on EntityFramework.
For example Project.Data.Entities library
public class User {
public long Id { get; set; }
public string UserName { get; set; }
}
Project.Data.Repositories.EntityFramework references Project.Data.Entities library. How can I add the Data Annotations regarding Key properties, Column names, Table names, etc.?
There are fluent APIs for this purpose.
EDIT
About your mapping you have to override OnModelCreating
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.ToTable("user")
.HasKey(_ => _.Id);
modelBuilder.Entity<User>()
.Property(_ => _.Id).HasColumnName("id");
modelBuilder.Entity<User>()
.Property(_ => _.UserName).HasColumnName("username"); // Add also HasMaxLength here
}
}
(if your database already exists and it's not created by EF on your model you need to disable also migrations)
EDIT
If you installed SQL Server with a CI codepage, column name casing is not important. So you need only to specify HasMaxLength
Related
I'm currently trying to write to a table which inherits from an abstract base class. When I try to do this I get the following error (The ContactMethod property is the discriminator):
System.Data.SqlClient.SqlException: Invalid column name 'ContactMethod'.
EmailContactDetails.cs:
public class EmailContactDetail : ContactDetail
{
[ApiMember(Description = "The Contact Method")]
public override ContactMethod ContactMethod => ContactMethod.Email;
[ApiMember(Description = "Email Address")]
public string EmailAddress { get; set; }
}
EmailContactDetailConfiguration.cs:
public class EmailContactDetailsConfiguration : IEntityTypeConfiguration<EmailContactDetail>
{
public void Configure(EntityTypeBuilder<EmailContactDetail> builder) => Configure(builder, "dbo");
public void Configure(EntityTypeBuilder<EmailContactDetail> builder, string schema)
{
builder.Property(x => x.EmailAddress).HasColumnName("EmailAddress").HasColumnType("nvarchar(255)");
}
}
ContactDetail.cs:
public abstract class ContactDetail
{
[ApiMember(Description = "The Identifier")]
public Guid Id { get; set; }
[ApiMember(Description = "The Contact Method")]
public virtual ContactMethod ContactMethod { get; set; }
}
ContactDetailConfiguration.cs
public class ContactDetailsConfiguration : IEntityTypeConfiguration<ContactDetail>
{
public void Configure(EntityTypeBuilder<ContactDetail> builder) => Configure(builder, "dbo");
public void Configure(EntityTypeBuilder<ContactDetail> builder, string schema)
{
builder.ToTable("ContactDetails", schema);
// Table per hierarchy. all subclasses share the same db table for performance.
builder.HasDiscriminator(x => x.ContactMethod)
.HasValue<EmailContactDetail>(ContactMethod.Email);
builder.Property(x => x.Id).HasColumnName("Id").IsRequired().HasColumnType("uniqueidentifier").ValueGeneratedOnAdd();
}
}
I've tried hiding the discriminator "ContactMethod" by adding the following to the ContactDetailConfiguration.cs file:
builder.Ignore(x => x.ContactMethod);
Once I've done that I end up with the following error
The entity type 'EmailContactDetail' is part of a hierarchy, but does not have a discriminator property configured.
You shouldn't hide the property configured as TPH discriminator from EF because it is essential for EF Core implementation of the TPH strategy.
The initial error simply indicates that your model and database are out of sync. It's true that by convention EF Core uses string shadow property and column called Discriminator. But the whole purpose of HasDiscriminator fluent API is to allow changing the discriminator property/column type, as well as mapping it to an existing property of your entity model.
Which is the case here. You've told EF Core to use your existing property ContactMethod as discriminator, hence EF Core is looking for column named ContactMethod in the database table. So to resolve the issue, simply update your database from the model (using the usual procedure when model is changed - add new migration, update database etc).
I have models which inherit from each other, but I am struggling to get the fluent api configuration to behave as I want it to. Let's say I have a base class which defines some core properties
public class Entity
{
public int Id { get; set; }
public string Title { get; set };
}
And a subclass for Book
public class Book : Entity
{
public int Edition { get; set; }
}
This way I can have books, magazines, pamphlets, comics, speeches etc, all inheriting from my Entity, and not have to define the relationship on every class.
Now I add my DbSets to the DbContext like this
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Books { get; set; }
public virtual DbSet<Magazine> Magazines { get; set; }
public virtual DbSet<Comic> Comics { get; set; }
}
And finally I add-migration Initial.
My migration now creates separate tables (TPC) for each type. Perfect.
The problem comes when I try to configure my base class using fluent API.
I add a configuration for Entity
class EntityConfiguration : IEntityTypeConfiguration<Entity>
{
public void Configure(EntityTypeBuilder<Entity> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Title).IsRequired();
}
}
The idea being that I will now only need to configure the base Entity, and all tables for the subclasses should pick up the configuration.
I add my configuration to the DbContext OnModelCreating method.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new EntityConfiguration());
}
When I add-migration, I end up with this
migrationBuilder.CreateTable(
name: "Entity",
columns: table => new
{
Edition = table.Column<int>(nullable: true),
Name = table.Column<string>(nullable: true),
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Discriminator = table.Column<string>(nullable: false),
Title = table.Column<string>(nullable: false),
Frequency = table.Column<int>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Entity", x => x.Id);
});
By trying to configure the base class, EF is now going down the TPH route, and creating a single table for Entity with a discriminator column.
Is there a way to avoid this? Is it even possible to configure the base class and have all concrete tables pick up the configuration on the base class but create tables for the subclasses?
NOTE: I have tried configuring the Entity directly in the DbContext OnModelCreating method, instead of using a separate configuration class, but this behaves just the same.
The EF Core docs actually say TPC is not supported, which is strange because it does create separate tables for the subclasses until I try to configure the base class.
I have tried using Ignore() to suppress the TPH but this has no effect.
Example given is not real-world. My actual project has many more classes which all have common properties and relationships, so I want to avoid having to configure the same things over and over again.
You are correct in saying that EF Core does not support TPC as of writing.
However, there does appear to be a way to get around this (for generating the 'Up' script at least).
Remove the FluentAPI registrations and use Annotations on the Entity class's properties:
public abstract class Entity
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
}
Also, because TPC is Table Per (Concrete) Class, it's good practice to make the class you're inheriting from abstract.
I always use TPC pattern in this way.
First, I mark the "Entity" as abstract.
EntityConfiguration.cs
public class EntityConfiguration<T> : IEntityTypeConfiguration<T> where T : Entity
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Title).IsRequired();
}
}
BookConfiguration.cs
public class BookConfiguration : EntityConfiguration<Book>
{
public override void Configure(EntityTypeBuilder<Book> builder)
{
base.Configure(builder);
builder.ToTable("book");
builder.Property(b => b.Edition).HasColumnName("edition");
}
}
With this kind of approach EF core will only create the "book" table without using any discriminator.
I have two classes as follows.
class Donkey
{
public Guid Id { get; set; }
}
class Monkey
{
public Guid Id { get; set; }
public Donkey Donkey { get; set; }
}
Now I want to configure the schema so that the relation is set in the database. Using Fluent API, I'll go something like this.
protected override void OnModelCreating(DbModelBuilder model)
{
base.OnModelCreating(model);
model.HasDefaultSchema("dbo");
...
model.Entity<Monkey>
.HasRequired(_ => _.Donkey)
.WithMany(_ => _.Monkeys)
.Map(_ => _.MapKey("DonkeyId"));
}
The problem is that now I have to declare a list of monkeys in the donkey. And I don't want to do that. I still want the monkey to point to a donkey using foreign key, so only required status won't do, because I need to specify my custom column name to store the FK pointing to the PK in the table of donkeys.
model.Entity<Monkey>.HasRequired(_ => _.Donkey);
So, the above lacks the mapping (and it doesn't compile when I just add it). Is there a way to work around it without actually changing the definition of Donkey class?
modelBuilder.Entity<Monkey>()
.HasRequired(x => x.Donkey)
.WithMany();
I have a class called Client mapped to a database table using Entity Framework code first. The table has a computed field that I need available in my Client class, but I understand that it won't be possible to write to this field. Is there a way of configuring Entity Framework to ignore the property when saving, but include the property when reading?
I have tried using the Ignore method in my configuration class, or using the [NotMapped] attribute, but these prevent the property from being read from the database.
You can use DatabaseGeneratedAttribute with DatabaseGeneratedOption.Computed option:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public ComputedPropertyType ComputedProperty { get; set; }
or if you prefer fluent api you can use HasDatabaseGeneratedOption method in your DbContext class:
public class EntitiesContext : DbContext
{
public DbSet<EntityType> Enities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EntityType>().Property(e => e.ComputedProperty).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
}
}
Mark property as computed:
modelBuilder
.Entity<MyEntityType>()
.Property(_ => _.MyProperty)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
How do I make non persisted properties using codefirst EF4?
MS says there is a StoreIgnore Attribute, but I cannot find it.
http://blogs.msdn.com/b/efdesign/archive/2010/03/30/data-annotations-in-the-entity-framework-and-code-first.aspx
Is there a way to set this up using EntityConfiguration?
In EF Code-First CTP5, you can use the [NotMapped] annotation.
using System.ComponentModel.DataAnnotations;
public class Song
{
public int Id { get; set; }
public string Title { get; set; }
[NotMapped]
public int Track { get; set; }
Currently, I know of two ways to do it.
Add the 'dynamic' keyword to the property, which stops the mapper persisting it:
private Gender gender;
public dynamic Gender
{
get { return gender; }
set { gender = value; }
}
Override OnModelCreating in DBContext and remap the whole type, omitting the properties you don't want to persist:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().MapSingleType(p => new { p.FirstName, ... });
}
Using method 2, if the EF team introduce Ignore, you will be able to easily change the code to:
modelBuilder.Entity<Person>().Property(p => p.IgnoreThis).Ignore();
If you don't want to use Annotations, you can use the Fluent API. Override the OnModelCreating and use DbModelBuilder's Ignore() method. Supposing you have a 'Song' entity:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Song>().Ignore(p => p.PropToIgnore);
}
}
You can also use EntityTypeConfiguration to move configurations to separate classes for better manageability:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SongConfiguration());
}
}
public class SongConfiguration : EntityTypeConfiguration<Song>
{
public SongConfiguration()
{
Ignore(p => p.PropToIgnore);
}
}
I'm not sure if this is available yet.
On this MSDN page the Ignore Attribute and API are described but below, in the comments, somebody writes on 4 june 2010:
You will be able to ignore properties in the next Code First release,
Add
using System.ComponentModel.DataAnnotations.Schema
to the model class. (Must include "SCHEMA")
Add [NotMapped] data annotation to the field(s) you want to keep from persisting (ie. not save to database).
This will prevent them from being added as a column to the table in the db.
Please note - previous answers may have included these bits, but they did not have the full "using" clause. They merely left off "schema" - under which the NotMapped attribute is defined.