ef modelBuilder force foreignkey a or foreignkey b - c#

OK.
Background.
I was initially trying to make EF models along the lines of:
public class Person
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public Guid PhoneID { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
}
public class Org
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public Guid PhoneID { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
}
public class Phone
{
[Key]
public Guid ID { get; set; }
public string Number { get; set; }
public Guid EntityID { get; set; }
[ForeignKey("EntityID")]
public virtual User User { get; set; }
[ForeignKey("EntityID")]
public virtual Org Org { get; set; }
}
But I now (mostly) realize that this causes an issue with Foreign Key relationship integrity in SQL Server. So to correct this, I altered the Phone class to:
public class Phone
{
[Key]
public Guid ID { get; set; }
public string Number { get; set; }
public Guid? PersonID { get; set; }
public Guid? OrgID { get; set; }
[ForeignKey("PersonID")]
public virtual User User { get; set; }
[ForeignKey("OrgID")]
public virtual Org Org { get; set; }
}
Question.
How can I enforce / map a rule using modelbuilder / Fluent API to ensure a Phone object has either a PersonID or an OrgID?
Edit:
I do realise that this is creating a data integrity rule that I would be unable to do if I was designing the database in SQL Server, but to me it seems that EF has the potential flexibility to take database design to the next level.
I see EF (Code-First especially) as Microsoft's next big leap in their software development strategy. IMHO this is as big a leap as the introduction of .Net (Now there is a statement that should generate some debate!), that being moving the database design away from the database itself & integrating it in with the managed code.

Related

Having a hard time understanding how cascade delete works and how to configure relationships

So, I have the dependent entity ArtItem, with parent class Location, which is a dependent entity of Company, which is a dependent entity of Country. ArtItem is also dependent on Artist and Artist is dependent on Country.
public class Country
{
[Required]
public int Id { get; set; }
}
public class Company
{
public Country Country { get; set; }
[Required]
public int CountryId { get; set; }
[Required]
public int Id { get; set; }
}
public class Location
{
[Required]
public int Id { get; set; }
public Company Company { get; set; }
public int CompanyId { get; set; }
//public List<ArtItem> ArtItems { get; set; }
}
public class ArtItem
{
public Artist Artist { get; set; }
public int ArtistId { get; set; }
[Required]
public int Id { get; set; }
public Location Location { get; set; }
[Required]
public int LocationId { get; set; }
}
public class Artist
{
public Country Country { get; set; }
[Required]
public int CountryId { get; set; }
[Required]
public int Id { get; set; }
}
My issue is that whenever I try to do update-database in powershell, I get the error Introducing FOREIGN KEY constraint 'FK_ArtItems_Locations_LocationId' on table 'ArtItems' may cause cycles or multiple cascade paths..
I tried configuring the relationship in my DbContext class,
.
but to no avail. I simply don't understand how I'm supposed to configure relationships in EF Core, or how modelBuilder works. I would highly appreciate an explanation on how to avoid this error and what actually causes it. I thought that having a navigation property and a foreign key defined would be enough for EF Core to properly generate the relationships. I tried the online documentation for EF Core on relationships to no avail.
Help is much appreciated.
Thanks in advance.

Avoiding 'Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths'

