Entity Framework Code First navigation issue - c#

I am trying to setup a navigation property that will hold zero or more elements of another table. The problem Entity Framework seems to be having is the other table has a composite primary key.
public class Alpha
{
public int Id { get; set; }
public int? BetaId { get; set; }
public virtual ICollection<Beta> Beta { get; set; }
public string Name { get; set; }
}
public class Beta
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SequenceNumber { get; set; }
public string Name { get; set; }
}
public class ABContext : DbContext
{
public DbSet<Alpha> Alpha { get; set; }
public DbSet<Beta> Beta { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Beta>()
.HasKey(b => new { b.Id, b.SequenceNumber });
}
}
I am not sure how to properly setup the relationship. I've tried several different things. Entity Framework either complains about not using both keys from the Beta class to define the navigation property, or creates a pair of extra columns in the Alphas table that doesn't properly link the tables.
The goal is Alpha should hold a set of zero or more Betas based on Beta.Id. A Beta may belong to zero or more Alphas. However, I'm not really interested in the Beta to Alpha relationship.
Any ideas?

So let's have a look at your requirements:
Alpha should hold set of zero or more Betas .... A Beta may belong to zero or more Alphas
It is many-to-many relation so you have to map it.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Beta>()
.HasKey(b => new { b.Id, b.SequenceNumber });
modelBuilder.Entity<Alpha>()
.HasMany(a => a.Beta)
.WithMany();
}
This will create additional table in database with trhee columns (probably called Alpha_Id, Beta_Id and Beta_SequenceNumber).
I still don't underestand what do you mean by based on Beta.Id. If alpha can hold only records with the same Beta.Id you will probably have to control this in the application logic. It is something that would need additional complicated constructs to be enforced by mapping.

Related

StarSchema - Entity Framework Core - Migration

I am trying to build a datawarehouse (DWH), using the code-first approach (star-schema):
Fact-/dimension classes:
[Table("FactBase")]
public class FactBase
{
[Key]
public Guid Id { get; set; }
[ForeignKey("DimTest1")]
public string DimDigitalesBuchISBN { get; set; }
public virtual DimTest1 DimTest1 { get; set; }
}
[Table("DimTest1")]
public class DimTest1
{
[Key]
public string ISBN { get; set; }
public string Bla { get; set; }
}
Context:
public class XDBContextDWH : DbContext
{
public DbSet<FactBase> FactBase { get; set; }
public DbSet<DimTest1> DimTest1 { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(new string("connection string"));
}
}
After using migration the schema looks like this:
Based on that star schema, shouldn't be the relationship (here: SQL) like that?
When you specify the attribute [ForeignKey("DimTest1")], you're telling EF to use DimTest1 as the navigation property of the FactBase class, not pointing to the DimTest1 class.
But since that property does not exist, it does not create the relationship.
Change your class to:
[Table("FactBase")]
public class FactBase
{
[Key]
public Guid Id { get; set; }
[ForeignKey("DimTest1")]
public string DimDigitalesBuchISBN { get; set; }
public virtual DimTest1 DimTest1 { get; set; } // navigation property
}
This should make it work as intended.
As you imply is your question, the star schema Fact table should use a composite key made up of the foreign keys it's referencing.
So I would say there are a couple issues with your situation that should be addressed.
First, a fact table probably shouldn't have a a column called Id, though it's not really going to hurt anything, it probably wouldn't ever be used to query by, so you are just adding extra data taking up disk space.
Second, and probably the answer you are looking for is, if you want a composite primary key on your fact table, you need to specify that in the Database Context.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FactBase>()
.HasKey(x => new { x.Id, x.DimDigitalesBuchISBN });
}
As I mentioned, you probably don't want to include the Fact.Id column in your PK but instead you would refer to more than one dimension like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<FactBase>()
.HasKey(x => new { x.Dim1Id, x.Dim2Id, x.Dim3Id});
}
Where Dim1Id, Dim2Id and Dim3Id are the primary keys of your dimensions.
I should also mention that you need to remove the [Key] attribute from the Id field of your FactBase class.
refer to: https://learn.microsoft.com/en-us/ef/core/modeling/keys?tabs=data-annotations

