I’m trying to incorporate EF in my application. I use EF6 and Code First. My object model has some generic classes and it seems that Code First accepts it partially (tables and columns are generated correctly).
Below an example:
public class Group : TimeableObject<GroupTimeline> {}
public class GroupTimeline : TimelineBase
{
public GroupTimeline() { }
public string Name {get; set;}
}
public abstract class TimelineBase
{
public int Id { get; set; }
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
}
public abstract class TimeableObject<T>
where T : TimelineBase
{
private DateTime? _snapshotDate;
public TimeableObject()
{
Timelines = new List<T>();
}
public int Id { get; set; }
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
public List<T> Timelines { get; set; }
public DateTime SnapshotDate
{
get { //some logic.. }
set { _snapshotDate = value; }
}
public T Timeline
{
get { //some logic.. }
}
}
Through fluent API I configured the inheritance as TPC. So I expect a table Groups and a table GroupTimeline with a foreign key to Groups in GroupTimeline.
First of all, the generated database looks correct except for the identity. The strange thing about this is that EF sees the Id property in e.g. Group and generates a PK in the database. Althought without an identity. The same for the Id property in the TimelineBase class.
Second, I don't want the property SnapshotDate to be mapped. Through fluent API I was not able to reach this. Here I get an exception telling me that the complete class is ignored or is a generic. So I assume that EF does not support generic classes although the database is generated correctly (not the identity). What's the story behind this??
With the use of data annotations I fixed my issues. I can get an identity with the attributes (on the Id property):
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
and I can prevent mapping the SnapshotDate property with:
[NotMapped]
below my fluent configuration:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>().Map(m =>
{
m.MapInheritedProperties();
});
modelBuilder.Entity<GroupTimeline>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Group_Timeline");
});
modelBuilder.Entity<TimeableObject<TimelineBase>>().HasKey(m => m.Id).Property( p => p.Id ).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<TimeableObject<TimelineBase>>().Ignore(m => m.SnapshotDate);
modelBuilder.Entity<TimelineBase>().HasKey(m => m.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
This throw's an exception:
The type 'Data.DataObjectModel.TimeableObject`1[Data.DataObjectModel.TimelineBase]'
was not mapped. Check that the type has not been explicitly excluded by using the
Ignore method or NotMappedAttribute data annotation. Verify that the type was
defined as a class, is not primitive or generic, and does not inherit from
EntityObject.
Regards,
Daniel Kaya
Related
I am working on a project in Entity Framework Core which uses POCOs to connect to a database. However, these POCOs cannot convey intent, which is necessary because of a role-based permission system. I need some way to convey roles alongside my POCO.
However, some of the requirements of the project make it difficult to use subclasses with additional fields to do this. Thus, I have been using interfaces and partial classes, as under normal circumstances, I can use an interface to control which parts of the class are being recognized.
For example, in following code I have a guest entity, with the role of who is editing the entity. Only the fields Id, Name, and Confirmed exist as columns within the database. Under most circumstances, I can cast to an IGuest to remove the "MyRole" field.
public partial class Guest : IGuest
{
public int Id { get; set; }
public string Name { get; set; }
public bool Confirmed { get; set; }
}
public partial class Guest : IRole
{
public string MyRole { get; set; }
}
public interface IGuest
{
public int Id { get; set; }
public string Name { get; set; }
bool Confirmed { get; set; }
}
public interface IRole
{
public string MyRole { get; set; }
}
However, if I try to create a DbSet of guests, it attempts to find a matching column for MyRole. I tried changing the following sets of code
public virtual DbSet<Guest> Guest { get; set; } = null!;
and
modelBuilder.Entity<Guest>(entity =>
{
entity.HasKey(e => e.Id);
});
into the following:
public virtual DbSet<IGuest> Guest { get; set; } = null!;
and
modelBuilder.Entity<IGuest>(entity =>
{
entity.HasKey(e => e.Id);
});
This results in an error
ArgumentException: The specified type '...IGuest' must be a non-interface reference type to be used as an entity type.
Is it possible to use an interface as part of a DbSet / entity to hide the MyRole field (and any others not in the IGuest interface) from Entity Framework so that it does not attempt to map it to a column?
I am having trouble with setting up a many-to-many join table with EF core if one side of the join table is a derived class in table-per-hierarchy set up.
Here's the set up:
class Chore
{
Guid Id;
}
class LaundryChore : Chore
{
// PROBLEMATIC
List<Clothing> ManyClothing;
}
class FoldingChore : Chore
{
Clothing SingleClothing;
}
class Clothing
{
Guid Id;
// PROBLEMATIC
List<Chore> Chores;
}
I have the TPH set up with discriminator and that all works fine. IF the ManyClothing field was on the Chore class then I can just do:
builder.Entity<Clothing>().HasMany(clothing => clothing.Chores)
.WithMany(chore => chore.ManyClothing);
and this works as expected.
But since ManyClothing field was on the LaundryChore class, I would get DNE error with above.
I tried switching the direction:
builder.Entity<LaundryChore>().HasMany(chore => clothing.ManyClothing)
.WithMany(clothing => clothing.Chores);
and I get a casting error:
Cannot implicitly convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.IEnumerable'
If I change to:
class Clothing
{
Guid Id;
List<LaundryChore> Chores;
}
Then the error I get is:
The filter expression ... cannot be specified for entity type 'LaundryChore'. A filter may only be applied to the root entity type 'Chore'
Any guidance would be appreciated - thanks!
A navigation properties can only participate in a single relationship.
It isn't a limitation type, but a limitation by relation. For example, if you have only the Chore and Clothing classes :
public class Chore
{
public int Id { get; set; }
public List<Clothing> ManyClothingToLaundry { get; set; }
public Clothing SingleClothingToFolding { get; set; }
}
public class Clothing
{
public int Id { get; set; }
public List<Chore> Chores { get; set; }
}
Now, you want add a Chore to a Clothing :
clothing.Chores.Add(chore);
Do this add a chore to laundry or to folding? EF Core can't know in this case.
In your specific case, EF Core could detect the relationship from the type, but this functionality is not implemented.
If Clothing has two distinct relationship, then Clothing need two navigation properties :
public class Clothing
{
public int Id { get; set; }
public List<Chore> FoldingChores { get; set; }
public List<Chore> LaundryChores { get; set; }
public IEnumerable<Chore> Chores => FoldingChores.Union(LaundryChores);
}
...
protected override void OnModelCreating(ModelBuilder builder)
{
...
builder.Entity<Clothing>().Ignore(c => c.Chores);
builder.Entity<Clothing>()
.HasMany<LaundryChore>(nameof(Clothing.LaundryChores))
.WithMany(chore => chore.ManyClothing);
builder.Entity<Clothing>()
.HasMany<FoldingChore>(nameof(Clothing.FoldingChores))
.WithOne(chore => chore.SingleClothing);
}
Clothing.FoldingChores is a collection of the base entity Chore for the example, but it can be directly a collection of the finale type FoldingChore. Idem with Clothing.LaundryChores :
public class Clothing
{
public int Id { get; set; }
public List<FoldingChore> FoldingChores { get; set; }
public List<LaundryChore> LaundryChores { get; set; }
public IEnumerable<Chore> Chores => FoldingChores.Cast<Chore>().Union(LaundryChores.Cast<Chore>());
}
I'm trying to design a data model using EF Core.
I have a Party class (superclass) that InformalOranization LegalOrganization and Person inherit from. Only LegalOrganization and Person have TaxNumber. Thus, This 2 classes have TaxNumber class property.
public abstract class Party
{
public Party()
{
Names = new List<Name>();
}
public int PartyId { get; set; }
public PartyType PartyType { get; set; }
public string Comment { get; set; }
}
public class Person : Party
{
public Person()
{
}
public Gender Gender { get; set; }
public DateTime Birthday { get; set; }
public TaxIdentificationNumber TaxIdentificationNumber { get; set; }
}
public class LegalOrganization : Organization
{
public LegalOrganization()
{
}
public DateTime RegistrationDate { get; set; }
public bool IsProfitable { get; set; }
public TaxIdentificationNumber TaxIdentificationNumber { get; set; }
}
public class TaxIdentificationNumber
{
public int TaxIdentificationNumberId { get; set; }
public string Number { get; set; }
public DateTime IssueDate { get; set; }
public int PartyId { get; set; }
public Party Party { get; set; }
}
I get this error.
Unhandled Exception: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The MERGE statement conflicted with the FOREIGN KEY constraint "FK_TaxIdentificationNumbers_Parties_PartyId". The conflict occurred in database "Teknowhow.DataModel080519_01", table "dbo.Parties", column 'PartyId'.
The statement has been terminated.
You can use an interface:
public interface Taxable
{
public TaxIdentificationNumber TaxIdentificationNumber { get; set; }
}
Then you can make Person and LegalOrganization inherit from it:
public class Person : Party, Taxable
{
// etc.
}
public class LegalOrganization: Party, Taxable
{
// etc.
}
After you added the implementation of the TaxIdentificationNumber class it's clear now how it should work and what the problem is. The TaxIdentificationNumber in Person and LegalOrganization should only be a reference navigation property using the PartyId in TaxIdentificationNumber as foreign key.
When I create the database for this model in EF Core 2.2.4 I do not get an error. But the result is not what you want. The Party property in TaxIdentificationNumber is of type Party but on the other side you have a Person and LegalOrganization type. So EF does not match them and it creates foreign keys for the TaxIdentificationNumber in Person and LegalOrganization which both end in the Parties table:
I tried to use fluent API:
modelBuilder.Entity<Person>()
.HasOne(p => p.TaxIdentificationNumber)
.WithOne(t => (Person)t.Party)
.HasForeignKey<TaxIdentificationNumber>(t => t.PartyId);
modelBuilder.Entity<LegalOrganization>()
.HasOne(l => l.TaxIdentificationNumber)
.WithOne(t => (LegalOrganization)t.Party)
.HasForeignKey<TaxIdentificationNumber>(t => t.PartyId);
But here it seems like the second wins. When I wanted to add data I could only assign a LegalOrganization to the Party property in TaxIdentificationNumber (not a Person). And for the Person a TexIdentifactionNumberId was created in the Parties-Table.
When I'm even more specific
modelBuilder.Entity<Person>()
.HasOne(p => p.TaxIdentificationNumber)
.WithOne(t => (Person)t.Party)
.HasForeignKey<TaxIdentificationNumber>(t => t.PartyId)
.HasPrincipalKey<Party>(t => t.PartyId);
modelBuilder.Entity<LegalOrganization>()
.HasOne(l => l.TaxIdentificationNumber)
.WithOne(t => (LegalOrganization)t.Party)
.HasForeignKey<TaxIdentificationNumber>(t => t.PartyId)
.HasPrincipalKey<Party>(p => p.PartyId);
I get this error:
You are configuring a relationship between 'TaxIdentificationNumber' and 'Person' but have specified a foreign key targeting 'Party'. The foreign key must be targeting a type that is part of the relationship.
So I assume EF does not allow such a model. I think it's not easy to create a model which does behave like you want and is quite reasonable. Your model has the flaw that you could assign an InformalOrganization to the Party property of the TaxIdentificationNumber.
The easiest workaround would be to add the reference navigation property TaxIdentificationNumber from Person and LegalOrganization to the Party class. With this approach it's even easier to wrongly assign a TaxIdentifactionNumber to a InformalOrganization. You could add a database CHECK CONSTRAINT with the Discriminator column to prevent this on the database level.
Another one would be to work with two foreign keys. One for Person and one for LegalOrganization. But this seems to be even uglier.
I like to use composition to group up like properties in a class so that they are easier to use when the number of fields increase. My question is, can entity framework handle modeling when we use composition and ComplexTypes in a class? When I use the two together, I get the following error during migrations:
The expression 'x => x.AllQuantities.OnHandUom' is not a valid
property expression. The expression should represent a property: C#:
't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'.
Here is the code. This is a simple model.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public AllQuantities AllQuantities { get; set; }
}
Note, AllQuantities is a Complex Type added through composition (I want the fields to appear in the same table as the products, but I want to group them under AllQuanties (Actually I have many more fields, but I have simplified the problem for here.))
[ComplexType]
public class AllQuantities
{
public double OnHand { get; set; }
public Uom OnHandUom { get; set; }
public int OnHandUomID { get; set; }
}
Please note in my Uom class, I am trying to set up a navigation property back to products through the complex type.
public class Uom
{
public int ID { get; set; }
public string Name { get; set; }
public double QtyInUom { get; set; }
public virtual ICollection<Product> OnHandUoms { get; set; }
}
Then, I have used the following fluent code to set up the model in my context file so that the navigation property works.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.HasRequired(x => x.AllQuantities.OnHandUom)
.WithMany(y => y.OnHandUoms)
.HasForeignKey(z => z.AllQuantities.OnHandUomID)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Note the
"HasRequired(x => x.AllQuantities.OnHandUom)."
I believe the dot notation is causing some kind of a problem. To my mind, it shouldn't.
I have tried the model creation by adding all the fields from my ComplexType AllQuantities to the product directly, as per normal, then everything works, however, when I separate the two as listed above, I get the error mentioned earlier. I would like to use the ComplexType so that I can group like fields.
My question is, is there any way to make the above work with a ComplexType and compostion?
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.