Change EF6 key FK convention - c#

EF by default name my FKs as EntityName_id and I would like it to be named id_EntityName. How can I do that?
Edit1:
There are over 700 FKs here... automate this would be a lot faster I belive... Also intend to use the same answer to normalize composite PKs...

MSDN has an example of creating a custom ForeignKeyNamingConvention. You could modify this example to name the Foreign Keys according to your convention.
I haven't tested this, but here's some rough code that you might be able to build on:
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
var constraint = association.Constraint;
for (int i = 0; i < constraint.ToProperties.Count; ++i)
{
int underscoreIndex = constraint.ToProperties[i].Name.IndexOf('_');
if (underscoreIndex > 0)
{
// change from EntityName_Id to id_EntityName
constraint.ToProperties[i].Name = "id_" + constraint.ToProperties[i].Name.Remove(underscoreIndex);
}
}
}
}
}
You can then register your custom convention in your DbContext's OnModelCreating() method like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add<ForeignKeyNamingConvention>();
}

I think that the best way is to use fluent mapping, for instance
.Map(m => m.MapKey("id_EntityName")

You can do this through setting up the mappings for your entities.
public class User
{
public int Id {get;set;}
public virtual Address Address {get;set;}
}
public class Address
{
public int Id {get;set;}
//Some other properties
}
public class UserMapping: EntityTypeConfiguration<User>
{
public UserMapping()
{
HasOptional(u => u.Address).WithMany()
.Map(m => m.MapKey("Id_Address"));
}
}
//Override the OnModelCreating method in the DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuild.Configurations.Add(new UserMapping());
}

Related

How to create composite unique constraint on multiple value objects

I need to create a unique constraint on multiple fields, and those fields are ValueObjects.
Let's say I have this
public class MyEntity
{
public EntityCode Code {get;set;}
public SecondaryCode Second {get;set;}
}
public class EntityCode : ValueObject<string>
{
public string Value {get;set;}
public string Description {get;set;}
}
public class SecondaryCode: ValueObject<string>
{
public string Value {get;set;}
public string Description {get;set;}
}
I can create a unique constraint like this
public class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
...
builder.OwnsOne(p => p.Code)
.HasIndex(p => p.Value)
.IsUnique()
...
}
But I would like a composite key and I can't figure out how to do this with value object.
Take a look at this sample, it is not working as I expected
builder.OwnsOne(p => p.Code);
builder.OwnsOne(p => p.Second);
builder
.HasIndex(p => new { p.Code.Value, p.Seconde.Value })
.IsUnique()
Any help would be appreciated
A key serves as a unique identifier for each entity instance. Most entities in EF have a single key, which maps to the concept of a primary key in relational databases. You can also configure multiple properties to be the key of an entity - this is known as a composite key.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
builder.Entity<MyEntity>()
.HasKey(p => new { p.Code.Value, p.Seconde.Value });
}
Composite keys can only be configured using the Fluent API, conventions will never set up a composite key, and you can not use Data Annotations to configure one.
To create this composite primary key with these two columns, override DbContext.OnModelCreating(). This method is called when the model for a derived context has been initialized, but before the model has been locked down and used to initialize the context.
public class MyDbContext : DbContext
{
// Normal DbContext stuff here
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
builder.Entity<MyEntity>()
.HasKey(p => new { p.Code.Value, p.Seconde.AnotherValue });
}
}
after that generate the migration and update database.

EF Core 2.1 with Seed method and NotMapped attribute

I have a question, why my Strategy property is null, when i getting all DbSet from context? How to [NotMapped] property make visible in my backend?
My class looks like this:
public class Machine
{
[Key]
public int Id { get; set; }
[NotMapped]
public WorkStrategy Strategy { get; set; }
public double GetManHours() => Strategy.TimeOfWork(HoursPerDay);
}
WorkStrategy is an abstract class:
public abstract class WorkStrategy
{
public abstract double TimeOfWork(double hours);
}
public class FarmStrategy : WorkStrategy
{
public override double TimeOfWork(double hours) => // do things
}
public class CultivationStrategy : WorkStrategy
{
public override double TimeOfWork(double hours) => //do things
}
Part of my Seed method where i seeding machines looks like this:
//Machines
for(int i = 0; i < countOfMachines; i++)
{
Machine machine = new Machine { Id = i + 1 };
machine.Strategy = new FarmStrategy;
modelBuilder.Entity<Machine>().HasData(machine);
}
But when i call Machines from DB:
var machines = _context.Machines;
The Strategy property is null. Could you tell me, how to attach [NotMapped] property while seeding a db
? Is it possbile?
EDIT
When i want to add WorkStrategy as not "notmapped" i get an error from EF while i adding migration:
The entity type 'WorkStrategy' requires a primary key to be defined
But i dont want to make an table for WorkStrategy.
EDIT
My OnModelCreating in context class:
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Machine>().Ignore(x => x.Strategy);
builder.Seed();
base.OnModelCreating(builder);
}
Its not work as [NotMapped]
You can use fluent api ignore instead of notmapped
modelBuilder.Entity<Machine>().Ignore(x => x.Strategy );
I think your Problem is not the not mapped Attribute, but the Structure of your Classes.
If you had a Flag, which Type of Strategy is needed and adapt the Strategy-Property depending on that Flag to initialize a Strategy if it’s null, you could keep your Notmapped-Attribute or the Ignore-Method with Fluent-Api.

Entity Framework - Create Composite PK from value object