How to create one-to-one relationship in Entity Framework Core?

Let's start with one-to-many relationship:
public sealed class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Slave>()
.HasOne(typeof(Master))
.WithMany() // *
.HasForeignKey(nameof(Slave.ForeignField));
}
}
So I declare that per one record in Master table I can have multiple records in Slave table. When I run EF tools to build migration this is accepted and works fine.
But when I change the line marked with asterisk to:
.WithOne()
in order to build one-to-one relationship building migration fails with error:
You are configuring a relationship between 'Slave' and 'Master' but
have specified a foreign key on 'ForeignField'. The foreign key must
be defined on a type that is part of the relationship.
I don't get it, just a second ago the given field (property in C# terms) was OK, and now EF claims it cannot find it?
Whad do I miss? How to make EF happy?
Record types are as follows -- please note there are no navigational properties.
internal sealed class Slave
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid InstanceId { get; set; }
public Guid ForeignField { get; set; }
}
internal sealed class Master
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
}
For the time being I solved this using raw SQL, it works, but I am still curious what is wrong here.
Thanks to #AminGolmahalle answer, my curiosity was triggered why and can I use HasForeignKey in generic form. This lead me to finding out that I cannot, but what more is that WithOne and WithMany are not 1:1 replacements of each other. Both lead to different overloads.
So the first version one-to-many worked because I was hitting the right overload, the second didn't, because I was passing incorrect arguments. The correct version is:
builder.Entity<Slave>()
.HasOne(typeof(Master))
.WithOne()
.HasForeignKey(nameof(Slave), nameof(Slave.ForeignField)); // changed
the first argument has to be name of the slave table (again).
But is is even better to switch to generic version (see last comment under accepted answer) and avoid possibility of such "stupid" mistake in the first place.
Below code just sample for relation one to one:
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AuthorBiography Biography { get; set; }
}
public class AuthorBiography
{
public int AuthorBiographyId { get; set; }
public string Biography { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public string Nationality { get; set; }
public int AuthorRef { get; set; }
public Author Author { get; set; }
}
You Can Use FluentApi For Relation In EntityFramework:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>()
.HasOne(a => a.Biography)
.WithOne(b => b.Author)
.HasForeignKey<AuthorBiography>(b => b.AuthorRef);
}
Using FluentApi is much better than DataAnnotaion.
FluentApi In Asp Core
I Suggest To You Read About FluentValidation
Refer this link, i think this will do
https://www.learnentityframeworkcore.com/configuration/one-to-one-relationship-configuration

How to Define Foreign Key Relationships in Entity Framework other than the default method

