Can I use an Interface as a property with Entity Framework? - entity-framework-core

I have the following class which is a model for a database table:
public class User : IUser
{
internal int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public ITest TestData { get; set; }
}
When I run Update-Database command, it throws an error:
The property User.TestData is of an interface type (ITest). If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type
How and where can I manually configure the relationship for this property if I do not want the actual property to be a class?

EF does not support interfaces, but you can deal with it this way. Take a look on this solution How to use interface properties with CodeFirst

Related

.NET Core Entity Framework linking subtable to property

This is an existing .NET Core 3.1 project I inherited.
I have a class referring to a database table
public class SupportContract
{
public int Id { get; set; }
public DateTime StartDate { get; set; }
public int SupportContractStatusId { get; set; }
public virtual SupportContractStatus SupportContractStatus { get; set; }
}
and a sub table with a foreign key
public class SupportContractStatus
{
public int Id { get; set; }
public string SupportContractStatusName { get; set; }
}
This works fine I can get
supportContract.SupportContractStatus.SupportContractStatusName
But if I rename SupportContractStatusId to ContractStatusId in C# and the database, I get an error "SupportContractStatusId missing".
I cannot find any link between the column SupportContractStatusId and table SupportContractStatus anywhere in code nor is there any mention of the foreign key.
There is no link in the DbContext either.
Is this naming convention assumed by Entity Framework? How does the framework know of the foreign key?
Yes, the naming convention that EF expects by default is based on the class name, not the property name. It will look for ClassNameId or ClassName_Id. You can link the FK either through annotation or configuration.
I.e.
public int ContractStatusId { get; set; }
[ForeignKey("ContractStatusId")]
public virtual SupportContractStatus ContractStatus { get; set; }
Configuration is done through IEntityTypeConfiguration implementations or by implementing the OnModelCreating method in the DbContext and configuring the relationship within the modelBuilder. For occasional deviations from convention, the attribute approach can generally cover everything.

Entity Framework Core 2.0 - Error Migration "Field of entity type is readonly and so cannot be set"

There might be something wrong in my model that I cannot figure out since I get the following error when trying to make a migration:
"An error occurred while calling method 'BuildWebHost' on class Program. Continuing without the application service provider. Error: Field 'k__BackingField' of entity type 'MapeoArticuloProveedor' is readonly and so cannot be set.
Unable to create an object of type 'NSideoContext'. Add an implementation of 'IDesignTimeDbContextFactory' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time."
Entities:
[Table("MapeosArticuloProveedor", Schema = "public")]
public class MapeoArticuloProveedor
{
public string Codigo { get; set; }
public int? IdLadoDeMontaje { get; set; }
[ForeignKey("IdLadoDeMontaje")]
public virtual LadoDeMontajeMapeoArticulo LadoDeMontaje { get; }
}
[Table("LadosDeMontajeMapeosArticulos", Schema = "public")]
public class LadoDeMontajeMapeoArticulo
{
public string Codigo { get; set; }
public string Valor { get; set; }
}
What could it be?
#WanneBDeveloper basically you're exposing the property since you made it public. A more conservative approach would be to set it as follows:
public LadoDeMontajeMapeoArticulo LadoDeMontaje { get; private set; }
note the private keyword
Then the propery can only be set from within the class and not outside of it. Therefore you'll know which class is mutating it's state.
this is your issue:
public virtual LadoDeMontajeMapeoArticulo LadoDeMontaje { get; }
Basically the error is saying you can't set the "LadoDeMontaje" once it is retrieved.
Simply change it to:
public virtual LadoDeMontajeMapeoArticulo LadoDeMontaje { get; set; }

How do I tell UserManager.FindByNameAsync to include a relation?

I am using ASP.NET Identity 2.2.0 with ASP.NET MVC 5.2.3 and Entity Framework 6.1.2.
I added a new property and its corresponding table to my database using ASP.NET Identity with Code First like so:
public class ApplicationUser
{
[ForeignKey("UserTypeId")]
public UserType Type { get; set;}
public int UserTypeId { get; set;}
}
public class UserType
{
[Key]
public int Id { get; set;}
public string Name { get; set; }
}
Now, from some action, when I call:
var user = UserManager.FindByNameAsync(userName);
It does get the user with the correct UserTypeId because that is a primitive, but it does not get the UserType property of the ApplicationUser class.
If I were not using this abstraction, I would either call LoadProperty<T> or the Include method in Entity Framework to include the navigational property or relation named Type (of type UserType) on the ApplicationUser class.
How do I do that with ASP.NET Identity's UserManager? I suspect the only way would be to override this method in my custom UserManager derived class and do it myself?
With Entity Framework lazy loading, you need to ensure that your navigation properties are marked as virtual.
public class ApplicationUser
{
[ForeignKey("UserTypeId")]
public virtual UserType Type { get; set;}
public int UserTypeId { get; set;}
}
Alternatively if you are unable/don't want to use lazy loading, then you can still use your context as you would any other entity:
var user = context.Users.Include(u => u.Type).Single(u => u.UserName == userName);

