Read it several times and understood that i'm not so good in asking questions correctly. Made some corrections to the question.
I have 2 classes in my app: Contractor and ContractorGroup.
Each Contractor has a parent Contractor. Parent Contractor can see only it's dependants. And can not see dependants of it's dependants. Only one level of visibility.
Parent Contractor can group it's dependant Contractors into ContractorGroup.
So, many dependant Contractors for one ContractorGroup (many-to-one)
Contractor has a NavProperty ContractorGroup, the group to which it belongs. Each Contractor can be ONLY IN ONE ContractorGroup.
Example:
I'm the parent Contractor and have 5 dependant Contractors and i want to group first 2 of them into 1stContractorGroup and last 3 to 2ndContractorGroup.
So, options to implement:
First: I can not to include FK(VisibleToContractorId - id of my parent Contractor) in ContractorGroup which connects each of 2 groups to parent Contractor.
In this case i can do query similar to:
var ContractorGroupsToDispalayForParentContractor =
context.ContractorGroups.Where(p => p.Contractors.All(p => p.Parent == ParentContractor));
In other words: "Find all groups which consist of contractors with parent == ParentContractor"
In this option everything works fine. DbSchema is simple and clear. But i dont like the query.
Second: Or i can introduce FK(VisibleToContractorId). So one parent Contractor has many ContractorGroups which consist of dependant Contractors. Then i have a simplier and more robust query:
var ContractorGroupsToDispalayForParentContractor =
context.ContractorGroups.Where(p => p.VisibleToContractor == ParentContractor);
This query i like. But EF introduces strange DbColumn which is ALWAYS null. >:-E
Short db schema:
Table("Contractor")
ContractorId = PK
ContractorGroupId = FK
ContractorGroup_ContractorGroupId = FK <--- This One
Table("ContratorGroup")
ContractorGroupId = PK
VisibleToContractorId = FK
My domain Classes and EntityConfiguration:
public class Contractor : IObjectWithState
{
[Key]
public virtual int ContractorId { get; set; }
public virtual Contractor Parent { get; set; }
public virtual int? ParentId { get; set; }
public virtual ContractorGroup ContractorGroup { get; set; }
public virtual int? ContractorGroupId { get; set; }
public virtual ICollection<ContractorGroup> GroupsVisible { get; set; }
}
public class ContractorGroup : IObjectWithState
{
[Key]
public virtual int ContractorGroupId { get; set; }
public virtual Contractor VisibleToContractor { get; set; }
public virtual int? VisibleToContractorId { get; set; }
public virtual ICollection<Contractor> Contractors { get; set; }
}
Entity configurations (only in ContractorGroupConfiguration):
HasMany(p => p.Contractors).WithOptional(p=>p.ContractorGroup);
HasOptional(p => p.VisibleToContractor).WithMany(
p=>p.GroupsVisible).HasForeignKey(p=>p.VisibleToContractorId);
Is it a bug in EF?
What implementation First or Second of domain model would you prefer?
Thanks.
The point is that you need to tell EF that the associations ContractorGroup.VisibleToContractor and Contractor.GroupsVisible are no independent associations (with two FK fields), but are two parts of a bidirectional association. Replace the entity configurations by:
class ContractorConfiguration : EntityTypeConfiguration<Contractor>
{
public ContractorConfiguration()
{
HasMany(c => c.GroupsVisible).WithOptional(g => g.VisibleToContractor);
}
}
Without this, EF creates the field ContractorGroup_ContractorGroupId as a FK for Contractor.GroupsVisible on top of the FK VisibleToContractorId.
Related
I use EF Core (Code first) I need to make relation between 2 tables by ID and Type
the following are my classes
public class Lead : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public short Status { get; set; }
public short PhoneType { get; set; }
public string Phone { get; set; }
public short EmailType { get; set; }
public string Email { get; set; }
public string Notes { get; set; }
public List<AddressInformation> AddressInformations { get; set; }
}
public class Opportunity : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<AddressInformation> AddressInformations { get; set; }
}
public class AddressInformationViewModel
{
public int Id { get; set; }
public int SourceID { get; set; } // in this column i need to store ID for Lead or Oppurtunity
public string RelatedTo { get; set; } // in this column i need to store text "Lead" or "Oppurtunity"
public short AddresType { get; set; }
public string Description { get; set; }
}
The AddressInformation class will hold information for Leads or Opportunity based on SourceID and RelatedTo columns
how we can handle this relation?
when I make Data Migrations EF will add new column in Lead Table the column name "LeadID" and I don't need this approach, is there any way to handle like this relation.
I would suggest considering a many-to-many joining table for each relationship rather than effectively a SourceId + SourceType association within Address. I.e. using a LeadAddress table and an OpportunityAddress table. With EF Core 5 and EF 6 you can associate these without a joining entity, just mapping the joining table, or create a joining entity for earlier EF Core, or if you need additional columns in the relationship.
The main advantage of using specific linking tables is that you can maintain FK relationships. With a SourceId + SourceType you cannot use SourceId as a FK to both Lead and Opportunity, however with a joining table, the LeadId can FK to lead while AddressId can FK to address. This helps keep querying address related details efficient.
A benefit, or limitation of this approach to consider is that with linking tables an address can legally be assigned to both a Lead and an Opportunity, or shared between Leads /Opportunities. If you don't want to support addresses being shared across multiple entities you would need to implement checks to prevent it. This does mean treating an address as a distinct location rather than merely a data container for a particular related entity. For example 123 Smithe St. is always 123 Smithe St. To change an address for a Lead would typically mean associating it with a new Address object with different values rather than editing the values of 123 Smithe St. (Unless they actually mean to correct the address, I.e. 123 Smith St.)
SourceId + SourceType can be implemented, but AFAIK this would have to be handled as separate unrelated entities and joined manually with queries, I.e. something like:
var query = context.Leads
.Join(context.Addresses.Where(a => a.RelatedTo == "lead"),
l => l.Id,
a => a.SourceId,
(l,a) => new {Lead = l, Addresses = a})
.Single(x => x.Lead.Id == leadId);
As queries get more involved this gets more complex to deal with the Join, and AFAIK you won't get something as useful as lead.Addresses out of the mapping/navigation properties where you can have lead.Addresses or at least lead.LeadAddresses using the dedicated linking table.
All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.
I have a domain model that has a collection of entities configured in the normal 1 to many relationship, however I want to store a reference to a specific item in that collection using a FK in this model
The list as defined in the model
public ICollection<SLWOUpdate> Updates { get; set; }
The reference to the specific item in the list
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public virtual SLWOUpdate LastUpdate { get; set; }
Of course the code is responsible for updating the specific item as opposed to having EF do it.
Is this kind of relationship configurable in EF?
The reason I want to do this is for querying filtering purposes as part of complex query that must execute as one statement
Ended up adding a new domain model to represent the LastUpdate which simply holds a primary key to this entity and a FK to the LastUpdate
New Domain Model to represent the Last Update
public virtual SLCurrentWOUpdate LastUpdate { get; set; }
public class SLCurrentWOUpdate
{
[Key]
public int SLWorkOrder_Id { get; set; }
public SLWorkOrder SLWorkOrder { get; set; }
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public SLWOUpdate SLWOUpdate { get; set; }
}
I can query this as part of a larger more complex set of predicates... I just have to reach into the model one reference deeper:
db.SLWorkOrders
.Where(t => t.TAutoDeclined != null && t.TClosedPendingPayment != null)
.Where(t => t.LastUpdate.SLWOUpdate.UpdateStatusType.SystemName == "CHANGE_PRIORITY");
Feels kind of hackish.. but it works..
In my scenario I have Jobs, Companies and Departments.
Single Job may have only one Company; Company may have multiple Jobs (One-To-Many)
Single Job may have multiple Departments; Department may have multiple Jobs (Many-To-Many).
I want to set relations using Foreign Keys only. For that I have property of Foreign Key and lazy navigation property.
These are my classes:
public class JobEntity
{
[Key]
public int Id
{
get;
set;
}
public Companies CompanyId
{
get;
set;
}
//Navigation
[ForeignKey("CompanyId")]
public virtual CompanyEntity Company
{
get;
set;
}
public IList<Departments> Departments
{
get;
set;
}
//navigation
public virtual IList<DepartmentEntity> DepartmentsNavigation
{
get;
set;
}
}
public class DepartmentEntity
{
public Departments Id
{
get;
set;
}
public string Name
{
get;
set;
}
//navigation
public virtual IList<JobEntity> Jobs
{
get;
set;
}
}
public class CompanyEntity
{
public Companies Id
{
get;
set;
}
public string Name
{
get;
set;
}
//navigation
public virtual List<JobEntity> Jobs
{
get;
set;
}
}
Also, I have many-to-many mapping inside my context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<JobEntity>()
.HasMany<DepartmentEntity>(s => s.DepartmentsNavigation)
.WithMany(c => c.JobsNavigation)
.Map(cs =>
{
cs.MapLeftKey("JobId");
cs.MapRightKey("DepartmentId");
cs.ToTable("JobsDepartments");
});
}
When I set CompanyId into Job, everything work as expected: when I getting Job from DB, I have an associated Company lazy loaded.
However, when I setting into Job list of related foreign keys ('Departments') - when I loading Job from DB this list is null and departments navigation property ('DepartmentsNavigation') having count of 0 (I was expected to have collection of department ids that I set + lazy loaded collection of departments).
What I doing wrong?
For many-to-many, you can't use a foreign key association.
There's a reference to this here:
One-to-one relation in EFv4 always uses Foreign Key association and many-to-many relation always uses Independent association.
and here:
Note: In many-to-many (*:*) you cannot add foreign keys to the entities. In a *:* relationship, the association information is managed with an independent object.
and here:
However, if you have a pure many-to-many relationship that is connected by a join table that contains only foreign keys, the EF will use an independent association to manage such many-to-many relationship.
Your foreign keys are already in the JobsDepartments table, so EF won't let you add additional foreign keys for that relationship in the JobEntity and DepartmentEntity entities.
Let's say I have a model where I have the Person entity with general info (Names, Date of Birth, etc.), and two additional entities (Customer, Worker) which inherit from Person. As you see there is the option of having a Customer who CAN ALSO play the role of a Worker in the model. There is a way to design this in EF (I saw something about TPH, TPT and TPC) but I see that there is a use of discriminator which doesn't allow a Person table to include values for Worker and Customer "simultaneously".
I don't know if maybe I'm getting wrong with the general OOP concept of inheritance :S.
Thanks in advance for all your help.
You cant have multiple inheritance in .net, it is not supported (and the same applies to entity framework). You can implement multiple interfaces, but this is a slightly different notion - i.e. 'Worker' could be an interface that is implemented by some objects, such as customer
In entity framework, I believe that the discriminator is only implemented when using Table-per-hierarchy. This is where both child entities are stored in the same table, and the discriminator identifies which is which.
Table-per-type is essentially where the entities (person, customer, worker) are stored in different tables, but are accessible as single entities in your code (i.e. customer with an inheritance from person)
It may be that you need to create an interface (maybe IWorker), and create a class (maybe WorkerCustomer??) that inherits from Customer, and implements IWorker.
EDIT: 15/02/2013 19:00
Ok, so the below might be what you're looking for in terms of representing the data in a single table:
public class MyDbContext : DbContext
{
public MyDbContext() : base("TestDB")
{
}
public DbSet<Person> People { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Worker> Workers { get; set; }
public DbSet<WorkerCustomer> WorkerCustomers { get; set; }
}
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Customer : Person
{
public string CustomerNumber { get; set; }
}
public interface IWorker
{
string WorkerNumber { get; set; }
}
public class Worker : Person, IWorker
{
public string WorkerNumber { get; set; }
}
public class WorkerCustomer : Customer
{
public string WorkerNumber { get; set; }
}