Modelling two one-to-many relationships as one - c#

I am not sure what the name for the following concept is, which is problematic as I am trying to implement it in my Entity Framework domain model (my reputation is not high enough to post images, even though it would be useful!):
Permit -- has many --> OfficePermit -- has many --> Task
Essentially this is a one-to-many relationship between Permit and Task and I would like to represent it with a collection navigation property ICollection<Task> Tasks on my Permit domain model.
I am not interested in implementing this in a ViewModel layer as an extra property since I am relying on metadata generated by entity framework for use on the client side (using Breeze.js). How would I go about representing this relationship in my domain model - is it even possible?

You can do it like this...
public class Permit
{
public Permit()
{
this.PermitId = Guid.NewGuid().ToString();
}
public string PermitId { get; set; }
public ICollection<OfficePermit> OfficePermits { get; set; }
}
public class OfficePermit
{
public OfficePermit()
{
this.OfficePermitId = Guid.NewGuid().ToString();
}
public string OfficePermitId { get; set; }
public string PermitId { get; set; }
public Permit Permit { get; set; }
public ICollection<#Task> Tasks { get; set; }
}
public class #Task
{
public Task()
{
this.TaskId = Guid.NewGuid().ToString();
}
public string TaskId { get; set; }
public OfficePermit OfficePermit { get; set; }
public string OfficePermitId { get; set; }
}

Related

Entity framework core load navigation properties for recursive table

I am using Entity Framework Core 5.0 and the following model:
So a Job has a MainFlow, and the mainflow can have 0 or more Subflows which in turn can have subflows (recursive)
The way this has been setup is that I have a Job entity which has a MainflowId property (and also navigation property)
The Flows have a ParentFlowId property, and a navigation property with a collection of SubFlows.
I also included a TreeId property on Job and Flow to easily identify a tree.
public class Job
{
public int Id { get; set; }
public DateTimeOffset Timestamp {get; set; }
public string JobName{ get; set; }
public int MainFlowId { get; set; }
public Guid TreeId { get; set; }
public virtual Flow MainFlow { get; set; }
}
public class Flow
{
public int Id { get;set; }
public string Name { get; set; }
public string DisplayName { get; set; }
public Guid TreeId { get; set; }
public int? ParentFlowId { get; set; }
public virtual Flow ParentFlow { get; set; }
public virtual ICollection<Flow> SubFlows { get; set; } = new List<Flow>();
}
What I am trying to achieve is to load a list of jobs (for example based upon the timestamp), but in a way that all the navigation properties become available (Job->Mainflow->Subflow->Subflow->...)
I was able to get this behavior by loading the jobs, and then loading the flows separately using the TreeId, however because of performance issues I am now using .AsNoTracking() and this does not seem to work anymore.
Is there a way to load the whole trees with their navigation properties while using .AsNoTracking?
Thanks for any insight!
Edit:
Here is the way it is working without the AsNoTracking()
(I simplified the code a bit)
private IQueryable<Job> GetAllJobs()
{
return DbContext.Set<Job>()
.Include(a=>a.MainFlow)
}
private IEnumerable<Flow> GetAllFlowsForTreeIds(IEnumerable<Guid> treeIds)
{
var result = from flow in DbContext.Set<Flow>()
.Include(a => a.ParentFlow)
.AsEnumerable()
join treeId in treeIds
on flow.TreeId equals treeId
select flow;
return result;
}
public IEnumerable GetJobTrees()
{
var allJobs = GetAllJobs().ToList();
var flows = GetAllFlowsForTreeIds(allJobs.Select(a=>a.TreeId)).ToList());
//by getting the flows, the navigation properties in alljobs become available
}

EF Core: Circular entity reference

