Code First database design issue - addresses - asp.net

I have a POCO that represents a form a user fills out. There are about 10 different addresses on the form, so I separated them out into a separate address POCO. The problem is how to set up the relationships.
What I would like to be able to do:
Public Class formtofillout
Public Property form_id As Integer
<ForeignKey("address_1")> _
Public Property address_1_id As Integer
<ForeignKey("address_2")> _
Public Property address_2_id As Integer
<ForeignKey("address_3")> _
Public Property address_3_id As Integer
<ForeignKey("address_4")> _
Public Property address_4_id As Integer
<ForeignKey("address_5")> _
Public Property address_5_id As Integer
<ForeignKey("address_6")> _
Public Property address_6_id As Integer
<ForeignKey("address_7")> _
Public Property address_7_id As Integer
<ForeignKey("address_8")> _
Public Property address_8_id As Integer
<ForeignKey("address_9")> _
Public Property address_9_id As Integer
<ForeignKey("address_10")> _
Public Property address_10_id As Integer
Public Overridable Property address_1 As address
Public Overridable Property address_2 As address
Public Overridable Property address_3 As address
Public Overridable Property address_4 As address
Public Overridable Property address_5 As address
Public Overridable Property address_6 As address
Public Overridable Property address_7 As address
Public Overridable Property address_8 As address
Public Overridable Property address_9 As address
Public Overridable Property address_10 As address
End Class
And not sure what to do on the address POCO to represent the other end of this relationship. BUT... this gives me a series of errors:
This one:
Introducing FOREIGN KEY constraint 'FK_dbo.formtofillout_dbo.address_1_id' on table
'formtofillout' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION
or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
And I think a few others.
In order to get this to work, I did not create specific id columns in the 'formtofillout' POCO. Instead I just created the navigation properties:
Public Class formtofillout
Public Property form_id As Integer
Public Overridable Property address_1 As address
Public Overridable Property address_2 As address
Public Overridable Property address_3 As address
Public Overridable Property address_4 As address
Public Overridable Property address_5 As address
Public Overridable Property address_6 As address
Public Overridable Property address_7 As address
Public Overridable Property address_8 As address
Public Overridable Property address_9 As address
Public Overridable Property address_10 As address
End Class
And then in the OnModelBuilding event, I added the following for each address:
modelBuilder.Entity(Of formtofillout).HasOptional(Function(n) n.address_1).WithRequired().WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasOptional(Of address)(Function(n) n.address_1).WithMany().Map(Function(m) m.MapKey("address_1_id"))
This worked, and when I performed the migration, the table was created with the address_1_id, address_2_id columns, etc.... But these ID columns are in the database, but not in the POCO. How do I set this up to get the foreign key column in the POCO and the table?

You will need to add cascadeOnDelete(false) to the foreign key definition.
With multiple references back to one table or a->b->c->a type scenarios EF cant deal with that:
eg
entity.HasRequired(p=>p.SomeNavProp)
.WithMany()
// .HasForeignKey(p=>p.RefId)
.WillCascadeOnDelete(false);
Dont forget the Virtual Nav property definition. Didnt see that in your code extract

OK, figured this out. Here is my class:
Public Class formtofillout
Public Property form_id As Integer
Public Property address_1_id As Integer
Public Property address_2_id As Integer
Public Property address_3_id As Integer
Public Property address_4_id As Integer
Public Property address_5_id As Integer
Public Property address_6_id As Integer
Public Property address_7_id As Integer
Public Property address_8_id As Integer
Public Property address_9_id As Integer
Public Property address_10_id As Integer
Public Overridable Property address_1 As address
Public Overridable Property address_2 As address
Public Overridable Property address_3 As address
Public Overridable Property address_4 As address
Public Overridable Property address_5 As address
Public Overridable Property address_6 As address
Public Overridable Property address_7 As address
Public Overridable Property address_8 As address
Public Overridable Property address_9 As address
Public Overridable Property address_10 As address
End Class
And here is the OnModelCreating in the DbContext, I did this:
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_1).WithMany().HasForeignKey(Function(a) a.address_1_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_2).WithMany().HasForeignKey(Function(a) a.address_2_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_3).WithMany().HasForeignKey(Function(a) a.address_3_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_4).WithMany().HasForeignKey(Function(a) a.address_4_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_5).WithMany().HasForeignKey(Function(a) a.address_5_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_6).WithMany().HasForeignKey(Function(a) a.address_6_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_7).WithMany().HasForeignKey(Function(a) a.address_7_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_8).WithMany().HasForeignKey(Function(a) a.address_8_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_9).WithMany().HasForeignKey(Function(a) a.address_9_id).WillCascadeOnDelete(False)
modelBuilder.Entity(Of formtofillout).HasRequired(Function(n) n.address_10).WithMany().HasForeignKey(Function(a) a.address_10_id).WillCascadeOnDelete(False)

Related

