Code first 2 columns as primarykey/index - c#

So I got a class called ObjectA it has many ObjectsB, the objects got a Id, to identify them but ALSO a language (enum, but it is actually saved as and INT and doesn t matter either for this case).
public class ObjectA
{
public int Id {get;set;}
public Language Language {get;set;}
... // and a list of objectA properties
public virtual ICollection<ObjectB> ObjectBs {get;set;}
}
public class ObjectB
{
public int Id {get;set;}
public Language Language {get;set;}
... // and a list of objectB properties
public ObjectA TheObjectA {get;set;}
}
Now I learned how to map them both as a primary key, I use fluent API for this. I know you can also use [Key] and [Column] attributes (which I don t use):
modelBuilder.Entity<ObjectA>().HasKey(a => new {a.Id, a.Languague})
modelBuilder.Entity<ObjectB>().HasKey(a => new {a.Id, a.Languague})
Now I tried a lot of stuff but I can't seem to be able to connect them with each other. Anyone has an idea how I can fix this?

If the principal (ObjectA) in a one-to-many relationship has a composite primary key the dependent (ObjectB) must have a foreign key that is composed of the same number of columns as well.
I assume that the related objects A and objects B must have the same language. In that case you can make ObjectB.Language the first part of the foreign key. (It's part of the primary and foreign key in ObjectB at the same time.) If you expose the second part of the FK as property in your model, it would look like this:
(I believe you must swap Id and Language for the following to work.)
public class ObjectA
{
public Languague Languague {get;set;}
public int Id {get;set;}
... // and a list of objectA properties
public virtual ICollection<ObjectB> ObjectBs {get;set;}
}
public class ObjectB
{
public Languague Languague {get;set;}
public int Id {get;set;}
... // and a list of objectB properties
public int TheObjectAId {get;set;}
public ObjectA TheObjectA {get;set;}
}
And the mapping with Fluent API:
modelBuilder.Entity<ObjectA>().HasKey(a => new { a.Languague, a.Id })
modelBuilder.Entity<ObjectB>().HasKey(b => new { b.Languague, b.Id })
modelBuilder.Entity<ObjectA>()
.HasMany(a => a.ObjectBs)
.WithRequired(b => b.ObjectA)
.HasForeignKey(b => new { b.Language, b.TheObjectAId });
If the languages can be different introduce a separate FK property for the language:
public Language TheObjectALanguage {get;set;}
public int TheObjectAId {get;set;}
public ObjectA TheObjectA {get;set;}
...and map:
modelBuilder.Entity<ObjectA>()
.HasMany(a => a.ObjectBs)
.WithRequired(b => b.ObjectA)
.HasForeignKey(b => new { b.TheObjectALanguage, b.TheObjectAId });
If the relationship is optional use WithOptional instead of WithRequired and make the FK properties nullable:
public Language? TheObjectALanguage {get;set;}
public int? TheObjectAId {get;set;}
I you don't want to have FK properties in your model you can use MapKey with Fluent API:
modelBuilder.Entity<ObjectA>()
.HasMany(a => a.ObjectBs)
.WithRequired(b => b.ObjectA)
.Map(m => m.MapKey("TheObjectALanguage, TheObjectAId")); // FK columns

Related

EntityFreamwork full entity and lite entity

i have table users
user table :
Id, Name , firstName , password , email , address , dateofBrith
i want to create two entity for user table one lite and other full
[Table("user")]
public class LiteUser
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
second entity
public class fullUser : LiteUser
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
but not I get error about no column discriminator
is possible to do somthing like my entity are same but one have more filed then the other entity
thank you in advance for help
Unfortunately, no. You can only define one entity to one table. Instead, you'd have to do a manual .Select off of the full entity to return a custom "Lite" entry because EF needs to know all the columns that tie to a specific table from the start.
Edit: The only way around this would be to create a view and map to that instead.
You can do something like this
[Table("user")]
public class LiteUser
{
public string Name {get;set;}
public int firstName{get;set;}
}
public class fullUser : LiteUser
{
public int ID {get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Use primary key public int ID {get;set;} value in the derived class
As Daniel points out, a table can be associated to a single entity definition, outside of Table Per Hierarchy inheritance, which isn't what you are looking for.
This was an old trick I used with NHibernate which isn't supported in EF.
With EF you can utilize Linq and ViewModels to avoid the need of Lite vs. Full models.
Given:
//Entity
public class User
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
// View Models...
public class LiteUserViewModel
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
public class FullUserViewModel : LiteUserViewModel
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Querying..
//Give me a list of lite data..
var viewModels = context.Users
.Where(x => x.DateOfBirth < startDate)
.Select(x => new LiteUserViewModel
{
UserId = x.UserId,
Name = x.Name,
FirstName = x.FirstName
}).ToList();
// Give me a full user.
var viewModel = context.Users
.Where(x => x.UserId = userId)
.Select(x => new FullUserViewModel
{
UserId = x.UserId,
// ... etc ...
}).SingleOrDefault();
You can leverage libraries like AutoMapper to handle mapping entity to view model. In cases where you just want to inspect data you don't need to define a view model / DTO, just use an anonymous type. The end result is the same in that EF will execute an optimized query to just return back the data you want rather than entire entities. You can optimize view models to flatten down hierarchical data using this technique. You do need to ensure that any methods or transformations in the .Select() are pure and EF compatible because EF will attempt to translate and pass those to SQL. More complex transformations should be done in the view model itself, or utilize an anonymous type select of the raw data, followed by a ToList/Single/etc. then .Select() into the view model with appropriate transformations via Linq2Object.
One option is to use table splitting which is when you map a single table to two or more entities. The difference with your requested solution is that the "additional" properties in the "full" configuration will be represented by another entity type. Example (for EF Core; EF6 will be very similar):
public class SplitTablePrincipal
{
[Key]
public int Id { get; set; }
public string PrincipalProperty { get; set; }
// principal entity has a nav property to the dependent entity
public virtual SplitTableDependent Dependent { get; set; }
}
public class SplitTableDependent
{
[Key]
public int Id { get; set; }
public string DependentProperty { get; set; }
}
public class SplitTablePricipalConfiguration : IEntityTypeConfiguration<SplitTablePrincipal>
{
public void Configure( EntityTypeBuilder<SplitTablePrincipal> builder )
{
//builder.HasKey( pe => pe.Id );
// establish 1:? relationship w/ shared primary key
builder.HasOne( pe => pe.Dependent )
.WithOne()
.HasForeignKey<SplitTableDependent>( de => de.Id ); // FK is PK
builder.ToTable( "YourTableName" );
}
}
public class SplitTableDependentConfiguration : IEntityTypeConfiguration<SplitTableDependent>
{
public void Configure( EntityTypeBuilder<SplitTableDependent> builder )
{
//builder.HasKey( de => de.Id );
// map dependent entity to same table as principal
builder.ToTable( "YourTableName" ); // same table name
}
}
You only need to include a DbSet for the SplitTablePrincipal entity type in your DbContext. When querying, the Dependent property will not be populated by default (your "lite" configuration); you would need to eager load the property for the "full" data configuration via .Include( stp => stp.Dependent ). You could also lazy load or explicitly load the Dependent property further down the line should you so choose. For example:
dbContext.Entry( principalEntity ).Reference( p => p.Dependent ).Load();

EF6 fluent relation with custom foreign key and FK property

I'm trying to migrate existing project to EF and already have strict database structure and mandatory set of properties. The problem I now fight with is on the following.
I have two classes:
public class Entity
{
public virtual long ID {get;set;}
public virtual long ContragentID {get;set;}
public virtual Contragent {get;set;}
}
public class Contragent
{
public virtual long ID {get;set;}
public virtual long EntityID {get;set;}
public virtual Entity {get;set;}
}
With the following entity mappings:
public class ClassContragentAccountMap : EntityTypeConfiguration<Contragent>
{
public ClassContragentAccountMap()
{
// Primary Key
HasKey(t => t.ID);
// Table & Column Mappings
ToTable("contragent");
Property(t => t.ID).HasColumnName("id");
Property(t => t.EntityID).HasColumnName("e_id");
HasOptional(t => t.Entity).WithOptionalDependent(t => t.Contragent);
}
}
public class ClassLegalEntityAccountMap : EntityTypeConfiguration<Entity>
{
public ClassLegalEntityAccountMap()
{
// Primary Key
HasKey(t => t.ID);
// Table & Column Mappings
ToTable("entity");
Property(t => t.ID).HasColumnName("id");
Property(t => t.ContragentID).HasColumnName("contragentid");
}
}
The problem is that relation throws an exception that Entity_ID column is not found. Is there a way to specify FK for the relation as attributes seems to not work here? I've also tried to use:
HasOptional(t => t.Entity).WithOptionalDependent(t => t.Contragent).Map(t=> t.MapKey("e_id"));
But then it conflicts with the already defined 'e_id' property. And I need both relation and the property present.
Is there any way to specify FK for relation and preserve ID properties in classes?
Probably I can go with remaking relation ID properties:
public virtual long ContragentID => Contragent?.ID;
public virtual long EntityID => Entity?.ID;
But I want to know is there any other way to do it.

Composite key entity and dont want to declare PK keys

OK so this should be simple. I have a class
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
}
These two navigation properties are also primary keys for the table.
Declaring PRoductId and CategoryIds are redundat. How can get configure the primary keys using the nav properties?
edit: Stupid me. I forgot something very important in my question above. Those two above are to point out the config. Then we have a third fk thats the selected config for the combination of Product and category. So above entity must be a materialized entity
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
public ProductCategoryType { get; set; }
}
Declaring ProductId and CategoryId are redundant. How can get configure the primary keys using the nav properties?
Shortly - you can't. While EF6 supports shadow property based FKs, it does not provide a way to configure the PK (and many other column related settings) using the shadow property names - [Key], [Column]data annotations cannot be applied on navigation property and HasKey fluent API requires primitive property selector expression. In general EF6 does not support shadow properties in PK.
All these limitations have been removed in EF Core. But in EF6, redundant or not, you must define the actual primitive properties in the entity and map them to the composite PK.
You have only to set up a relationship between Product and Category entities by navigation properties. EF will set up the correct table structure by its own as many-to-many relationship. So no own relationship entity is needed.
Please check this out: many-to-many-relationship in EF
e.g.:
Product class:
public class Product
{
// other properties
public virtual ICollection<Category> Categories { get; set; }
}
Category class:
public class Category
{
// other properties
public virtual ICollection<Product> Products { get; set; }
}
Or did I misunderstood your question?
EDIT:
IF you need an separate entity like your ProductConfig, than you should try to set it as a unique index constraint by following:
modelBuilder
.Entity<ProductConfig>()
.HasIndex(pc => new {pc.Category, pc.Product})
.IsUnique();
For further information you should read this: HasIndex - Fluent API
EDIT 2 (after getting info solution is for EF < 6.2 needed):
Well after your last question edit, another solution approach is needed.
Here we go...
You need a structure like followed:
Product
public class Product
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
Category
public class Category
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
ProductConfig
public class ProductConfig
{
// other properties
public virtual Category { get; set; }
public virtual Product { get; set; }
public virtual ProductCategoryType { get; set; }
}
To set up a unique constraint in EF < 6.2 you have to do it like that way:
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Category)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 1) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Product)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 2) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.ProductCategoryType)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 3) { IsUnique = true }));
in EF 6.2:
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.Category, p.Product, p.ProductCategoryType })
.IsUnique();
EDIT 3
If you have no primary key in your ProductConfig class or you used mine in the example where I added none, because I thought you already have that class.
It is possible to set up multiple properties as key. That will result in unique combinations too.
You would archive that with the following - instead of the index stuff:
modelBuilder.Entity<ProductConfig>()
.HasKey(pc => new { pc.Category, pc.Product, pc.ProductCategoryType });
For further information check out the MS docs.
You could also add an Id as primary key, than the indexes are needed.

