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?
Related
I am not sure how to achieve the relation between 2 DbContexts. PurchaseOrderDbContext is a Code first approach & AgencyDbContext is an existing database. How can I include the "Division" from AgencyDbContext based on PurchaseOrder DivisionId?
To start off here is a very simplified version of my code.
Purchase Order Model
namespace Website.Models.PurchaseOrders
{
public class PurchaseOrder
{
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public string Name { get; set; }
public int DivisionId { get; set; }
public int StatusID { get; set; }
public Agency.Division Division { get; set; }
}
}
Division Model (this is in a different DbContext)
namespace Website.Models.Agency
{
public class Division
{
public int DivisionId { get; set; }
public string DivisionName { get; set; }
public string DivisionShortName { get; set; }
public string DivisionAbbrev { get; set; }
public int? DivisionDirectorEmpId { get; set; }
}
}
Agency DbContext
namespace Website.Models.Agency
{
public class AgencyDbContext : DbContext
{
public Agency DbContext(DbContextOptions<AgencyDbContext> options) : base(options)
{
}
public virtual DbSet<Division> Division { get; set; }
public virtual DbSet<Section> Section { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
}
PurchaseOrderDbContext
namespace Website.Models.PurchaseOrders
{
public class PurchaseOrderDbContext : DbContext
{
public PurchaseOrderDbContext(DbContextOptions<PurchaseOrderDbContext> options) : base(options)
{}
public DbSet<Status> Statuses { get; set; }
public DbSet<PurchaseOrder> PurchaseOrder { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
}
I get an the error InvalidOperationException: Lambda expression used inside Include is not valid. This is referring to the Include extension on Division.
var purchaseOrder = _context.PurchaseOrder
.Include(p => p.Division)
.Include(p => p.Status)
.OrderByDescending(p => p.OrderDate);
Thank you in advance!
Probably the only way to resolve is to make a query to the first context for items you are looking for, and then populate Division property with entries from second context
public class PurchaseOrderService
{
private readonly PurchaseOrderDbContext purchaseOrderDbContext;
private readonly AgencyDbContext agencyDbContext;
public PurchaseOrderService(PurchaseOrderDbContext purchaseOrderDbContext,
AgencyDbContext agencyDbContext)
{
this.purchaseOrderDbContext = purchaseOrderDbContext;
this.agencyDbContext = agencyDbContext;
}
public PurchaseOrder Get(int id)
{
var purchaseOrder = purchaseOrderDbContext.PurchaseOrder.FirstOrDefault(x => x.ID == id);
if (purchaseOrder == null)
{
return null;
}
purchaseOrder.Division = agencyDbContext.Division.FirstOrDefault(x => x.DivisionId == purchaseOrder.DivisionId);
return purchaseOrder;
}
}
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; }
}
I have below class :
public abstract class BaseAttachment
{
public Guid BaseAttachmentId { get; set; }
public string FileName { get; set; }
public string FileExtension { get; set; }
public string FileSize { get; set; }
public Folder Folder { get; set; }
public Guid? FolderId { get; set; }
public byte[] RowVersion { get; set; }
}
And I have below Table and Inherite BaseAttachment :
public class ProductGallery : BaseAttachment
{
public ProductHeader ProductHeader { get; set; }
public Guid? ProductHeaderId { get; set; }
}
and this :
public class Attachment:BaseAttachment
{
}
when Run Project and Create Database I have Below Tables :
BaseAttachment,ProductCategory
instead Of One Table (BaseAttachment) .
why ?
I was a config Class Like this for ProductCategoryConfig and Set TableName with below :
public class ProductGalleryConfig:EntityTypeConfiguration<ProductGallery>
{
public ProductGalleryConfig()
{
ToTable("ProductGallery");
Property(row => row.Code).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(row => row.RowVersion).IsRowVersion();
HasRequired(row => row.ProductHeader).WithMany(row => row.Galleries).HasForeignKey(row => row.ProductHeaderId).WillCascadeOnDelete(false);
}
}
and Comment below Line and its fix.
// ToTable("ProductGallery");
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
I have a very small object graph that I'm using:
public struct Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
etc...
}
public class User
{
public Address HomeAddress { get; set; }
public Address WorkAddress { get; set; }
public string FirstName { get; set; }
etc...
}
Using Entity Framework 4.1, how would I map this structure to one table so that they're mapped to columns like:
HomeAddressLine1
HomeAddressLine2
WorkAddressLine1
WorkAddressLine2
FirstName
LastName
etc...
EF doesn't support structures. You must use class for your Address and map it as complex type:
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
public class User
{
public int Id { get; set; }
public Address HomeAddress { get; set; }
public Address WorkAddress { get; set; }
public string FirstName { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(u => u.HomeAddress.AddressLine1)
.ColumnName("HomeAddressLine1");
// Use the same pattern for all columns of HomeAddress and WorkAddress
}
}