Three models in to one maintenance form - c#

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.

Related

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.

Using Data annotations to create navigation property from class to itself?

Using Entity Framework Code first I have a class that holds data for a drop-down list. The same class holds records that are sub-items for the items in the main list. Ultimately this will create a cascading set of drop-down lists.
I am trying to figure out how to make the navigation property for the class link back to itself. The issue class is the one that I am using to populate the drop-down list. The Complaint class also has a link to the Issues class but does not need a link back to the subcategory.
public class Issue
{
public Issue()
{
Complaints = new List<Complaint>();
SubIssues = new List<Issue>();
}
[Key]
public int IssueID { get; set; }
public string Name { get; set; }
public bool IsSubCategory { get; set; }
[ForeignKey("IssueID")]
public ICollection<Issue> SubIssues { get; set; }
public virtual ICollection<Complaint> Complaints { get; set; }
}
public class Complaint
{
public Complaint()
{
}
public int ComplaintID { get; set; }
public string Name {get; set;}
[ForeignKey("IssueID")]
public virtual Issue Issue { get; set; }
}
I did something similar, but actually did only have a parent reference in the children. Either way this should work.
public class Folder
{
[Key]
public int Id { get; set; }
// Some Property
public string Name { get; set; }
// They foreignkey for Many-side
public virtual Folder Parent { get; set; }
// The list for One-side (Not tested in my application)
public virtual ICollection<Folder> SubFolders { get; set; }
}
It is same as a regular one-to-many relation, just all the references are within same entity.

Entity Framework 6 multiple table to one foreign key relationship code first

I am wondering if anyone could advise me on how to accomplish the below using code first in EF6
If I add the Table_3 as a List on to Table_1 & Table_2 in my entities. EF automatically generates a foreign key column for both tables in Table_3 instead of recognizing that they are of the same type.
My model classes are set as follows.
public interface IParent
{
int ID { get; set; }
List<Table_3> Children { get; set; }
}
public class Table_1 : IParent
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public virtual List<Table_3> Children { get; set; }
}
public class Table_2 : IParent
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public virtual List<Table_3> Children { get; set; }
}
public class Table_3
{
[Key]
public int ID { get; set; }
public int ParentID { get; set; }
[ForeignKey("ParentID")]
public virtual IParent Parent { get; set; }
}
EF code first generates the below
Edit
Just to let anyone having the same problems know
I have now resolved this by changing the IParent interface to an abstract class
my classes now look like the following
[Table("ParentBase")]
public abstract class ParentBase
{
[Key]
public int ID { get; set; }
public List<Table_3> Children { get; set; }
}
[Table("Table_1")]
public class Table_1 : ParentBase
{
public string Name { get; set; }
}
[Table("Table_2")]
public class Table_2 : ParentBase
{
public string Name { get; set; }
}
[Table("Table_3")]
public class Table_3
{
[Key]
public int ID { get; set; }
public int ParentID { get; set; }
[ForeignKey("ParentID")]
public virtual ParentBase Parent { get; set; }
}
with a table arrangement of
this will work although it would have been nicer if the original could have been met.
I had this problem too, and I used abstract class instead of interface from the beginning.
The problem for mine was my table_3 have two navigation properties:
one is public virtual Table_1, another is public virtual Table_2, and then EF just provisioned these extra foreign key columns,
I merged the two navigation properties into one to
public virtual parentbase {get;set;}. And then it worked. Hope this helps.
Side Note,Would suggest to add virtual keyword on public List Children { get; set; } in parentbase class, because in your previous example , it was already like that.
Thanks for posting this, i came across this issue too.
You can also do like the following where you make a 1 to many relationship between Table_1 and Table_2 with Table_3 respectively:
modelBuilder.Entity<Table_3>().HasOptional(/*Nav Prop*/).WithMany(m => m.Table_3s).HasForeignKey(f => f.ParentId).WillCascadeOnDelete(false);
modelBuilder.Entity<Table_3>().HasOptional(/*Nav Prop*/).WithMany(m => m.Table_3s).HasForeignKey(f => f.ParentId).WillCascadeOnDelete(false);
Let me know if anymore clarification is required.

C# Entity Framework and Business Logic

I have a database and I'm accessing it via EF.
public partial class Project
{
public int ProjectID { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectAssets> ProjectAssets { get; set; }
}
public partial class ProjectAssets
{
public int MappingID { get; set; }
public int ProjectID { get; set; }
public int AssetID { get; set; }
public virtual Project Project { get; set; }
public virtual Asset Asset { get; set; }
}
public partial class Asset
{
public int AssetID { get; set; }
public string Name { get; set; }
public short Type { get; set; }
public virtual ICollection<ProjectAssets> ProjectAssets { get; set; }
}
So, my program have only 1 active Project in the time.
I want to be able to bind to Project and display as a tree or some other way all Assets and I want to be able to create new Asset or add existing Asset what belongs to other project.
If I will use the generated entities I would not be able to manage that all so I need some rules. I've already found good impl. of repositories, but still don't know how to create rules.
Do I need to create something like:
public class WorkProject : Project
{
public WorkProject(Project projject){...}
WorkAsset CreateAsset(){...}
void AddAsset(Asset asset){...}
}
As far as I understand your question, you want to control the graph of entities which are related to a project object. You can leave the repository layer intact and put another layer (business layer) on top of it to enforce the rules.

In ASP.NET MVC how to use another model as property

So I have two model, one is Company, one is Province.
[Table("Company")]
public class Company {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public int ProvinceID { get; set; }
public ProvinceModel Province{
get {
// ????
}
}
}
public class CompanyContext : MyXsiteContext {
public DbSet<Company> Companies { get; set; }
}
Here is my Province:
[Table("Province")]
public class ProvinceModel {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
}
public class ProvinceContext : MyXsiteContext {
public DbSet<ProvinceModel> Provinces { get; set; }
}
How do I get my Company, which only save the ProvinceID, to reference the Province object so I can refer to the province.name in my view?
It seems like you want to do it similar to how the navigation properties are described here.
So in the link they describe Course -> Department but for you it is Company -> Province.
Also, as an aside, if you are going to reference Province.Name in your view you might run into a Select N+1 problem so that might be something to account for (depending on your specific use case, which I'm not 100% across, just highlighting it as a potential "thing")

Categories