I would say that it takes a lot of time to get to know ASP.NET Core to understand how to achieve things then previous versions with webforms, but I understand that ASP.NET Core is bigger and you are able to build more complex solutions.
I'm quite new to ASP.NET Core and I'm trying to understand EF Core and related data. I'm using https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro to learn the basics and create my first ASP.NET Core application.
I have a Entity "Standard" that can have multiple Forms (Form entity). The entities share a couple of same properties so I've made them both inherit from a master class called MasterDocument. Previously called Document.
Standard:
namespace Skjemabasen.Models.Document
{
public class Standard : MasterDocument
{
[Display(Name = "Kategori")]
public virtual Category Category { get; set; }
[Display(Name = "Dokumenter")]
public ICollection<Form> Forms { get; set; }
}
}
Form:
public class Form : MasterDocument
{
public Format Format { get; set; }
public virtual Member Assignee { get; set; }
public String Status { get; set; }
[ForeignKey("Standard")]
public int StandardId { get; set; }
public Standard Standard { get; set; }
public ICollection<Customer.Subscription> Subscribers { get; set; }
}
MasterDocument:
namespace Skjemabasen.Models.Document
{
public class MasterDocument : IDocument
{
public int ID { get; set; }
[Required]
[Display(Name = "EStandard")]
[StringLength(50)]
public string EStandard { get; set; }
[Required]
[Column("Betegnelse")]
[Display(Name = "Betegnelse")]
[StringLength(60)]
public string Betegnelse { get; set; }
[Display(Name = "Kommentar")]
public string Comment { get; set; }
}
}
I understand that this can cause circular request or circular deletion so I inserted a DeleteBehavior.Restrict on Standard:
modelBuilder.Entity<Standard>()
.HasOne(d => d.Forms)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
My complete context class:
namespace Skjemabasen.Data
{
public class SkjemabasenContext : DbContext
{
public SkjemabasenContext(DbContextOptions<SkjemabasenContext> options) :base(options)
{
}
public DbSet<Member> Members { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Standard> Standards { get; set; }
public DbSet<Form> Forms { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Revision> Revisions { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<MasterDocument> Documents { get; set; }
public IQueryable<Customer> CurrentCustomers
{
get { return Customers.Where(c => c.Inactive == false); }
}
public IQueryable<Customer> InActiveCustomers
{
get { return Customers.Where(c => c.Inactive == true); }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().ToTable("Member");
modelBuilder.Entity<Category>().ToTable("Category");
modelBuilder.Entity<Standard>().ToTable("Standard");
modelBuilder.Entity<Form>().ToTable("Form");
modelBuilder.Entity<Customer>().ToTable("Customer");
modelBuilder.Entity<Revision>().ToTable("Revision");
modelBuilder.Entity<Subscription>().ToTable("Subscription");
modelBuilder.Entity<MasterDocument>().ToTable("Document");
modelBuilder.Entity<Standard>()
.HasOne(d => d.Forms)
.WithMany()
.OnDelete(DeleteBehavior.Restrict);
}
}
}
When I try to run the application I get the error:
System.ArgumentException: 'The entity type
'System.Collections.Generic.ICollection`1[Skjemabasen.Models.Document.Form]'
provided for the argument 'clrType' must be a reference type.' Because
all Forms must have a parent Standard and both 'Standard' and 'Form'
inherits from MasterDocument, I understand that ASP.NET Core warns
about circular deletion, but I'm not sure how to achieve this. The
error says something about ICollection of 'Forms' not being a
reference type. Is something missing in 'Standard' related to the
relation between and 'Form'.
Based on https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro I can't figure out what I'm missing here.
I'm assuming you don't actually want to have polymorphic entities by inheriting from MasterDocument. So, from what I see, you want Form and Standard to share the same properties of MasterDocument while MasterDocument being itself an Entity. If that's the case, just abstract away those properties to a base class:
public abstract class MasterBaseDocument
{
public int ID { get; set; }
[Required]
[Display(Name = "EStandard")]
[StringLength(50)]
public string EStandard { get; set; }
[Required]
[Column("Betegnelse")]
[Display(Name = "Betegnelse")]
[StringLength(60)]
public string Betegnelse { get; set; }
[Display(Name = "Kommentar")]
public string Comment { get; set; }
}
public class Form : MasterBaseDocument
{
...
}
public class Standard : MasterBaseDocument
{
...
}
public class MasterDocument : MasterBaseDocument
{
// right now, empty here...
}
That should fix it.
Another approach to your model would be to have a MasterDocument FK on Forms and Standard. That way you don't get the duplicates fields on the tables.
Further improving: Also, keep in mind that you can achieve all those configurations you have using attributes with FluentAPI. This way your classes are keep and decouple from EF stuff. That just adds noise and makes it very hard to read. Should be examples on Fluent API on EF docs as well.

EF6.1 Code First Mapping or Annotations for Tables

I have the following two models that I'm trying to setup, but cannot figure out how to do it using data annotations or fluent API. Can anyone advise either the proper annotations or Fluent API code?
public class Vehicle
{
public int ID { get; set; }
public int JobID { get; set; }
public int StatusID { get; set; }
public string Name { get; set; }
public virtual Job Job { get; set }
public virtual Status Status { get; set; }
}
public class Job
{
public int ID { get; set; }
public int VehicleID { get; set; }
public int StatusID { get; set; }
public string Description { get; set; }
public virtual Vehicle Vehicle{ get; set; }
public virtual Status Status { get; set; }
}
The problem I'm having is the reference to the Job model from Vehicle, and from Vehicle to Job. In the database, the Jobs tables holds all jobs pending or completed. There may or may not be a vehicle associated with it (when a job is in progress or complete, a vehicle will be associated with it). For the vehicles table, the JobID represents the current job assigned to the vehicle (if it's assigned a job) and will constantly change through the day, but should have no effect on the Jobs table.
The following snippet outlines a fluent code-first example of the relationship you describe:
public class Vehicle
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Job Job { get; set; }
}
public class Job
{
public int ID { get; set; }
public string Description { get; set; }
}
public class AppDataContext : DbContext
{
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<Job> Jobs { get; set; }
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Job>().HasKey(x => x.ID);
mb.Entity<Vehicle>().HasKey(x => x.ID);
mb.Entity<Vehicle>().HasOptional(x => x.Job).WithOptionalDependent();
// ... other config, constraints, etc
}
}
And to get Jobs associated with vehicles:
using (var context = new AppDataContext())
{
var query = context.Vehicles.Where(x => x.Job != null).Select(x => x.Job);
// ...
}
IMHO, the explicit foreign key properties should be kept out of the model unless strictly needed. This tends to make life easier and keeps the code clean.
Hope this is along the lines of what you're looking for...
P.S. - At the risk of 'just posting links' - If you already have a database (as implied in your question), it might be worth considering a Database First approach: http://msdn.microsoft.com/en-gb/data/jj206878.aspx ..or Code First with an existing database: http://msdn.microsoft.com/en-us/data/jj200620

