Have and "Address" model used by several other models ("Employee" & "Client").
I would call this a one to one relationship, I could be wrong. The address is required by both of the other models. Remove, deletes only the parent object.
Tried in both EF Core and EF6. Remove deletes the parent object, but not the "Address" object.
public class Address
{
public int AddressID { get; set; }
public string Street { get; set; }
public string CityStateZip { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Address EmployeeAddress { get; set; }
}
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Address ClientAddress { get; set; }
}
No error messages - context.remove simply won't delete the child object. Complete noob here when it comes to EF. Sorry, this is probably a very basic question, but please believe that I have searched extensively. Most solutions suggest a foreign key back to the parent - but, in this case, the child object can be used (but not shared) in several different models.
The same Address object can be used in multiple Employee and/or Client instances as currently implemented.
The suggestion you received
Most solutions suggest a foreign key back to the parent - but, in this case, the child object can be used (but not shared) in several different models.
informs Entity Framework that a given Address can only appear in one specific Employee/Client.
You should be able to resolve this by having Employee and Client inherit from a common base class, e.g.
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[Required]
public virtual Address PersonAddress { get; set; }
}
public class Employee : Person
{
// Other properties that make Employee unique go here
}
public class Client : Person
{
// Other properties that make Client unique go here
}
Then add the backreference to the base class
public class Address
{
public virtual int AddressID { get; set; }
public virtual string Street { get; set; }
public virtual string CityStateZip { get; set; }
[Required]
public virtual Person AddressOf { get; set; }
}
Related
Is there any way to represent something like this :
public abstract class DbEntity
{
[Key]
public string Id { get; set; } // <-- a GUID-String created by the domain
}
public class Carrier : DbEntity
{
public string Name { get; set; } = default!;
public ICollection<Address> Addresses { get; set; }
}
public class Customer : DbEntity
{
public string Name { get; set; } = default!;
public ICollection<Address> Addresses { get; set; }
}
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string? Street { get; set; }
}
Entity Framework will always create the address-table like this :
Id
Street
CarrierId
CustomerId
The problem is, that multiple tables will have an list of addresses (not just two) and I really don't want something like :
Id
Street
Parent1Id
Parent2Id
Parent3Id
Parent4Id
...
I've also tried to set up my entities like this :
_modelBuilder?.Entity<Carrier>()
.HasMany(entity => entity.Addresses);
Which will also create the navigation properties on the child.
I'm also understanding why, because its a many-to-one relationship and the child needs to know his parent.
However I really have no ideas to solve my 'problem'.
I was thinking about creating multiple classes/tables for my addresses like 'CustomerAddress' and 'CarrierAddress' (which inheritance from 'address'), but creating a new address table for every parent.. feels wrong?
Is this really the right way or can I solve this somehow else or is something considered 'good practise' for such cases?
You can use Address class with a generic type, but this will generate multiple tables with different names like this:
Address<Parent1>
Address<Parent2>
Address<Parent3>
If this is ok with you, you can modify Address class as:
public class Address<TParent>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string? Street { get; set; }
public TParent Parent { get; set; }
}
I am having issues trying to map two fields that are foreign keys into the same table. The use case is for a modifier and creator. My class already has the Ids, and then I wanted to add the full User object as virtual.
I am using a base class so that each of my tables have the same audit fields:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
[ForeignKey("CreatedById")]
public virtual User CreatedByUser { get; set; }
[ForeignKey("ModifiedById")]
public virtual User ModifiedByUser { get; set; }
}
The child class is very simple:
public class CircleUserSubscription : Entity
{
[Required]
public long Id { get; set; }
public long SponsorUserId { get; set; }
[ForeignKey("SponsorUserId")]
public virtual User User { get; set; }
public long TestId { get; set; }
[ForeignKey("TestId")]
public virtual User Test { get; set; }
}
This is a standard junction table.
When I try to generate the migration, I am getting errors that I don't understand fully.
Unable to determine the relationship represented by navigation property 'CircleUserSubscription.User' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
I tried what this answer had, but the code is basically the same: https://entityframeworkcore.com/knowledge-base/54418186/ef-core-2-2---two-foreign-keys-to-same-table
An inverse property doesn't make sense since every table will have a reference to the user table.
For reference, here is the User entity:
public class User : Entity
{
public long Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
I am hoping you all can help me out, TIA :)
EDIT: One thing to note, all of this worked fine when the entity class was as follows:
public class Entity
{
public long? ModifiedById { get; set; }
public long CreatedById { get; set; } = 1;
}
It was only after I added the entity that things went awry.
I have an 'User'class contains two 'Address' properties reference to 'Address' entity, and there is another class - 'Shipment' also associate with 'Address'.
How i can use fluent api on ef core to build a correct relation between entities.
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
...
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public virtual Address DefaultAddress { get; set; }
public virtual Address BillingAddress { get; set; }
}
public class Shipment
{
public int ShipmentId { get; set; }
public virtual Address DeliveryAddress { get; set; }
}
There is not need to declare explicit configuration, the EF will do everything without any help.
I prepared a working example with and without fluent configuration, you can check it out here. Just switch between commits to see the difference.
As you can notice, there is no differences in generated migration.
I am new to .NET Core and using EF Core 2
My domain objects are all derived from a base class with some audit fields on it that get set on SaveChanges as needed:
(Simplified below)
public abstract class AuditableEntity
{
public DateTime CreatedOn { get; set; }
[ForeignKey("CreatedBy")]
public Guid? CreatedByWebUserId { get; set; }
public WebUser CreatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
[ForeignKey("UpdatedBy")]
public Guid? UpdatedByWebUserId { get; set; }
public WebUser UpdatedBy { get; set; }
public DateTime? DeletedOn { get; set; }
[ForeignKey("DeletedBy")]
public Guid? DeletedByWebUserId { get; set; }
public WebUser DeletedBy { get; set; }
}
On add-migration, I get the error:
Unable to determine the relationship represented by navigation property
'Address.CreatedBy' of type 'WebUser'. Either manually configure the
relationship, or ignore this property using the '[NotMapped]' attribute or by
using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Address is one of the classes derived from AuditableEntity:
(Simplified below)
public class Address : AuditableEntity
{
public Guid Id { get; set; }
public string Nickname { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public string PostalCode { get; set; }
public string CountryCode { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
}
However, I have several objects that use the same "agent and timestamp" pair pattern similar to the above that work just fine such as:
public DateTime? VerifiedOn { get; set; }
[ForeignKey("VerifiedBy")]
public Guid? VerifiedByWebUserId { get; set; }
public WebUser VerifiedBy { get; set; }
The error always comes from Address, and if I remove the base class from Address everything works fine (meaning, these fields get successfully applied to my 15+ other domain objects).
The issue seemingly stems from WebUser having a reference to Address:
(Simplified below)
public class WebUser : AuditableEntity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public string Phone1 { get; set; }
public string Phone1Type { get; set; }
public string Phone2 { get; set; }
public string Phone2Type { get; set; }
[ForeignKey("AddressId")]
public Address Address { get; set; }
public Guid? AddressId { get; set; }
}
What is the correct way of creating these references prioritizing keeping the FK constraints (over keeping the ability to navigate)?
The problem is unrelated to the usage of a base class (the same will happen if you remove the base class, but copy its properties to Address class), but the multiple cross references between the two classes.
By convention EF Core tries to automatically "pair" navigation properties of the two entities in order to form a single relationship, which succeeds in most of the cases. However in this case the WebUser has Address type navigation property and Address class has WebUser type navigation property (actually 3).
Since all they have associated FK property via ForeignKey data annotation, EF Core should be able to correctly identify them as different one-to-many relationships, but it doesn't. Not only it fails with the exception in question, but also doesn't create FK relationships for the WebUser.
Everything works correctly if the base class contains only 1 WebUser type of navigation property, so I'm assuming thet unfortunately you are hitting some current EF Core bug.
As a workaround until they fixed it, I would suggest explicitly configuring the problematic relationships using fluent API, by overriding the OnModelCreating and adding the following code:
var auditableEntityTypes = modelBuilder.Model.GetEntityTypes().Where(t => t.ClrType.IsSubclassOf(typeof(AuditableEntity)));
var webUserNavigations = new[] { nameof(AuditableEntity.CreatedBy), nameof(AuditableEntity.DeletedBy), nameof(AuditableEntity.UpdatedBy) };
foreach (var entityType in auditableEntityTypes)
{
modelBuilder.Entity(entityType.ClrType, builder =>
{
foreach (var webUserNavigation in webUserNavigations)
builder.HasOne(typeof(WebUser), webUserNavigation).WithMany();
});
}
i.e. for each entity class that derives from AuditableEntity we explicitly configure the 3 WebUser reference navigation properties to be mapped to 3 separate one-to-many relationships with no inverse collection navigation properties. Once we do that, EF Core has no problem to correctly map the WebUser.Address FK association.
I am working on building a single family unit (class) for an application. I've done some searching and found solutions for entire family trees, but this app doesn't care about anything outside the single family unit which is defined as (Father, Mother, Child1, Child+n)
This application is about the children (activities they can do based upon age and skill levels), but needs references to the parents. The parents are only needed for reporting purposes and are required to have driver's license and insurance on file.
The application is being built using C# & EF Code First. None of the database annotation elements have been added to the class yet as that isn't the problem.
Below are my classes. The main business rule state that each sibling will have his/her own record, but they need to be linked together so only one mailing (electronic or snail) is sent, if the parents live together. If the parents are divorced, then two letter (or emails) need to be sent.
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Suffix { get; set; }
public string Sex { get; set; }
public DateTime DOB { get; set; }
}
public class Youth : Person
{
public string CurrentGrade { get; set; }
public Adult Mother { get; set; }
public Adult Father { get; set; }
public Adult ICE { get; set; }
public virtual Adult Adult { get; set; }
}
public class Adult : Person
{
public string DriversLicense { get; set; }
public string StateIssued { get; set; }
public string AutoInsuranceCarrier { get; set; }
public string PolicyNumer { get; set; }
public string MaritalStatus { get; set; }
//foreign key
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
}
public class Address
{
public int AddressId { get; set; }
public int PersonId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public virtual Person Person { get; set; }
}
The logic pattern I was stuck in was that siblings will have the same AddressId. That failed when I applied the divorced parents, each having one child at their address. As far as mailings go, it would work because they are at different addresses. It doesn't feel like the best design. If this were handled by UI, then it would work.
My next thought was to create a Family class and add each family member to it. In this instance, the user would have to make the selection of which people would be living at which address.
public class Family
{
public int FamilyId { get; set; }
public int AddressId { get; set; }
public List<Person> Person;
}
That also doesn't seem like the best solution. I feel there is a better design. I just cannot find it on my own.
Are there any pitfalls to one of these approaches that I'm not seeing yet?
Can someone point me in a better direction? And explain why that direction is better?
Thanks in advance for all your insights!
The "Father", "Youth", "Brother", etc... is not attribute of the person, but attribute of relationship between persons. One person can be both "Father" and "Brother" and "Uncle".
Better design is something like this (i don't know all your requirements):
public class Person {
public Name{get;set;}
// etc...
public List<Relationship> Relationships{get;set;}
}
public class Relationship {
public Person P1{get;set;}
public Person P2{get;set;}
public RelationshipKind Kind{get;set;}
}
public class RelationshipKind {
// for example: Father
public Name1 {get;set;}
// for example: Child
public Name2 {get;set;}
}
Typically you want to keep your models flowing as you would think about them.
For instance, I would not have an Address class that contains Person. I would have an Address class containing only base data about the Address. In Person, I would have an Address. This fits with the "Person lives at this address" and will likely fix your who-lives-where issue. This is the type of setup you have for "Vehicle"
Without going into details regarding whole design, I will focus only on the address issue.
I recommend that you have your address class defined in a way that it does not have navigation property back to a person:
public class Address
{
public int AddressId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Then I would link your Adult (or even Person) class to Address like so:
public class Adult : Person
{
public string DriversLicense { get; set; }
public string StateIssued { get; set; }
public string AutoInsuranceCarrier { get; set; }
public string PolicyNumer { get; set; }
public string MaritalStatus { get; set; }
//foreign key
public int VehicleId { get; set; }
public virtual Vehicle Vehicle { get; set; }
public int AddressId {get; set;}
public virtual Address Address { get; set; }
}
With this approach, if parents are not divorced, they can share the same address instance. In case they are divorced, your application has to set different address for one of the parents. In case you need to find people living under same address, you can do sth like this:
_dbContext.Persons.Where(p => p.AddressId = addressId) ...
If you have a table of States then you can use that in a lot of places: DriversLicenseState, AddressState, InsuranceState etc. If this isn't a blank string then you don't have to validate the user input and can just put a drop-down on the application.
String current grade. If you instead enter the year the child entered first grade then you don't have to manually update this every year; it can be calculated - though you also have to include an int for NumberOfYearsHeldBack. But its less work to update that int once in a great while, than update every student every year.
Personal Preference: I like 'NameLast' and 'NameFirst' rather than 'FirstName' & 'LastName' just because it means these properties will bunch together alphabetically in all the IDE drop downs etc, just like AddressID, Address1 and Address2
You might consider making 'Mother' into a nullable 'Mother?' and the same with 'Father' to 'Father?'. Some kids simply don't have both parents. If you want to be politically correct you might consider 'Parent1?' and 'Parent2' and put an enum on the relationship to select mother, father, guardian because some kids have 2 moms or 2 dads or their older brother is their legal guardian.
In your address class why is there a person property when you already have a PersonID property? For that matter why does the address have a personID at all? This only needs to be one-way. A person has an address, but an address doesn't have a person. This way you can have 5 people all with the same address. If you try to keep both synchronized two-way it will get fouled up, not to mention you have to have a List<> of personID inside the address. Since address is a type, add another one to each person so you have an AddressPhysical and AddressMailing. Now you can have 4 kids each with different places they sleep but they all get their mail at grandma's P.O.Box.
I wouldn't add a third adult to the child as their ICE. Instead I would make a List<> of adults (its just going to be a list of ID ints really) That way you can keep working down the list in an emergency until you reach someone.