Using enum as FK on EF 6

I'd like to use an enum as Foreign Key in a Code-First app. Since enums are stored as int, I thought I could use the attribute [ForeignKey] on the enum property, but it throws this exception:
The types of all properties in the Dependent Role of a referential constraint
must be the same as the corresponding property types in the Principal Role
Here is an example of what I am trying to do:
public enum UserType
{
Administrator = 1,
Member = 2
}
public class User
{
public int UserId { get; set; }
public string Login { get; set; }
[ForeignKey("TypeDetails")]
public UserType Type { get; set;}
public virtual MasterType TypeDetails { get; set; }
}
public class MasterType
{
public int MasterTypeId { get; set; }
public string Description { get; set; }
...
}
Is it possible to do this or something similar through fluent api or migrations?
Thanks
Here's one I made earlier: https://www.nuget.org/packages/ef-enum-to-lookup
It's a nuget package that provides a method you can call in your Seed (initializer and/or migrations) which will automatically build lookup tables and add FKs where the enum is used. Usage info.
Enjoy :-) And let me know if it works for you (or anyone else for that matter!)

Multiple inheritance with Entity Framework TPC

I tried to map some classes using Entity Framework in TPC style and got the following error:
Error: The type 'A' cannot be mapped as defined because it maps
inherited properties from types that use entity splitting or another
form of inheritance. Either choose a different inheritance mapping
strategy so as to not map inherited properties, or change all types in
the hierarchy to map inherited properties and to not use splitting.
This error occurs when I use the following classes:
public abstract class BaseEntityTest
public abstract class BaseEntityTest2 : BaseEntityTest
public abstract class BaseEntityTest3 : BaseEntityTest2
public class A: BaseEntityTest3 // this class is the only one with a table in the db
In the OnModelCreating method I added the following code to get the TPC mapping
modelBuilder.Entity<A>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("A");
});
When I exclude BaseEntityTest2 from the structure (so that A inherits only from BaseEntityTest instead of BaseEntityTest2) the error goes away. Does that mean that it is not possible to create this mapping or do I just miss something?
EDIT:
Properties of classes:
public abstract class BaseEntityTest
{
[Key]
public Guid Id { get; set; }
public String Info { get; set; }
[Required]
public DateTime CreationDate { get; set; }
[Required]
public String CreationUser { get; set; }
[Required]
public DateTime ModificationDate { get; set; }
[Required]
public String ModificationUser { get; set; }
[ConcurrencyCheck]
[Required]
public int LockVersion { get; internal set; }
}
public abstract class BaseEntityTest2 : BaseEntityTest
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
public abstract class BaseEntityTest3: BaseEntityTest2
{
[Required]
public DateTime FromDate { get; set; }
public DateTime ThruDate { get; set; }
}
public class A: BaseEntityTest3{
public String Test { get; set; }
}
The error occurs for EF 4.3.1 and earlier versions, but not for EF 4.4 and EF 5.0. (EF 4.4 is actually EF 5.0, but with .NET 4.0 as target platform.)
BUT: The error occurs only if you are using your abstract classes as entities in your model, that means
you either have DbSets for them in your context class, like
public DbSet<BaseEntityTestX> BaseEntityTestXs { get; set; }
or you have some Fluent mapping for BaseEntityTestX, some modelBuilder.Entity<BaseEntityTestX>()... stuff
or you are using one of the BaseEntityTestX as a navigation property in another (concrete) entity type
Do you need any of this?
Having a DbSet<BaseEntityTestX> in your context would only make sense if you really want to query for one of the abstract entities, like:
List<BaseEntityTest> list = context.BaseEntityTests
.Where(b => b.Info == "abc").ToList();
The result is of course a list of concrete entities that inherit from BaseEntityTest, but it can be a mix of different types, like some As and some Bs. Do you need such queries? Or do you only want to query for some of the concrete objects:
List<A> list = context.As
.Where(b => b.Info == "abc").ToList();
In the latter case you don't need a DbSet for the abstract base classes and you don't need any inheritance mapping. You can just remove the DbSet<BaseEntityTestX> from your context class and remove the TPC mapping and your error will go away.
The last point - having a navigation property to one of the abstract entities in another entity - doesn't make sense with TPC mapping. It is just not mappable to a relational database because with TPC mapping there is no table for the abstract entity, hence there is no target the foreign key relationship could refer to from the table of the concrete class that has the navigation property.
The error will also disappear if you extend your TPC mapping to the base classes:
modelBuilder.Entity<BaseEntityTestX>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BaseEntityTestX");
});
But it will create tables for those abstract entities that don't seem to make sense to me.
in EF6.0 its happed when
EntityTypeConfiguration'<'YourBaseClass'>'
did not detailed ALL your derived class with
this.Map<DerivedClass1>(m =>
{
m.MapInheritedProperties();
m.ToTable("..");
});
if just one dervied class in the assembley not configured like so
you get this exception