EF TPT Inheritance: one DbSet per child? - entity-framework

In the MSDN they show how to implement EF TPT with this example:
public abstract class BillingDetail
{
public int BillingDetailId { get; set; }
public string Owner { get; set; }
public string Number { get; set; }
}
[Table("BankAccounts")]
public class BankAccount : BillingDetail
{
public string BankName { get; set; }
public string Swift { get; set; }
}
[Table("CreditCards")]
public class CreditCard : BillingDetail
{
public int CardType { get; set; }
public string ExpiryMonth { get; set; }
public string ExpiryYear { get; set; }
}
public class InheritanceMappingContext : DbContext
{
public DbSet<BillingDetail> BillingDetails { get; set; }
}
Regarding (*) I have this question:
Can I have a DbSet per child in the hierarchy? ie:
public class InheritanceMappingContext : DbContext
{
public DbSet<BankAccount> BankAccount { get; set; }
public DbSet<CreditCard> CreditCards{ get; set; }
}
I'm asking this because I'd like to have a main father class from which all the other classes inherit, ie:
public abstract class DatabaseItem
{
[Key]
public long DatabaseItemId { get; set; }
}
public User : DatabaseItem
{
...
}
public Picture : DatabaseItem
{
...
}
public Gallery : DatabaseItem
{
...
}
So that given an ID, I know that it corresponds to only one thing.
In the past in other projects I didnt do this, and used specifical IDs: "UserId", "PictureId", "GalleryId".
In this case, I will be having only one DbSet?
DbSet<DatabaseItem> DatabaseItems

Related

EF Core 5 and FromSqlRaw with inheritance

I want to use a FromSqlRaw query with view models and inheritance between them.
I created two example view models:
public class SummaryVM
{
public int Quantity { get; set; }
public int Value { get; set; }
}
public class DistrictVM : SummaryVM
{
public string DistrictName { get; set; }
}
My DbContext has this content:
public DbSet<SummaryVM> SummaryVMs { get; set; }
public DbSet<DistrictVM> DistrictVMs { get; set; }
...
modelBuilder.Entity<SummaryVM>().HasNoKey();
My query:
return await _context.DistritVMs.FromSqlRaw(query).ToListAsync();
but I get an error:
SqlException: Invalid column name 'Discriminator'. Invalid column name 'DistrictName'
I know this is due to the TPH pattern and I can solve this problem if I do not use inheritance yet how I can use these two tables with inheritance without TPH, please?
SOLUTION
I had to create a base class which is not registered as entity:
public abstract BaseClass
{
public int Quantity { get; set; }
public int Value { get; set; }
}
public class SummaryVM : BaseClass
{
}
public class DistrictVM : BaseClass
{
public string DistrictName { get; set; }
}
SOLUTION
I had to create a base class which is not registered as entity:
public abstract BaseClass
{
public int Quantity { get; set; }
public int Value { get; set; }
}
public class SummaryVM : BaseClass
{
}
public class DistrictVM : BaseClass
{
public string DistrictName { get; set; }
}

Entity Framework Multiple Inheritance with several discriminators

I want to realize structure like this:
public enum TreeType {
Product = 1,
User = 2,
Document = 3
}
public enum ProductType {
Service = 1,
Ware = 2
}
public enum DocumentType {
Order = 1,
Invoice = 2
}
public abstract class Tree
{
[Key]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[NotMapped]
public TreeType Type { get; set; }
}
public abstract class Product : Tree
{
[Required]
public string Article { get; set; }
[NotMapped]
public ProductType ProductType { get; set; }
public Tree
{
this.Type = TreeType.Product;
}
}
public class User : Tree
{
[Required]
public string Login { get; set; }
[Required]
public string Password { get; set; }
public User
{
this.Type = TreeType.User;
}
}
public abstract class Document : Tree
{
[Required]
public int PageCount { get; set; }
[Required]
public DateTime Created { get; set; }
[NotMapped]
public DocumentType DocumentType { get; set; }
public Document
{
this.Type = TreeType.Document;
}
}
public class Service : Product
{
[Required]
public int VisitCount { get; set; }
public Service
{
this.ProductType = ProductType.Service;
}
}
public class Ware : Product
{
[Required]
public string StorageName { get; set; }
public Ware
{
this.ProductType = ProductType.Ware;
}
}
public class Order : Document
{
[Required]
public string CustomerName { get; set; }
public Order
{
this.DocumentType = DocumentType.Order;
}
}
public class Invoice : Document
{
[Required]
public string SupplierName { get; set; }
public Invoice
{
this.DocumentType = DocumentType.Invoice;
}
}
public class TreeDbContext : DbContext
{
DbSet<Tree> Trees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Tree>().ToTable("L_TREES");
.Map<Product>(x => x.Requires("Type").HasValue((int)TreeType.Product)).ToTable("L_PRODUCTS");
.Map<User>(x => x.Requires("Type").HasValue((int)TreeType.User)).ToTable("L_USERS");
.Map<Document>(x => x.Requires("Type").HasValue((int)TreeType.Document)).ToTable("L_DOCUMENTS");
modelBuilder.Entity<Product>()
.Map<Service>(x => x.Requires("ProductType").HasValue((int)ProductType.Service)).ToTable("L_SERVICES");
.Map<Ware>(x => x.Requires("ProductType").HasValue((int)ProductType.Ware)).ToTable("L_WARES");
modelBuilder.Entity<Document>()
.Map<Order>(x => x.Requires("ProductType").HasValue((int)DocumentType.Order)).ToTable("L_ORDERS");
.Map<Invoice>(x => x.Requires("ProductType").HasValue((int)DocumentType.Invoice)).ToTable("L_INVOICES");
}
}
In database it looks like this:
enter image description here
Two-level inheritance I can implement both through TPH, and through TPT, but multi-level inheritance, and even with several descriptors, I can not implement.
As an exit, I can use inheritance and combination, but the implementation is cumbersome and requires a lot of manual action to support in the future.
I tried to implement this architecture, but I did not succeed.
Does anyone know how I can do this?