I reproduced a simple example of a problem I have with Entity Framework.
I want to have three tables:
Users, Projects, WorkOrders
Table Users has information about users for all other tables (in example only two). WorkOrders has information about which User has to work on this work order and to which Project it belongs.
Here are the classes:
public class User
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
public class Project
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public DateTime Start { get; set; }
public virtual User Manager { get; set; }
public virtual ICollection<WorkOrder> WorkOrders { get; set; }
}
public class WorkOrder
{
[Key]
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User Assignee { get; set; }
}
When I try to run the program, it throws an exception:
'Introducing FOREIGN KEY constraint 'FK_dbo.WorkOrders_dbo.Projects_ProjectId' on table 'WorkOrders' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Then I went another way. I tried DB-first approach with EF. I first created the tables and connections in SQL Server Management Studio:
Then the generated models by EF look almost the same as mine, with code-first approach.
public partial class User
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public User()
{
this.Project = new HashSet<Project>();
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Project> Project { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class Project
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Project()
{
this.WorkOrder = new HashSet<WorkOrder>();
}
public int Id { get; set; }
public string Name { get; set; }
public int ManagerId { get; set; }
public System.DateTime Start { get; set; }
public virtual User User { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<WorkOrder> WorkOrder { get; set; }
}
public partial class WorkOrder
{
public int Id { get; set; }
public int Number { get; set; }
public string Type { get; set; }
public int AssigneeId { get; set; }
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public virtual User User { get; set; }
}
So the code is almost identical, except the SuppressMesages and constructors in WorkOrder and User classes. The second approach works.
I would like to know, what makes the difference? Also the context class is identical as my own. Where or how are defined this FK constraints or cascade delete settings?
It is unfortunate default behavior of EF code first to crate FK with on delete cascade. So while defining relations you need to simply change this setting:
//in context
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Project>()
.HasRequired<User>(s => s.User)
.WithMany()
.WillCascadeOnDelete(false);
Also there might be a convention for it in modelBuilder.Conventions if you want to just change behavior for all FK.

"Clue" like relational database design: Schema must enforce entity multiplicity across related entities.

I have a business case that requires as much as possible to implement a database schema for the application which prevents an incorrect state for an entity called Trailers to be in more than one location entity, called Bays or Lots.
The code snippets for relevant parts of the entity models are below.
Essentially, my problem is I cannot figure out how to create a schema that will prevent a trailer from being in a lot, and in a Bay at the same time, while also keeping the 1 to many relationship from lots to trailers and 1 to 1 for bays trailers.
I can obviously do things to mitigate this problem in the application, but having a schema that prevents this state is better.
Anyone have any thoughts on how this might be best accomplished?
public class Trailer
{
public int TrailerId { get; set; }
public MaintStatuses MaintStatus { get; set; }
....
}
public class Location
{
[Key]
public string LocationName { get; set; }
//Navigation
public virtual ICollection<Bay> LocationBays { get; set; }
public virtual Lot ParkingLot { get; set; }
...
}
public class Bay
{
[Key]
public int Id { get; set; }
public string BayName { get; set; }
//Navigation
public virtual Trailer Trailer { get; set; }
public virtual Location Location { get; set; }
...
}
public class Lot
{
[Key]
public string LotName { get; set; }
[ForeignKey("Location")]
public string LocationName { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<Trailer> ParkedTrailers{get; set;}
...
}

EF relation one-to-two

I'm having some troubles when saving in the database a model with a little complex relationship.
The UML of the classes is:
The classes definition are:
public abstract class EntityBase
{
public virtual Guid Id { get; set; }
}
public class LoanRequest : EntityBase
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual Applicant Applicant1 { get; set; }
public virtual Applicant Applicant2 { get; set; }
}
public class Applicant
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid LoanRequestId { get; set; }
[ForeignKey("LoanRequestId")]
public virtual LoanRequest LoanRequest { get; set; }
public virtual ICollection<MonthlyIncome> Incomes { get; set; }
}
public class MonthlyIncome
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid ApplicantId { get; set; }
[ForeignKey("ApplicantId")]
public virtual Applicant Applicant { get; set; }
}
I'm able to run a migration and looking into the database the tables and columns created by the framework seems fine to me. But, when saving an exception happens. The exception is:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
I've been searching for a solution on the internet and I can't see where my problem is. Any suggestions? Thanks!
After several tryings I was able to find a solution. Changing the applicant definition to:
public class Applicant
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public virtual ICollection<MonthlyIncome> Incomes { get; set; }
}
was all I needed

Creating relationships in entity with out foreign keys

I am using the code first approach, to create the database based on modals and the db context class. The problem is that when I create the relationships between one model and the next and run the code the data base generates foreign keys like it should.
How ever I want the relationships, with out the foreign keys. is this possible to do with entity framework and the code first approach?
for example:
namespace LocApp.Models
{
public class Location
{
public int id { get; set; }
[Required]
public string name { get; set; }
[Required]
public string address { get; set; }
[Required]
public string city { get; set; }
[Required]
public string country { get; set; }
[Required]
public string province { get; set; }
[Required]
public string phone { get; set; }
public string fax { get; set; }
public bool active { get; set; }
public virtual ICollection<LocationAssignment> LocationAssignment { get; set; }
}
}
Has a relationship with:
namespace LocApp.Models
{
public class LocationAssignment
{
public int id { get; set; }
public int locationID { get; set; }
public int serviceID { get; set; }
public int productID { get; set; }
public int personID { get; set; }
public virtual Location Location { get; set; }
public virtual Service Service { get; set; }
public virtual Product product { get; set; }
public virtual Person person { get; set; }
}
}
As you can see this table will have a foreign key generated with the location table. How do I keep this relationship WITH OUT the generation of foreign keys?
User Triggers On Insert and on update and delete relative and reference tables !!! but that's not cute you must control allthing in your application instead

Categories