Three models in to one maintenance form

I want to build the one maintenance form for the following three models -
namespace mysite.Models
{
public class LevelOne
{
public int LevelOneId { get; set; }
public string Name { get; set; }
public virtual ICollection<LevelTwo> LevelTwos { get; set; }
}
}
namespace mysite.Models
{
public class LevelTwo
{
public int LevelTwoId { get; set; }
public string Name { get; set; }
public int LevelOneId { get; set; }
public virtual LevelOne LevelOne { get; set; }
public virtual ICollection<LevelThree> LevelThrees { get; set; }
}
}
namespace mysite.Models
{
public class LevelThree
{
public int LevelThreeId { get; set; }
public string Name { get; set; }
public int LevelTwoId { get; set; }
public virtual LevelTwo LevelTwo { get; set; }
}
}
The relationship is :-
LevelOne can have multiple LevelTwo's.
LevelTwo's will have a LevelOne and multiple LevelThree's.
LevelThree will have a LevelTwo.
I have cascading ddl's to select based on the relationship above.
What would be the best way to implement a maintenance form so i can add/edit/delete a level based on what is chosen in the ddl's?
Is it a case of a lot of divs being hidden and shown in javascript based on the ddl's or can something simpler be done using viewmodels or some fancy nancy .net extensions etc?
My mind hurts thinking about it :(
This could also be seen as just 1 model with 3 entities.
Level1 -> Level2 is a one to many relationship. Level2 -> Level3 is a
one to many relationship.
EF supports this. And as i clearly see from ur namespace for all
the 3 models is the same mysite.Models, i guess u already have all of
them in the same model file for EF to create the relationships.

How to wrap a key with another object in entity framework / code first

I just started prototyping our existing object model in entity framework/code first and hit my first snag. Unfortunately the documentation for this seems to be very scarce.
My key is not a primitive but an object that wraps a primitive. How do I get this to work with EF/Code first:
public class EFCategoryIdentity
{
public string IdentityValue { get; private set; }
public EFCategoryIdentity(string value)
{
IdentityValue = value;
}
}
public class EFCategory
{
[Key]
public EFCategoryIdentity CategoryIdentity { get; set; }
public string Name { get; set; }
public virtual ICollection<EFProduct> Products { get; set; }
}
public class EFProduct
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public virtual EFCategory Category { get; set; }
}
What do I need to put here to make this work?
public class MyTestContext : DbContext
{
public DbSet<EFCategory> Categories { get; set; }
public DbSet<EFProduct> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EFCategory>()
.// Help!
}
}
Thanks!
Entity framework can use only primitive types as keys. Every time you wrap some mapped property to separate type you are creating complex type. Complex types have some limitation
They cannot be keys
They cannot contain keys
They cannot contain navigation properties
etc.

Categories