I have a problem I am trying to solve with the Entity Framework 6.0 and hope someone here can give some direction on. I am much more comfortable with ADO.NET but want to do this project in EF.
I have an object called Policy and another called PayPlan
public class Policy
{
//Various properties not relevant
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set;}
public string Description { get; set; }
}
As you can see, in this example, a PayPlan is a child object for a Policy. It may be null, or there may be a single instance of a PayPlan associated with a policy.
When I run the model builder, it creates the tables and inserts a Foreign Key into the Policy Table for the record in the PayPlan. This doesnt really work for me though because 1) I would like to keep the Db schema similar to a previous version of the application wherein the PolicyId is a ForeignKey into the PayPlan and 2) With Cascading Deletes, if the PayPlan were to be deleted it would take the Policy with it and I need this to be the other way around. The Policy is the root object form which all other objects in the Db draw their relations. PayPlan, btw, is just a single example for this discussion but in the actual project the Policy object would contain a number of child objects associated with it in a similar manner.
My question, how do I set this up, either through Data Annotations or via the Fluent API, to achieve the schema I described?
If I understood your requirements correctly, you want to build model like this:
public class Policy {
[Key]
public int PolicyId { get; set; }
// this attribute is not required, but I prefer to be specific
// this attribute means navigation property PaymentPlan
// is "anoter end" of navigation property PayPlan.Policy
[InverseProperty("Policy")]
public virtual PayPlan PaymentPlan { get; set; }
}
public class PayPlan {
[Key]
public int PayPlanId { get; set; }
// define foreign key explicitly here
[ForeignKey("Policy")]
public int PolicyId { get; set; }
public virtual Policy Policy { get; set; }
public string Description { get; set; }
}
Update: the above works in EF Core, but does not work in EF 6. EF 6 treats this as one to many relationship (and is correct in that, because one Policy could have multiple PayPlans). To create one to (zero or) one relationship, you can create model like this:
public class Policy
{
[Key]
public int PolicyId { get; set; }
[InverseProperty("Policy")]
public virtual PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
[Key, ForeignKey("Policy")]
public int PolicyId { get; set; }
public Policy Policy { get; set; }
public string Description { get; set; }
}
So PayPlan doesn't have its own Id and instead has PolicyId which is both PK and FK. That way, only one (or none) pay plan may exist for one policy.
So, after digging a little bit about this on EF 6 after you mentioned you are using that version and found this:
Apparently alternate keys are not supported on EF 6. As #rowanmiller on this Github issue:
Unfortunately this is a limitation of EF6. You can not have a foreign
key property in a one-to-one relationship, unless it is also the
primary key property. This is essentially because EF6 doesn't support
alternate keys/unique indexes, so you can't enforce that a non-primary
key property is unique. The fact that you can do it when the foreign
key property isn't in the entity is a bit of a quirk... but obviously
not something we would remove :smile:.
BTW alternate keys (and therefore this scenario) is supported in EF
Core.
Mapping foreign key in HasOptional().WithOptionalDependent() relation
You can still have the FK as you want, but you can't have the FK property on your PayPlan class. If you do, you'll ended up with two FKs. So, if you configure your relationship like this:
public class Policy
{
public int PolicyId { get; set; }
public string Description { get; set; }
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set; }
public string Description { get; set; }
public Policy Policy { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PayPlan>()
.HasOptional(a => a.Policy)
.WithOptionalDependent(p => p.PaymentPlan)
.WillCascadeOnDelete(true);
}
You will end with this on SQL:
Didn't know about this since I never had this scenario. Sucks a lot. BUT you still can do it using EF core :), which is cool.
EF Core answer just for the record
You can solve this also using the FluentAPI. (I prefer the FluentApi rather than polluting my models with Attributes). Also, since you didn't mention which version of EF you are using, I assumed you are using EF Core.
public class Policy
{
public int PolicyId { get; set; }
public string Description { get; set; }
public PayPlan PaymentPlan { get; set; }
}
public class PayPlan
{
public int PayPlanId { get; set; }
public string Description { get; set; }
public Policy Policy { get; set; }
public int? PolicyId { get; set; }
}
Context class:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Policy>()
.HasOne(a => a.PaymentPlan)
.WithOne(b => b.Policy)
.IsRequired(false)
.HasForeignKey<PayPlan>(b => b.PolicyId)
.OnDelete(DeleteBehavior.Cascade);
}
This will produce the following tables on SQL:

Entity framework 6 code first - one way many to many via annotations

