Are generic classes not supported as models in Entity Framework? - c#

I am trying to do something like this :
public class TrackerContext : DbContext
{
public bool TrackNewValues { get; set; }
public TrackerContext(bool trackNewValues = false)
: base()
{
TrackNewValues = trackNewValues;
}
public TrackerContext(string connectinString, bool trackNewValues = false)
: base(connectinString)
{
TrackNewValues = trackNewValues;
}
public DbSet<AuditLog<string>> AuditLog { get; set; }
public DbSet<AuditLogChild> LogChildren { get; set; }
}
public class AuditLog<UserIdOrUserNameColumnType>
{
public AuditLog()
{
Children = new List<AuditLogChild>();
}
[Key]
public Guid AuditLogID { get; set; }
public UserIdOrUserNameColumnType UserId { get; set; }
[Required]
public DateTimeOffset EventDateUTC { get; set; }
}
But I guess DbSet<AuditLog<string>> is not supported. I get this error:
Additional information: The type 'TrackerEnabledDbContext.AuditLog`1[System.String]' 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.
Is there any ways I can implement public DbSet<AuditLog<string>> AuditLog { get; set; } ?

You cannot map the generic type because Entity Framework simply doesn't support generic Entity types.
When using the EF Code-First approach you need to remember that you should model your POCO classes within the constraints that allow Entity Framework to create POCO proxies.
This means, shortly speaking that such a class:
Should not contain any attributes
Should not be generic
Should be public
Must not be sealed
Must not be abstract
Must have a public or protected constructor that does not have parameters

I have been using generic classes with success in Entity Framework.
If you declare your class and DbSet the following way it will work.
public class AuditLogString : AuditLog<String>{}
public DbSet<AuditLogString> AuditLogStrings { get;set;}
[Update]
I have not used this method recently and in the light of the comments on this answer I suggest Pawel's answer instead.
However I have not deleted this answer since I was able to use the method.

Related

How to migrate a class using Generic enums with the Entity Framework Core?

How would you migrate this Car Class to a database using the Entity Framework?
I have a Main car class (which inherits from a Car Interface Class) with one Generic property called Part
public class Car<Parts> : GenericCarInterface
{
public Parts Part { get; set; }
}
Then I have several enums representing different parts that can be passed into the Generic Parts property of the Car Class
public enum VokswagenParts
{
VWWheels,
VWLights,
VWEngine
}
public enum TyotaParts
{
ToyotaSpoiler,
ToyotaAirFilter,
ToyotaBrakes
}
public enum FiatParts
{
FiatExhaust,
FiatSuspension,
FiatCVOuter
}
I envisage setting up an ApplicationDbContext as follows but the EF does not know what to make of the Part property
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"ConnectionStringHere");
}
public DbSet<GenericCarInterface> Cars { get; set; }
}
Any Ideas on the ways that this can be accomplished using Entity Framework Core?
Looking at your enums, it looks like each part has a unique name. if you don't mind whether your parts are grouped or not, you could put all your parts together in single enum.
public enum Parts {
VWWheels,
VWLights,
VWEngine,
ToyotaSpoiler,
ToyotaAirFilter,
ToyotaBrakes,
FiatExhaust,
FiatSuspension,
FiatCVOuter
}
then in your Car class you can do this
public Parts Parts { get; set; }
EF should be able to handle the rest.
I think i may have figured out a solution.
If i can create a separate class in my repository to hold the enum number values and the enum type names:
public class Car
{
public Guid CarId { get; set; }
public Part Part { get; set; }
}
public class Part
{
public Guid CarId { get; set; }
public Guid PartId { get; set; }
public int Enum { get; set; }
public string EnumName { get; set; }
}
Then I can persist that standard class to the database then simply re-compile the code back to the original Car class format just before transferring between layers
I might create a separate enum for the part names just to keep it strongly typed but I like the idea of having the names persisted not just enum numbers

Entity Framework core 3.1.1 Multilevel Inheritance

An exception is thrown while running add-migration (EF core 3.1.1):
CLR property 'DiscriminatorLevel2Id' cannot be added to entity type 'CustomerBase' because it is declared on the CLR type 'InternalCustomer'
The following Image shows the needed hierarchy (briefly):
Mapping looks like:
// Discriminator (Level 1)
modelBuilder.Entity<CustomerBase>()
.HasDiscriminator(b => b.CustomerTypeId)
.HasValue<InternalCustomer>((int)CustomerType.Internal)
.HasValue<ExternalCustomer>((int)CustomerType.External);
// Discriminator (Level 2)
modelBuilder.Entity<InternalCustomer>()
.HasDiscriminator(b => b.DiscriminatorLevel2Id)
.HasValue<VIPCustomer>((int)DiscriminatorLevel2.VIP)
.HasValue<RegularCustomer>((int)DiscriminatorLevel2.Regular);
Is "Multilevel Inheritance TPH" supported on Entity Framework Core 3.1.1?
It's possible, but with single shared discriminator at the root abstract level containing values for all possible creatable (non abstract) direct or indirect derived entities.
Applying to your sample requires removing the DiscriminatorLevel2 property (column), removing Internal from CustomerType enum (assuming InternalCustomer is abstract) and merging Regular and VIP into it, e.g. something like this:
Model:
public abstract class CustomerBase
{
public int Id { get; set; }
public string Name { get; set; }
public int CustomerTypeId { get; set; }
}
public abstract class InternalCustomer : CustomerBase
{
public decimal Points { get; set; }
}
public class RegularCustomer : InternalCustomer
{
public int PartnerId { get; set; }
}
public class VIPCustomer : InternalCustomer
{
public string CardNo { get; set; }
}
public class ExternalCustomer : CustomerBase
{
}
public enum CustomerType { External, Regular, VIP }
Configuration:
modelBuilder.Entity<CustomerBase>()
.HasDiscriminator(b => b.CustomerTypeId)
.HasValue<ExternalCustomer>((int)CustomerType.External)
.HasValue<VIPCustomer>((int)CustomerType.VIP)
.HasValue<RegularCustomer>((int)CustomerType.Regular);
modelBuilder.Entity<InternalCustomer>();
When you want to query InternalCustomer derived entities, you could use db.Set<InternalCustomer>() or db.Set<CustomerBase>().OfType<InternalCustomer>() and EF Core will apply filter similar to t.CustomerTypeId IN (1,2), i.e. the IN clause will contain list of discriminator values for all final entities derived from InternalCustomer.