Alternative to List.Include in EF

I am having circular reference due to the EF code below. I have a Parent table with 2 childs and their grandchildren respectively. Is there any alternative to using Include, as I read something about Navigation property and it includes the parents in each of the child, causing the serialization having circular reference problem.
Error Message:
A circular reference was detected while serializing an object of type 'Product'.
This part is only to get retrieval and I don't need the parent reference back to each of the child. I debug inside and saw that inside each of the child, it has the parent reference back.
var query = db.Products.Include(c => c.ProductTriggers.Select(b => b.ProductTriggerItems))
.Include(d => d.ProductsExtra.Select(e => e.ProductAllotments.Select(m => m.ProductAllotmentDetails))).AsNoTracking();
return query.ToList();
Product Class
public partial class Product
{
public Product()
{
this.ProductExtras = new HashSet<ProductExtra>();
this.ProductTriggers = new HashSet<ProductTrigger>();
}
public int ProductID { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
public virtual ICollection<ProductProduct> ProductExtras { get; set; }
public virtual ICollection<ProductTrigger> ProductTriggers { get; set; }
}
ProductExtra Class
public partial class ProductExtra
{
public ProductExtra()
{
this.ProductAllotments = new HashSet<ProductAllotment>();
}
public int ProductExtraID { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<ProductAllotment> ProductAllotments { get; set; }
}
ProductAllotment Class
public partial class ProductAllotment
{
public ProductAllotment()
{
this.ProductAllotmentDetails = new HashSet<ProductAllotmentDetail>();
}
public int ProductAllotmentID { get; set; }
public int ProductExtraID { get; set; }
public virtual ProductExtra ProductExtra { get; set; }
public virtual ICollection<ProductAllotmentDetail> ProductAllotmentDetails { get; set; }
}
ProductTrigger Class
public partial class ProductTrigger
{
public ProductTrigger()
{
this.AddOnTriggerItems = new HashSet<ProductTriggerItem>();
}
public int ProductTriggerID { get; set; }
public int ProductID { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<ProductTriggerItem> ProductTriggerItems { get; set; }
}
ProductTriggerItem Class
public partial class ProductTriggerItem
{
public int ProductTriggerItemID { get; set; }
public int ProductTriggerID { get; set; }
public virtual ProductTrigger ProductTrigger { get; set; }
}

Sequence contains more than one matching element on schema update

I´m using ef-core inheritance like this:
public abstract class Person
{
public int Id { get; set; }
public string Name { get; set; }
public PersonType PersonType { get; set; }
public int PersonTypeId { get; set; }
public double Height { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}
public class Daughter : Person
{
public double Weigth { get; set; }
public DateTime SomeDate { get; set; }
}
public abstract class Son : Person
{
public DateTime BirthDate { get; set; }
public DateTime GraduationDate { get; set; }
}
public class SingleSon : Son
{
}
public class SonWithDaughter : Son
{
public int Daughter { get; set; }
public Daughter Daughter { get; set; }
}
In DbContext:
public DbSet<PersonType> PersonTypes { get; set; }
public DbSet<Daughter> Daughters { get; set; }
public DbSet<SingleSon> SingleSons { get; set; }
public DbSet<SonWithDaughter> SonWithDaughters { get; set; }
When I update sql server database (dotnet ef database update) it throws a
System.InvalidOperationException, Sequence contains more than one
matching element
Any ideas about how to solve this?
UPDATE
ef migrations console output
This is issue #5894. It will be fixed in version 1.0.1. Until that's released, you can use the nightly feed.

Which entities should be included in the DbContext, only the base class or every derived classes?

Album, Article, and Photo are all derived classes of Content.
public class MyContext : DbContext
{
//What set should be here ???!
}
I wonder whether only adding public DbSet<Content> Contents { get; set; } is enough or need to represent all the entities like this?
public DbSet<Album> Albums { get; set; }
public DbSet<Article> Articles { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Content> Contents { get; set; }
Thanks for helping.
I think you are looking for a sample of how to set up your model, this is an example.
you can create classes that are mapped to the same table.
public MyContext : DbContext
{
public MyContext() :
base("ConnectioString") // this is from the App.Config
{
}
public DbSet<Album> Albums { get; set; }
public DbSet<Article> Articles { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Content> Contents { get; set; }
[Table("Content")]
public class Content
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ContentId { get; set; }
[Required]
public bool Field { get; set; }
}
[Table("Content")]
public class Album
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ContentId { get; set; }
[Required]
public bool Field { get; set; }
}
}