I'm using Entity Framework.
My Customer Entity is:
public partial class Customer
{
public virtual int ID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string CompanyName { get; set; }
}
And I have a BaseEntity class which is inherited by partial Customer class.
public class BaseEntity
{
public virtual int ID { get; set; }
}
public partial class Customer : BaseEntity
{
}
After that, I created a generic method as follows:
public static int GetId<T>(T entity) where T : BaseEntity
{
// id is zero after savechanges
var id = entity.ID;
return id;
}
My problem occurs when I want to get the id after SaveChanges() in the generic method, it's zero.
My test code is:
var db = new ContextDb();
db.Customers.Add(customer);
db.SaveChanges();
WriteLine($#"{customer.ID}"); // OK
WriteLine($#"{GetId(customer)}"); // Not OK --> ZERO
Thanks everyone for taking the time to try and help explain.
It is caused by the fact that, indeed, you have two ID fields: one (hidden) from BaseEntity (which has a value of zero) and one from Customer (which is not zero). Try to override the ID property or simply remove it from the Customer class declaration:
public partial class Customer
{
public override int ID { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string CompanyName { get; set; }
}
You can also mark the ID property and BaseEntity class as abstract to force to override the ID property in inherited classes:
public abstract class BaseEntity
{
public abstract int ID { get; set; }
}
NOTE: Previously accepted answer is not good advice, while it satisfies the issue, for new developers it encourages them to think that it is normal to make all properties virtual or common practice to override every virtual member from the base class.
Because BaseEntity already defines ID, you SHOULD NOT be trying to re-implement ID in the inheriting class unless you specifically want to augment the original implementation.
In this case we are not offering any different implementation to the base and so should not be trying to override it.
Conversely, if you do want to force every inheriting class to implement a function or property in their own way, then mark it as abstract, not virtual, and follow this advice.
Putting all 3 class definitions in-line, I would recommend the following solution:
public class BaseEntity
{
public int ID { get; set; }
}
public partial class Customer : BaseEntity
{
}
public partial class Customer
{
// ID is provided from the base!
//public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
}
Using virtual properties becomes dangerous as a design choice because there is a gray area where auto-properties might or might not be implemented in the base or other inheriting classes. If all classes in the inheritance tree use auto-properties, then there is no issue, however when any traditional getter or setter that is not auto is overriden, if the override does not call back to the base implementation, then any references to the fields or placeholders that were implemented in the base will not have their values set.
which is pretty close to the situation that OP has raised.
Specifically with ORM tools like EF, I would caution making the ID property virtual or abstract at all.
The ID is too important to leave to chance.
If you are using virtual properties to support Lazy Loading, in EF you only need to make navigational properties virtual to support this, not every property on a class.
Related
We are getting data for our application.
We have common set of data with common fields, PurchaseDate, Manufacturer, PurchaseAmount,
Then we have specific subclass (which contains specific vehicle info, Aircraft wingspan, or car miles/per gallon, or Boat floatation metrics.
Instead of having classes which are not required, how can we create a model with a specific data type?
public class VehicleInfo
{
public Datetime PurchaseDate { get; set; }
public string Manufacturer { get; set; }
public int PurchaseAmount { get; set; }
// specific data, not all these needed
public AircraftInfo AircraftInfo {get;set;}
public CarInfo Carinfo {get;set;}
public BoatInfo BoatInfo {get;set;}
}
I read Dependency injection is not ideal practice for DTOs? https://softwareengineering.stackexchange.com/a/83166/354368
You can choose from "is-a" (inheritance) or "has-a" (composition).
You already have what is common to all vehicles
public class VehicleInfo
{
public Datetime PurchaseDate { get; set; }
public string Manufacturer { get; set; }
public int PurchaseAmount { get; set; }
}
So you can use inheritance (and make VehicleInfo abstract)
public class AircraftInfo : VehicleInfo
{
...
}
or use composition
public class AircraftInfo
{
public VehicleInfo VehicleInfo { get; set; }
...
}
or mix composition and inheritance
public class PassengerAircraftInfo : AircraftInfo
{
public PassengerVehicleInfo PassengerVehicleInfo { get; set; }
...
}
If you have e.g. 5 out of 10 vehicles that shares a set of common properties in addition to common properties you already have, inheritance will require another abstract class that inherits from VehicleInfo. Composition will require a new property on each of the 5 vehicles.
What is best, depends on final result of your analysis for the project. Number of inheritance levels should be kept to a minimum in order to keep code complexity low.
I am creating an application using Entity Framework 6.0 and Database-First approach. After I updated model from the database, I realised the essential need models to be derived from a BaseEntity class. The reason is that I need the base class to access the Id property using the BaseEntity class because the model class is often not specified.
My current solution is simple. According to Luke answer I implemented the partial class schema and directly derived a model class from RootEntity. In fact, the RootEntity class has the Id property as the model classes do. The compiler says CS0114 warning that classes should override properties. Since the model is autogenerated it cannot override properties.
What is the best practice to solve the particular issue? I'd like to implement a cleared architecture but this unpretty pattern may confuse anyone who reads my code.
// autogenerated EF code
public partial class Education
{
public int Id { get; set; }
public string Title { get; set; }
public System.DateTime AwardDate { get; set; }
public Nullable<int> PersonId { get; set; }
public virtual Person Person { get; set; }
}
// the base custom class I wont others to be derived from
public class RootEntity
{
public int Id { get; set; }
}
// partial class deriving
public partial class Education : RootEntity { }
EDIT:
The best solution I've found is to release the RootEntity class as an interface IPrimary. It also doesn't allow to directly create an object and provides a clearer definition of the required functionality.
public interface IPrimary
{
int Id { get; set; }
}
The best solution I've found is to release the RootEntity class as an interface IPrimary. It also doesn't allow to directly create an object and provides a clearer definition of the required functionality.
public interface IPrimary
{
int Id { get; set; }
}
public partial class Education : IPrimary { }
I have a DbContext with ProxyCreationEnabled set to true (actually it's the default value).
As far as I remember, this enables EF to load proxy entities from database, so any change we make to properties are recognized by the change tracker, and we can call SaveChanges() like this:
using (var db = new MyDbContext())
{
var people = db.People.Where(p => p.Status = PersonStatus.New).ToList();
foreach (var person in people)
{
person.Name = "Something";
}
db.SaveChanges();
}
The problem is: why would EF not use the proxy for a specific class, even though ProxyCreationEnabled is true? The class is not sealed, so it should be able to use proxy.
Here is my sample class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime RegisterDate { get; set; }
public PersonStatus Status { get; set; }
}
To generate proxy for property it should be virtual
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime RegisterDate { get; set; }
public virtual PersonStatus Status { get; set; }
}
To get change tracking proxies, the
basic rule is that your class must be
public, non-abstract or non-sealed.
Your class must also implement public
virtual getters/setters for all
properties that are persisted.
Finally, you must declare collection
based relationship navigation
properties as ICollection<T> only.
They cannot be a concrete
implementation or another interface
that derives from ICollection<T> (a
difference from the Deferred Loading
proxy)
I'm struggling with TPT inheritance in MVC - EF.
I have an AAnimal abstract class and two classes that inherit it, Zebra and Lion. There is a Cage class which holds an AAnimal.
My problem is that because AAnimal is abstract, the EF cannot create an instance of it when I load all Cages. So what I want is a way to override this behavior and make it understand whether it needs to load a Zebra or a Lion.
Zebra and Lion have a Primary key which is also foreign key to Animal table. This is done by the EF (TPT inheritance model).
public abstract class AAnimal
{
[Key]
public int AnimalId { set; get; }
public string Name { set; get; }
}
public class Lion : AAnimal {}
public class Zebra : AAnimal {}
public class Εmployee
{
[Key]
public int EmployeeId { set; get; }
public string Name { set; get; }
}
public class Cage
{
[Key]
public int CageId { set; get; }
[ForeignKey("CagedAnimal")]
public int CagedAnimalId { set; get; }
public AAnimal CagedAnimal { set; get; }
[ForeignKey("CageEmployee")]
public int CageEmployeeId { set; get; }
public Employee CageEmployee { set; get; }
}
// Model mapping
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AAnimal>().ToTable("Animal");
modelBuilder.Entity<Lion>().ToTable("Lion");
modelBuilder.Entity<Zebra>().ToTable("Zebra");
modelBuilder.Entity<Εmployee>().ToTable("Εmployee");
}
// Load all cages
public ActionResult Index()
{
var allCages = db.Cages.ToList();
}
At this point all cages are loaded, all fields have values except the CagedAnimal which is null. Even the CagedAnimalId has value.
How can I tell the EF to follow the same procedure while saving data, in order to load entities?
Note that this is just an example. Also, TPT inheritance model has been selected over other inheritance models.
Getting an error when trying to set a ForeignKeyAttribute in a base class
class User { }
abstract class FruitBase
{
[ForeignKey("CreateById")]
public User CreateBy{ get; set; }
public int CreateById{ get; set; }
}
class Banana : FruitBase { }
class DataContext : DbContext
{
public DbSet<Banana> Bananas { get; set; }
}
If I move the FruitBase code into the banana, all is well, but I don't want to, as there will be many many fruit and I want to remain relatively DRY if I can
Is this a know issue that will be fixed by March?
Does anyone know a work around?
The problem caused by the fact that in your DbContext you put DbSet<Banana> instead of DbSet<FruitBase>. The following object model works as expected:
public class User
{
public int UserId { get; set; }
}
public abstract class FruitBase
{
public int Id { get; set; }
public int CreateById { get; set; }
[ForeignKey("CreateById")]
public User CreateBy { get; set; }
}
public class Banana : FruitBase { }
public class DataContext : DbContext
{
public DbSet<FruitBase> Fruits { get; set; }
}
You have to be aware that by doing this, you are essentially creating a Polymorphic Association and as of CTP5, not all of the inheritance mapping strategies allow polymorphic association. Here it works fine because you've used Table per Hierarchy (TPH).
Update: Use Table per Type (TPT) Strategy:
Polymorphic Associations work with TPT as well:
public class StackoverflowTestContext : DbContext
{
public DbSet<FruitBase> Fruits { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Banana>().ToTable("Bananas");
}
}
as discussed above, this is TPC (Table per Concrete Type), the answer/work around here is to take the ForeignKeyAttribute out of the base
class User{}
abstract class AuditObjectBase{ // was FruitBase
// [ForeignKey("CreateById")]
public abstract User CreateBy{ get; set; } // made abstract
public int CreateById{ get; set; } // both get and set required public
}
class ItemOne : AuditObjectBase{ // Was Banana
// added
[ForeignKey("CreateById")]
public override User CreateBy{ get; set; }
}
class ItemTwo : AuditObjectBase{ // Added
[ForeignKey("CreateById")]
public override User CreateBy{ get; set; }
}
class DataContext : DbContext{
DbSet<ItemOne> ItemOnes{ get; set; }
DbSet<ItemTwo> ItemTwos{ get; set; }
}
Not completely DRY but at least when you create an object which uses the AuditObjectBase it will force you to implement the Foreign Key property, some nice comments to remind you of the attribute and away you go
Basically it seams, attributes should be added to the class declared in the DbContext as an Entity, in this case has a DbSet property.