This question is purely conceptual! The code below works fine, but I can't figure out how . . .
The Scenario
I've begun reading up on Entity Framework concepts, and I'm currently using the information and examples located here to build my first MVC project. Here's the code example from that link:
Imports System.Collections.Generic
Imports System.Data.Entity
Namespace MyDataAccessDemo
Module Program
Sub Main()
Using context As New ProductContext()
Dim food As New Category With {.CategoryId = "FOOD"}
context.Categories.Add(food)
Dim cheese As New Product With {.Name = "Cheese"}
cheese.Category = context.Categories.Find("FOOD")
context.Products.Add(cheese)
context.SaveChanges()
End Using
End Sub
End Module
Public Class ProductContext : Inherits DbContext
Public Property Products As DbSet(Of Product)
Public Property Categories As DbSet(Of Category)
End Class
Public Class Product
Public Property ProductId As Integer
Public Property Name As String
Public Property Category As Category ' <-- Circular reference?
End Class
Public Class Category
Public Property CategoryId As String
Public Property Name As String
Public Property Products As ICollection(Of Product) ' <-- Circular reference?
End Class
End Namespace
The Problem
So, "Category" is a class and "Product" is a class. Category contains a collection of its products, each product of which contains its category, which contains a collection of its products, each product of which contains its category, which contains a collection of products . . . well, you get the idea.
The Question
Why does this work? Shouldn't this cause some kind of circular reference? I would think the category would contain the product IDs and vice versa, not the objects themselves.
Those are called Navigation Properties, you can find out more information here on the Entity framework website.
Related
I've an old programm with edmx. Inside this one, I've linked a class (Table) To a View (Table/filter on a value of a column)
I want migrate this project to code first.
I copy/paste the project delete edmx file and generate models from an existing database.
All is good except this link.
<Table("JoinAffectation")>
partial public Class JointAffectation
public property Id as Long
public IdRecherche as Integer 'the link with my view
<NotMapped>
<ForeignKey("Id")>
PUBLIC OVERRIDABLE PROperty RechercheJoint as ViewRechercheJoint
But When I try to use function of automatical sort/filter using expression
I've error : The specified type member 'RechercheJoint' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
If I removed I error saying I don't same comumn and property... Also , How Can I stipulate RechercheJoint is mapped on IdRecherche
thanks for your help
Finally Using modelbuilder, I can join my view and my table like in edmx
<Table("JointAffectation")>
Partial Public Class JointAffectation
Public Property Id As Long
Public Property IdTypeJoint As Long
Public Property IdRecherche As Integer
Public Overridable Property JointType As JointType
<ForeignKey("Id")>
Public Overridable Property RechercheJoint As ViewRechercheJoint
End Class
<Table("ViewRechercheJoint")>
Partial Public Class ViewRechercheJoint
<Key>
<DatabaseGenerated(DatabaseGeneratedOption.None)>
Public Property Id As Integer
<StringLength(50)>
Public Property Libelle As String
<ForeignKey("IdRecherche")>
Public Overridable Property JointAffectations As ICollection(Of JointAffectation)
End Class
modelBuilder.Entity(Of JointAffectation)() _
.HasRequired(Function(e) e.RechercheJoint) _
.WithMany(Function(e) e.JointAffectations) _
.HasForeignKey(Function(e) e.IdRecherche)
The Situation-
Have EF database entities with collections of other database entities as navigation properties all of which are not exposed outside of the project. Each of these entities are nested in base classes that allow public read and protected set access to the Db entity's property values and are the classes the business layer will work with. So these base classes also need collections of their Db entity's navigation properties.
What I have now-
I am still working on how I want the data layer to interact with with the other layers that reference it. So I have been experimenting with creating collection classes that can be set equal to a collection of Db entities and create a collection of the base class that the Db entity is nested in. The collection is then exposed outside the class though an interface as well as the base class. The interface gives access to some custom methods of collection class like accepting DTOs and prebuilt quires but would hide any Add methods.
The Problem-
I would like to create a generic base class for the collections but cant seem to find a way. I have gotten close to something workable but it was ugly and confusing. Below is an outline of what I'm working with. The narrowing operators in the entity class work well. I would rather have widening operators and narrowing operators in the base class so I could keep the New method private for the base classes but I don't want to expose the actual database entities outside the Data Layer.
Interops Namespace referenced by all projects in solution-
Public Interface IGenericEntity
End Interface
Public Interface INavigationPropertyEntity : Inherits IGenericEntity
End Interface
Public Interface IDbEntityToExposedEntityCollection(Of TEntity As IGenericEntity)
'Used to hide Add method
End Interface
Public Interface IPublicEntity
ReadOnly Property NavProps As IDbEntityToExposedEntityCollection(Of INavigationPropertyEntity)
End Interface
Data Layer project-
-Database Entities
Friend Class DbEntity1
Public Shared Narrowing Operator CType(ByVal value As DbEntity1) As NavigationPropertyEntity
Return New NavigationPropertyEntity(value)
End Operator
End Class
Friend Class DbEntity2
Public Sub New()
NavigationPropertyDbEntities = New HashSet(Of DbEntity1)
End Sub
Public Property NavigationPropertyDbEntities As ICollection(Of DbEntity1)
Public Shared Narrowing Operator CType(ByVal value As DbEntity2) As PublicEntity
Return New PublicEntity(value)
End Operator
End Class
-Exposed Base Classes
Public Class NavigationPropertyEntity : Implements INavigationPropertyEntity
Private _value As DbEntity1
Friend Sub New(value As DbEntity1)
_value = value
End Sub
End Class
Public Class PublicEntity : Implements IPublicEntity
Dim _value As DbEntity2
Friend _NavProps As DbEntityToPublicEntityCollection(Of INavigationPropertyEntity, DbEntity1, NavigationPropertyEntity)
Public ReadOnly Property NavProps As IDbEntityToExposedEntityCollection(Of INavigationPropertyEntity) Implements IPublicEntity.NavProps
Get
Return _NavProps
End Get
End Property
Friend Sub New(value As DbEntity2)
_value = value
_NavProps = new DbEntityToPublicEntityCollection(Of INavigationPropertyEntity, DbEntity1, NavigationPropertyEntity)(_value.NavigationPropertyDbEntities)
End Sub
End Class
-Entity Specific Collection
Friend Class DbEntityToPublicEntityCollection(Of IEnt As INavigationPropertyEntity, DbEnt As DbEntity1, ExpEnt As NavigationPropertyEntity)
Inherits HashSet(Of IEnt)
Implements IDbEntityToExposedEntityCollection(Of IEnt)
Public Sub New(value As HashSet(Of DbEnt))
For Each ent In value
Dim exp As NavigationPropertyEntity = ent
Dim i As INavigationPropertyEntity = exp
MyBase.Add(i)
Next
End Sub
End Class
Additional Info Edit
The main issue is being able to have a generic collection declared something like,
Friend Class DbEntityToPublicEntityCollection(Of IEnt, DbEnt as class, ExpEnt As class)
because in the New sub ExpEnt cannot be converted to DbEnt and a TryCast will just result in a runtime error.
Like I said, I tried setting up classes that are inherited by the exposed and entity classes so a new method with a parameter for the exposed class could be set up but with trying to have the interfaces in there it got unmanageable pretty quick. Then again I have not worked much with making my own generic types before so I'm not sure if this is even a proper strategy.
I have some custom classes and collection classes for those classes. I'm kind of new to actually implementing all of this, and I see a lot of different options but all with their own pros and cons, and I'm having trouble determining the best/right way to do what I need.
For example, I have a Product class with ID and Description properties. I have a ProductCollection class that I basically want to be a dictionary consisting of Product objects. This is a dictionary because there is a performance gain in accessing by key value, which is how I'll reference the objects. The key is Product.ID, and the value is Product.
I'd like to be able to do something like ProductCollection.Add(Product), and the collection will handle signing the dictionary key from the object's ID property. How do I best accomplish this? Implement Dictionary(Of Integer, Product) and override basically all methods to pass in the object and property separately? Or is there a better way?
Also, to give a little more background information to help clarify usage of these classes, there will be a "master" instance of ProductCollection containing all possible products. A ShipTo class will also have a ProductCollection containing specific products that are applicable for that particular ShipTo. My question was specific to the Product/ProductCollection classes, but also applied to the ShipTo classes.
This is how I would hold a collection of products. I have a Product class and a Products collection class.
First create the Product class which holds all the properties:
Imports System.Collections.ObjectModel
Public Class Product
Public Key As String
Public Sub New(ByVal id As Integer,
ByVal description As String)
_id = id
_description = description
End Sub
Private _id As Integer
Public ReadOnly Property ID() As Integer
Get
Return _id
End Get
End Property
Private _description As String
Public ReadOnly Property Description() As String
Get
Return _description
End Get
End Property
End Class
The create a Products collection class to hold the Product class:
Public Class Products
Inherits KeyedCollection(Of String, Product)
Protected Overrides Function GetKeyForItem(ByVal item As Product) As String
Return item.Key
End Function
End Class
You would then use these like this:
Dim myProducts As New Products
myProducts.Add(New Product(1,"Table"))
I have been struggling with the new ASP.NET Identity trying to extend it to do my next web application, I have opend a new empty web project in Visual Studio 2013 Profissional, and added MVC 5 assembly as usual.
I have write the following in VB.NET (Code-First)
Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema
Imports Microsoft.AspNet.Identity.EntityFramework
Imports System.Data.Entity
Partial Public Class MyAppContext
Inherits IdentityDbContext(Of User)
Public Sub New()
MyBase.New("ConnStr")
End Sub
Public Overridable Property Items As DbSet(Of Item)
End Class
Partial Public Class User
Inherits IdentityUser
<Required>
Public Property Name As String
Public Property Items As ICollection(Of Item) = New HashSet(Of Item)
End Class
Partial Public Class Item
<Key> <DatabaseGenerated(DatabaseGeneratedOption.Identity)>
Public Property ID As Integer
<Required>
Public Property Name As String
<Required>
Public Property UserID As String
<ForeignKey("UserID")>
Public Overridable Property User As User
End Class
I think the code is very straight-forward, i am trying to create "User" class that inherit from "IdentityUser" and adding a one-to-many relationship with another class called "Item", and of course I have to extend the generic class "IdentityDbContext(Of User)".
Then I want to add a controller with the views for my "User" and "Item" classes using the scaffolding option "MVC 5 Controller with views, using Entity Framework".
Error There was an error running the selected code generator: 'Value cannot be null. Parameter name: codeType'
I want you to know that this is a clean setup for visual studio 2013, a clean project, and also the scaffolding works very good for the current model without the Identity inheritance thing.
Please let me know if you have successfully reproduce the problem on your machines.
I wanted to let you know that after i have converted the exact code to C# it worked!...
It seems that this is a vb.net related problem (something with the built-in t4 templetes).
So this is a bug, and i will assume this is the answer.
If somebody found a workaround for this, please post and I will accept it as the answer.
i'm creating a new way to do the things in my project and i need some help in some point.
I have 3 projects in my solution:
Buusiness, Provider and Model.
The MODEL project, is the one that i have the classes just like the tables in database:
Ex:
public class Person
property Id as integert?
property Name as string
property CPF as string
end class
the PROVIDER project, is the one that makes the communication with database:
public class PersonProvider
public function ListPerson(filter as model.person) as list(of model.person)
public sub insertPerson(byRef person as model.peson)
public sub updatePerson(byRef person as model.peson)
public sub deletePerson(byRef person as model.peson)
end class
and finally, i have the BUSINESS project, that's the project that exposes the CRUD methods to the website (the website must not have access to the PROVIDER, because we have business logic in the BUSINESS)
public class PersonBusiness
public function ListPerson(filter as model.person) as list(of model.person)
return (new provider.PersonProvider).listPerson(filter)
end public
public sub InsertPerson(byRef person as model.person)
dim provider as new provider.PersonProvider()
provider.insertPerson(person)
end public
...
end class
But, i have come to a problem when i need some inner joins like this:
table CUSTOMER have an id_person, so in model.Customer i have a property IdPerson as integer?
but i want to have the properties Name and CPF that the model.Person have, read only property in the model.Customer, so i've made:
Property IdPerson As Integer?
Private _person As Pessoa
Private ReadOnly Property Person As model.Person
Get
If IsNothing(_person) Then
_person = New provider.PersonProvider.ListPerson(new model.Person with {.Id = Me.IdPerson})
End If
Return _person
End Get
End Property
ReadOnly Property Name As String
Get
Return Me.Person.Name
End Get
End Property
ReadOnly Property CPF As String
Get
Return Me.Pessoa.CPF
End Get
End Property
and here comes the question... i found that i can't have reference like:
Website reference Model and Business
Provider reference Model
Business reference Model and Provider
but to do the readonly Properties the Model needs reference to Provider, and that causes a circle reference..visual studio doesn't allow me to do this...
any idea how to do this?
Sorry that my post become so long, i just wanted to make things clear.
Instead of accessing Model by Website and Business, you could add another class that references Provider and Person, and this new class will referenced by Website and Business (instead of Website and Business referencing Person directly).