EF Core Code First Migration requires a primary key for a complex type

I have a data class which contains a subclass. They are used in a DbContext like this:
public class Foo
{
[Key]
public int Key { get; set; }
public SubFoo SubFoo { get; set; }
}
[ComplexType]
public class SubFoo
{
public double Value1 { get; set; }
public double Value2 { get; set; }
}
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) {
}
public DbSet<Foo> Foos { get; set; }
}
When trying to create a code-first migration, I get an error
The entity type 'SubFoo' requires a primary key to be defined
Since my class SubFoo is decorated with the attribute ComplexType and that the MSDN documentation states precisely that complex types do not have keys, I am a bit confused.
As mentioned here, I tried to replace the ComplexType annotation with an Owned attribute but it didn't solve the problem.
Am I misunderstanding how to make use of complex types?
Bottom line, after a long investigation, ComplexType are not yet supported in EF Core 2.2 when doing a Code First Migration.

entity framework multiple columns mapping

I'm trying to implement database structure in which there are common fields i put them in a separate abstract class but i want to know if 3 classes are inheriting from same abstract class and 2 have same property name so, by default entity framework will add a numeric followed by property name in database. Is there any way to implement this separately. I've studied complex types and searched over internet but couldn't find any flexible solution. I'm sharing my code, please guide me
public abstract class GenericImpression
{
[Key]
public int ImpressionId { get; set; }
public DateTimeOffset ReportingDate { get; set; }
}
public class Impression : GenericImpression
{
public string InventorySource { get; set; }
public string Media { get; set; }
}
public class Impression21 : GenericImpression
{
public string InventorySource { get; set; }
}
Now, EF will add one table with InventorySource1 and InventorySource Column.
Use OfType<>.
Example:
_context.GenericImpressions.ofType<Impression21>().ToList()

Can I use and return EF4 code-first POCO entities as their interface?

Given the code later in the question, I am getting the following error from the EF4 code-first API:
The given property 'Roles' is not a
supported navigation property. The
property element type 'IRole' is
not a supported entity type.
Interface types are not supported.
Basically, I have a Repository similar to the following:
public class Repository : IRepository {
private IEntityProvider _provider;
public Repository(IEntityProvider provider) {
_provider = provider;
}
public IUser GetUser(int id) {
return _provider.FindUser(id);
}
}
Notice that the IRepository.GetUser returns an IUser.
Let's say my IEntityProvider implementation looks like this.
public class EntityProvider : IEntityProvider {
public IUser FindUser(int id) {
/* Using Entity Framework */
IUser entity;
using (var ctx = new MyDbContext()) {
entity = (from n in ctx.Users
where n.Id == id
select (IUser)n).FirstOrDefault();
}
return entity;
}
}
The key here is that the IUser interface has a List<IRole> property called Roles. Because of this, it seems, the Entity Framework code-first cannot figure out what class to use to fulfill the IRole interface that property needs.
Below are the interfaces and POCO entities which would be used throughout the system and hopefully also used with EF4.
public interface IUser {
int Id { get; set; }
string Name { get; set; }
List<IRole> Roles { get; set; }
}
public interface IRole {
int Id { get; set; }
string Name { get; set; }
}
public class User : IUser {
public int Id { get; set; }
public string Name { get; set; }
public List<IRole> Roles { get; set; }
}
public class Role : IRole {
public int Id { get; set; }
public string Name { get; set; }
}
Am I going about this the wrong way? Is there a way to do this within the EF4 code-first API?
I can only think of the following:
Some sort of shadow property (List<Role> DbRoles) that is used by EF4 code-first. Then use Data Annotations to make sure the actual List<IRole> is ignored by EF4.
Create duplicate classes for all entities which EF4 code-first will use and then Map those to the official ones that implement the interface.
Remember, you need to make the base classes abstract, (check out inheritance with EF documents for that),
I recommend having a RootEntity with nothing in it, and then a Base entity with some common info, like Id, InsertedBy, UpdatedBy like standart fields, it makes everything much easier.
The use of Interfaces is not supported in EF 4 Code First (as of CTP5) and more than likely wont be supported in the RTM either. I would say make an abstract class in your DbContext to hold your objects.

Categories