EntityFramework 6 - Transient.collection Exception

I have the below Database-First model:
Imports System
Imports System.Collections.Generic
Partial Public Class User
Public Property ID As Integer
Public Property Username As String
Public Property Password As String
Public Property IsDisabled As Boolean
Public Property IsSuper As Boolean
Public Property MustChangePassword As Boolean
Public Property Notes As String
Public Overridable Property UserPermissions As ICollection(Of UserPermission) = New HashSet(Of UserPermission)
End Class
And the DB Context
Partial Public Class Entities
Inherits DbContext
Public Sub New()
MyBase.New("name=Entities")
End Sub
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
Throw New UnintentionalCodeFirstException()
End Sub
.
.
.
Public Overridable Property Users() As DbSet(Of User)
.
.
.
End Class
Whenever I run the below query:
dim ent = New Entities()
result = (From u In ent.Users
Where u.ID = ID
Select u).SingleOrDefault()
I get the following error:
'Users' is not a member of 'Transient.collection[Conso_Model.Entity(Nullable=True,DefaultValue=)]'. To extract a property of a collection element, use a sub-query to iterate over the collection. Near escaped identifier, line 1, column 12.
Any clue?
Found the problem!
There is another EF class named Entity with pluralized collection name 'Entities' (similar to Database Context class)!
How stupid VB compiler didn't fire any warning about this!

Assigning one Table ID to another Table ID Like Asp.Net Identity AspNetUserRole

Hi i have 3 model class setup Group
Public Class Group
Public Property Id As Long
Public Property Name As String
Public Property CoverImgUrl As String
Public Overridable ReadOnly Property Profile() As ICollection(Of Profile)
Get
End Get
End Property
End Class
Profile
Public Class Profile
Public Property Id As Long
<Required>
<MaxLength(20)>
<Display(Name:="First Name")>
Public Property FirstName As String
<Required>
<MaxLength(20)>
<Display(Name:="Last Name")>
Public Property LastName As String
Public Overridable ReadOnly Property Group() As ICollection(Of Group)
Get
End Get
End Property
and GroupMembers.
Public Class GroupMembers
Public Sub New()
End Sub
Public Overridable Property Group() As Group
Public Overridable Property GroupId() As String
Public Overridable Property Profile() As Profile
Public Overridable Property ProfileId() As String
End Class
i ran the Update-Database command which updated my database and created a table ProfileGroup like asp.net AspNetUserRole.
With the progress so far how can i assign one group to a profile i created like asp.net uses the UserManager.AddToRole method to assign a user to a role or any global best practice to this. Thanks
It looks like you're just trying to set up a many-to-many relationship between Profile and Group. However, you're going about it all wrong.
First, traditionally, you don't actually define a class for the intermediary table, GroupMembers in this case, unless you need a payload, i.e. additional data on the relationship. In your posted code, there's no payload on the relationship, so you're just making your life more difficult this way. Assuming you wanted to keep things as they are, though, you would need to set up your classes like:
Public Class Group
...
Public Overridable Property Profiles As ICollection(Of GroupMember)
End Class
Public Class Profile
...
Public Overridable Property Groups As ICollection(Of GroupMember)
End Class
Public Class GroupMember
Public Property GroupId As String
Public Overridable Property Group As Group
Public Property ProfileId As String
Public Overridable Property Profile As Profile
End Class
This will then be enough for Entity Framework to discern the relationships and create the appropriate tables with the appropriate foreign keys. However, you would be forced to access the Profile/Group indirectly, i.e. profile.Groups.First().Group. Whereas, with an implicit join table, you could just do profile.Groups.First().
So, in order to have an implicit join table, you would just need the following classes:
Public Class Group
...
Public Overridable Property Profiles As ICollection(Of Profile)
End Class
Public Class Profile
...
Public Overridable Property Groups As ICollection(Of Group)
End Class
Notice that the GroupMember class goes away completely. Entity Framework will still see this as a many-to-many and create a table, most likely named dbo.GroupProfiles. If you're not happy with the table name, you can declare it using fluent configuration:
modelBuilder.Entity(Of Profile)().
HasMany(Function(t) t.Groups).
WithMany(Function(t) t.Profiles).
Map(Sub(m)
m.ToTable("GroupMembers")
End Sub)

ASP.NET MVC Navigational Property

I'm looking to add a navigational property to my 'Registration' class, so I can access the parent 'Person'.
Ideally, I would be able to add Public Overrideable Property ParentPerson as Person, but I can't seem to get this to work as it complains about the principle end of association.
Public Class Person
<Required()>
Public Property ID As Integer
<Required()>
<StringLength(150)>
Public Property Firstname As String
<Required()>
<StringLength(150)>
Public Property Lastname As String
Public Overridable Property Registration As Registration
End Class
Public Class Registration
<Required()>
Public Property ID As Integer
Public Property RegistrationDate As Date
Public Overridable Property Sessions As List(Of RegistrationSession)
End Class