Entity Framework: Foreign Key in code first

What is wrong in my code that i get below error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
Code:
Class Food:
public class Food
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public short Id { get; set; }
//some Property
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
Class Person:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//some Property
public string FirstName { get; set; }
[ForeignKey("BestFoodId")]
public Food BestFood { get; set; }
public short BestFoodId { get; set; }
public virtual ICollection<Food> FavoriteFoods { get; set; }
}
Seed Method:
protected override void Seed(MyContext context)
{
Food food1 = new Food() { Name = "foo1" };
Food food2 = new Food() { Name = "foo2" };
Food food3 = new Food() { Name = "foo3" };
context.Persons.AddOrUpdate(new Person()
{
FirstName = "Jack",
BestFood = food2,
FavoriteFoods = new List<Food>() { food1, food2, food3 }
});
}
Cause of the error: confused associations
This happens because Entity Framework, by convention, assumes that the inverse property of Person.BestFoodId is Food.Persons. Stated differently: Person.BestFood and Food.Persons are assumed to be the two ends of a one-to-many association, having Person.BestFoodId as foreign key.
You can verify that by adding an [InverseProperty] attribute to BestFood:
public class Person
{
...
[ForeignKey("BestFoodId")]
[InverseProperty("Persons")]
public Food BestFood { get; set; }
...
}
This causes the same error.
This error --no valid ordering-- always indicates a chicken-and-egg problem. In your case, EF tries to insert the foods, which need the generated Id of the inserted person as foreign key, while the inserted person needs the generated Id of the inserted foo2 food.
Solution: explicitly mapped association
In reality, Person and Food have two associations:
1-n: Food can be BestFood of n people.
n-m: n Foods can be the FavoriteFoods of m people.
In your model, BestFood doesn't have an inverse property, which could have been something as ...
public virtual ICollection<Person> BestFoodOf { get; set; }
... but it isn't necessary and because it's missing, it obscures how EF infers the associations.
You can fix this by explicitly mapping the associations, for instance in the OnModelCreating override of your DbContext subclass:
modelBuilder.Entity<Person>()
.HasRequired(p => p.BestFood)
.WithMany() // No inverse property
.HasForeignKey(p => p.BestFoodId)
//.WillCascadeOnDelete(false)
;
modelBuilder.Entity<Person>()
.HasMany(p => p.FavoriteFoods)
.WithMany(f => f.Persons)
.Map(m => m.MapLeftKey("PersonId")
.MapRightKey("FoodId")
.ToTable("PersonFavoriteFood"));
I have commented out WillCascadeOnDelete(false). You either have to add this line, or add ...
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
... to prevent multiple cascaded delete paths (a SQL Server restrictions).
Having this in place, EF knows how to determine a valid ordering for the inserts: it will will first insert the foods, then insert the person (using the generated foo2 Id as foreign key) and then the junction records in the PersonFavoriteFood table.
Looks like you have a circular dependency.
Answers are here:
Unable to determine a valid ordering for dependent operations
Unable to determine a valid ordering for dependent operations?
Entity Framework Code First Circular Dependices
Entity Framework 4: inheritance and Associations
Entity Framework Circular Reference
Code First Circular Reference Foreign Key Configuration
How to configure a self referencing object in entity framework
Optional improvements:
You should declare your navigation property as virtual!
If you are using C# 6.0 or above, change your [ForeignKeyAttribute] Data Annotation definition to [ForeignKey([nameof(BestFoodId))] to avoid errors with hard coded property names. nameof is a really cool compiler feature! :)

Entity Framework 4.1: Using TPH and mapping properties to existing database columns

I got this simple class hierarchy;
public class A
{
[Key]
public int Id { get; set; }
}
public class B : A
{
public string Name { get; set; }
}
public class C : A
{
public string Name { get; set; }
}
Using TPH this will end upp with a table looking something like this;
Table A, fields Id, Name, Name1, Discriminator.
I want class B and C name property to map to the same field, ie Name, using these mappings.
Property(x => x.Id)
.HasColumnName("Id");
Map<B>(m =>
{
m.Properties(p => new
{
Name = p.Name,
});
});
Map<C>(m =>
{
m.Properties(p => new
{
Name = p.Name,
});
});
How can I make sure to map to the same column for my subtypes (B and C) property Name? Is there a HasColumnName to be used with the Properties collection?
THP does not allow for you to share properties between the objects unless the property is on the base class. Using TPH, a property is either Shared between all implementations or belongs to one of the specialized implementations.
Place Name property on the base class and this should solve your problem.

Categories