How can i create a composite primary key in parent using fields in a valueobject ?
The valueobject will not have an own table in the database, i want these two props to be inserted into the parents table.
ie.
Entities
public class Parent
{
public string Name { get;set; }
public MyValueObject MyValueObj { get;set; }
}
public class MyValueObject
{
public int Id { get;set; }
public int SSN { get;set; }
}
DbContext for parent
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().Property(new { p.MyValueObj.Id, p.MyValueObj.SSN}).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
I want the Parent table to look like this:
Composite PK
-----------
Id SSN Name
1 000000 Mikael
If you can use inheritance then it should look like this:
public class Parent : MyValueObject
{
public string Name { get;set; }
}
public class MyValueObject
{
public int Id { get;set; }
public int SSN { get;set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().Property(new { p.Id, p.SSN}).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
The reason why your implementation won't work is pretty simple - Entity Framework just can't transform your complex object (which is set as property) to SQL field, so it will try to find it as a referenced object in other table. In the code I've provided you don't have any complex objects as a property, so EF will easily map all properties to SQL columns.
UPDATE
Actually, I did some more investigations and found an article stating that you can use complex types. I have never used this (and even seen) before, so can't describe you all sides of this question, but what I see from article is that this complex property can't be optional, so you will need always initialize that. Also I can only suppose, but probably you don't even need to mark all fields of that complex type as primary key, so you should have something just like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>().Property(p => p.MyValueObj).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
base.OnModelCreating(modelBuilder);
}
Not sure if that will work, just give it a try :)

Primary Key Violation: Inheritance using EF Code First

I have following EF code first code. I am getting the following exception:
'GiftCouponPayment' does not contain an identity column.
The tables are successfully created in database. However, how can I get rid of this exception? Also, what is the reason for this exception?
Note: I am okay with any table schema as longs as the domain model (described using code first) is retained (and the data can be queried).
After continuing this exception, there is a another exception as below:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
{"Violation of PRIMARY KEY constraint 'PK_dbo.PaymentComponent'. Cannot insert duplicate key in object 'dbo.PaymentComponent'.\r\nThe statement has been terminated."}
Reference:
Entity Framework: Split table into multiple tables
Note: The resulting database schema is as shown below.
Code:
public class MyInitializer : CreateDatabaseIfNotExists<NerdDinners>
{
//Only one identity column can be created per table.
protected override void Seed(NerdDinners context)
{
//context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX IX_Payment_PayedTime ON Payment (PayedTime)");
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('Payment', RESEED, 1)");
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('GiftCouponPayment', RESEED, 2)");
context.Database.ExecuteSqlCommand("DBCC CHECKIDENT ('ClubCardPayment', RESEED, 3)");
}
}
//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{
public NerdDinners(string connString): base(connString)
{
}
protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
//Fluent API - Plural Removal
modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
//Fluent API - Table per Concrete Type (TPC)
modelbuilder.Entity<GiftCouponPayment>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("GiftCouponPayment");
});
modelbuilder.Entity<ClubCardPayment>()
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("ClubCardPayment");
});
}
public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
public DbSet<Payment> Payments { get; set; }
}
public abstract class PaymentComponent
{
public int PaymentComponentID { get; set; }
public int MyValue { get; set; }
public abstract int GetEffectiveValue();
}
public partial class GiftCouponPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
if (MyValue < 2000)
{
return 0;
}
return MyValue;
}
}
public partial class ClubCardPayment : PaymentComponent
{
public override int GetEffectiveValue()
{
return MyValue;
}
}
public partial class Payment
{
public int PaymentID { get; set; }
public List<PaymentComponent> PaymentComponents { get; set; }
public DateTime PayedTime { get; set; }
}
Client:
static void Main(string[] args)
{
Database.SetInitializer<NerdDinners>(new MyInitializer());
string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";
using (var db = new NerdDinners(connectionstring))
{
GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
giftCouponPayment.MyValue=250;
ClubCardPayment clubCardPayment = new ClubCardPayment();
clubCardPayment.MyValue = 5000;
List<PaymentComponent> comps = new List<PaymentComponent>();
comps.Add(giftCouponPayment);
comps.Add(clubCardPayment);
var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
db.Payments.Add(payment);
int recordsAffected = db.SaveChanges();
}
}
You are not specifying the ID field for your TPC / TPT mappings. Even with inheritance you need to do this when not running a TPH mappings. (To note, I'm also not sure on the MapInheritedProperties() call... This is generally used for TPH... not TPT)
//Fluent API - Table per Concrete Type (TPC)
modelbuilder.Entity<GiftCouponPayment>()
.HasKey(x => x.PaymentComponentID)
.Map(m =>
{
m.MapInheritedProperties();
m.ToTable("GiftCouponPayment");
})
.Property(x => x.PaymentComponentID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
This needs to be on each and every class mapping from the concrete type. If it were me I would go with TPH mapping where GiftCoupon as well as the other inheritance mapping so you end up with 1 table to represent the entire tree of objects using a discriminator column.
Regardless... The other thing you are missing in your base class is:
public byte[] Version { get; set; }
And the associated mapping for:
Property(x => x.Version).IsConcurrencyToken()
Which allows for optimistic concurrency.
Hopefully this helps a bit, let me know if you need further assistance or clarification.
I see that my initial advice about TPC wasn't correct because you are also using FK in the base class - do you see the PaymentComponent table? It should not be there in case of TPC inheritance. Try to use TPT inheritance (remove MapInheritedProperties from your mapping). This will end with the same correct database. Don't use reseed. The Id will be controlled just by identity column in PaymentComponent table (as it is at the moment).
On your PaymentComponent class decorate the ID with KeyAttribute
[Key]
public int PaymentComponentID { get; set; }

How not persist property EF4 code first?

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.

Categories

Resources