Entity Framework - Insert a many-to-many relationship from a FormView

I have two entities linked with an association entity. In code I can easily insert one, or all of these at once. But, on the front end, I am using FormViews to allow the user to enter data. When I insert an address, how do I let it know to insert a facility at the same time, unless I have the facility and the address on the same formview?
Public Class facility
<Key()> _
Public Property facility_id As Integer
Public Property facility_present_use As String
Public Property facility_prior_use As String
Public Property facility_occupied As Boolean
Public Property facility_size As Double
Public Property facility_floors As Integer
Public Property facility_age As Integer
Public Property facility_single_residence As Boolean
Public Property facility_number_of_units As Integer
Public Overridable Property facility_address As ICollection(Of facility_address) = New HashSet(Of facility_address)
End Class
Public Class address
<Key()> _
Public Property address_id As Integer
Public Property address_name As String
Public Property address_address_1 As String
Public Property address_address_2 As String
Public Property address_city As String
Public Property address_state As Integer?
Public Property address_zip As String
Public Property address_contact_first_name As String
Public Property address_contact_last_name As String
Public Property address_phone_area As String
Public Property address_phone As String
Public Overridable Property lu_state As lu_state
Public Overridable Property facility_address As ICollection(Of facility_address) = New HashSet(Of facility_address)
End Class
Public Class facility_address
<Key()> _
Public Property facility_address_id As Integer
Public Property address_type As Integer
Public Property facility_id As Integer
Public Property address_id As Integer
Public Overridable Property lu_address_type As lu_address_type
Public Overridable Property facility As facility
Public Overridable Property address As address
End Class

Updating just a subset of fields in a row

I'm learning asp.net MVC. Using the Code First model, where you create your class, and then use the EF to manage all database interactions.
Is there anyway of only requesting some of the fields from a given table using this method, and only updating those fields?
My whole class is:
Public Class Employee
Public Property ID() As Integer
<DisplayName("Staff Name")>
<Required()>
Public Property StaffName() As String
<DisplayName("Location")>
<Required()>
Public Property Location() As String
<Required()>
<DisplayName("Team Leader")>
Public Property teamleader() As String
Public Property ScoreCardM1() As Integer
Public Property ScoreCardM1Notes() As String
Public Property ScoreCardM2() As Integer
Public Property ScoreCardM2Notes() As String
Public Property ScoreCardM3() As Integer
Public Property ScoreCardM3Notes() As String
Public Property ScoreCardM4() As Integer
Public Property ScoreCardM4Notes() As String
Public Property ScoreCardM5() As Integer
Public Property ScoreCardM5Notes() As String
Public Property ScoreCardM6() As Integer
Public Property ScoreCardM6Notes() As String
Public Property ScoreCardTotal() As Integer
Public Property ScoreCardTotalNotes() As String
Public Property ScoreCardM7() As Integer
Public Property ScoreCardM7Notes() As String
Public Property ScoreCardM8() As Integer
Public Property ScoreCardM8Notes() As String
Public Property ScoreCardM9() As Integer
Public Property ScoreCardM9Notes() As String
Public Property ScoreCardQ3Total() As Integer
Public Property ScoreCardQ3TotalNotes() As String
Public Property ScoreCardM10() As Integer
Public Property ScoreCardM10Notes() As String
Public Property ScoreCardM11() As Integer
Public Property ScoreCardM11Notes() As String
Public Property ScoreCardM12() As Integer
Public Property ScoreCardM12Notes() As String
Public Property ScoreCardQ4Total() As Integer
Public Property ScoreCardQ4TotalNotes() As String
Public Property GeneralNotes() As String
End Class
However, I only want to display and edit one month at a time:
Public Class Employee
Public Property ID() As Integer
<DisplayName("Staff Name")>
<Required()>
Public Property StaffName() As String
Public Property ScoreCardM1() As Integer
Public Property ScoreCardM1Notes() As String
End Class
How do I best achieve this? Do I setup another 12 classes, with just one month contained within each, or is there a best practice way of updating a subset of fields within a database row, without affecting the others?
Thanks for any help,
Mark
further to my comment above.... you can set up your classes as follow:
Employee Class:
Public Class Employee
Public Property EmployeeID() As Integer
<DisplayName("Staff Name")>
<Required()>
Public Property StaffName() As String
<DisplayName("Location")>
<Required()>
Public Property Location() As String
<Required()>
<DisplayName("Team Leader")>
Public Property teamleader() As String
Public virtual Property reports() As ICollection<Of MonthlyRports> // not sure if the syntax is right (haven't worked in VB)
End Class
MonthlyReport Class
Public Class Employee
Public Property MonthlyReportID() As Integer // primary key
Public Property EmployeeID() As Integer // foreign key. who this report belongs to
Public Property Month As String // report for month ...
Public Property ScoreCard() As Integer
Public Property ScoreCardNotes() As String
End Class

Resources