Is it possible to create one-way many-to-many association in entity framework 6 with code first and annotations? Example:
class Currency
{
public int id { get; set; }
}
class Country
{
public int id { get; set; }
// How i can annotate this property to say EF that it is many-to-many
// and it should create mapping table?
// I don't need navigation property to Country in Currency class!
public virtual IList<Currency> currencies { get; set; }
}
On Java + JPA annotations i can implement what i need this way:
#OneToMany
#JoinTable(name = "MAPPING_TABLE", joinColumns = {
#JoinColumn(name = "THIS_ID", referencedColumnName = "ID")
}, inverseJoinColumns = {
#JoinColumn(name = "OTHER_ID", referencedColumnName = "ID")
})
so, does EF have equal features?
You can do this by specifying the relationship explicitly using the Fluent API. Override the OnModelCreating() method of your DbContext class, and in your override specify the details of the mapping table like this:
class MyContext : DbContext
{
public DbSet<Currency> Currencies { get; set; }
public DbSet<Country> Countries { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>()
.HasMany(c => c.Currencies)
.WithMany() // Note the empty WithMany()
.Map(x =>
{
x.MapLeftKey("CountryId");
x.MapRightKey("CurrencyId");
x.ToTable("CountryCurrencyMapping");
});
base.OnModelCreating(modelBuilder);
}
}
Note that - in my quick test anyway - you will have to Include() the Currencies property when loading the EF object to have the list populated:
var us = db.Countries
.Where(x => x.Name == "United States")
.Include(x=>x.Currencies)
.First();
EDIT
If you really want to do everything with Data Annotations, and not use Fluent at all, then you can model the join table explicitly as pointed out elsewhere. There are various usability disadvantages of this approach, though, so it seems the Fluent method is the best approach.
class Country
{
public int Id { get; set; }
public virtual ICollection<CountryCurrency> CountryCurrencies { get; set; }
}
class Currency
{
public int Id { get; set; }
}
class CountryCurrency
{
[Key, Column(Order=0)]
public virtual int CountryId { get; set; }
[Key, Column(Order=1)]
public virtual int CurrencyId { get; set; }
public virtual Country Country { get; set; }
public virtual Currency Currency { get; set; }
}
I think you wanna learn how to separate relations from EF code first entities. I have started a topic in here about this question. I wanna separate the relation objects from entity, and I used partial classes. In my question I wanna learn how to separate partial classes by class bilrary. But couldn't.
While I was using NHibernate I was using XML mapping and creating relations here, In java platform is same thing. But I think Entity Framework is not ready yet.
You can do this in code first quite easily in EF 6.
public class Country
{
public int ID {get;set;}
public virtual ICollection<Currency> Currencys {get;set;}//don't worry about the name, pluralisation etc
}
public class Currency
{
public int ID {get;set;}
public virtual ICollection<Country> Countrys {get;set;}//same as above -
}
Compile it, run it and hey presto - magic join table in the background. Depends if the naming conventions bother you. I personally think if you are doing code first, you should do it all in the code. Some people prefer annotation, some prefer fluent API - use whichever you prefer.

MVC Scaffolding and EF 'One To Zero or One' relationships error

In my AspNet MVC 3 project when I try to scaffold an entity which has a One to Zero or One relationship with another entity I get "An item with the same index has already been added" error.
Essentially this happens when the Primary Key of the related table is also a Foreign Key.
At the moment my workaround is
Add an Id column to the related table and make it the primary key
Add Unique Key to the Foreign Key Column.
The problem with this is that EF will generate an ICollection navigation property for the related entity instead of just a property of the related entity type (which I can set to null in case of zero related entities)
Is this a know bug?
Am I doing something wrong?
Is there a better work around to get rid of the ICollection navigation property?
See my answer on this question:
How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?
That's the example code with the correct configuration.
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
public virtual ZoneMedia ZoneMedia { get; set; }
}
public class TestEFDbContext : DbContext
{
public DbSet<ZoneMedia> ZoneMedia { get; set; }
public DbSet<ZoneMediaText> ZoneMediaText { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.MediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasKey(zmt => zmt.ZoneMediaID);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.MediaText);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main (string[] args)
{
var dbcontext = new TestEFDbContext();
var medias = dbcontext.ZoneMedia.ToList();
}
}
You can also achieve this with DataAnnotations, but I generally prefer to keep my entity models as POCOs.
Try to use the [Key] attribute to the intended primary key. You may need to import the namespace System.ComponentModel.DataAnnotations
Also check the documentation about the full implementation of this namespace.
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx

Categories