I've read How Should I Declare Foreign Key Relationships Using Code First Entity Framework (4.1) in MVC3? but I can't get my call to yield any results. It has the following error:
The ForeignKeyAttribute on property 'Footer_Item_Header_ID' on type
'MyBlog.Tbl_Footer_Item' is not valid. The navigation property
'Tbl_Footer_Header' was not found on the dependent type
'MyBlog.Tbl_Footer_Item'. The Name value should be a valid navigation
property name.
On this line:
Dim footerNavElements = db.Tbl_Footer_Headers.Where(Function(i) i.Footer_Header_Order = 1).Single.Items
Here is my parent model:
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations
Public Class Tbl_Footer_Header
<Key()> Public Property Footer_Header_ID() As Integer
Public Property Footer_Header_Content() As String
Public Property Footer_Header_Order() As Integer
Public Overridable Property Items As ICollection(Of Tbl_Footer_Item)
End Class
Public Class FooterHeaderDbContext
Inherits DbContext
Public Property Tbl_Footer_Headers As DbSet(Of Tbl_Footer_Header)
End Class
Here is my child model:
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations
Public Class Tbl_Footer_Item
<Key()> Public Property Footer_Item_ID() As Integer
<ForeignKey("Tbl_Footer_Header")>
Public Property Footer_Item_Header_ID() As Integer
Public Property Footer_Item_Content() As String
Public Property Footer_Item_Link() As String
Public Property Footer_Header_Order() As Integer
Public Overridable Property Header As Tbl_Footer_Header
End Class
Public Class FooterItemDbContext
Inherits DbContext
Public Property Tbl_Footer_Items As DbSet(Of Tbl_Footer_Item)
Public Property Tbl_Footer_Headers As DbSet(Of Tbl_Footer_Header)
End Class
What can I do to make the action yield a result without error? Thanks.
Your foreign key is annotation should be for the name of the property, not the type.
Your navigation property is Header:
Public Overridable Property Header As Tbl_Footer_Header
So your annotation, should reference the property. Change it to:
<ForeignKey("Header")>
Public Property Footer_Item_Header_ID() As Integer
Related
I have the following scenario:
public class Stay
{
[Contained]
public Guest PrimaryGuest {get;set;}
}
public abstract class Guest
{
public int ID {get; set;}
}
public class EntityGuest : Guest
{
public string EntityName {get;set;}
}
public class PersonGuest : Guest
{
public string SurName {get;set;}
public string GivenName {get;set;}
}
When querying for the stays, I wish to order by a PersonGuest/SurName.
I know how to order by a child property: [URL]/Stays?$expand=PrimaryGuest&$orderby=PrimaryGuest/ID - but how would I order by on a child property that is derived? Is it even possible? I could not determine it by the OData documentation - it wasn't at least called out for contained entities.
This answer helped me a lot in a similar scenario: oData $expand on Derived Types
Basically you can 'Cast' any complex or entity typed property in your query by adding a forward slash and the qualified name of the model type, using the namespace you have defined for your model, not the .Net full type name.
[URL]/Stays?$expand=PrimaryGuest&$orderby=PrimaryGuest/ModelNamespace.PersonGuest/Surname
If you are unsure of the model namespace, look at the model builder code, or use something similar to this:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "MyAppModel";
Then your URL should look like this:
[URL]/Stays?$expand=PrimaryGuest&$orderby=PrimaryGuest/MyAppModel.PersonGuest/Surname
I can not figure out what I'm doing wrong here. New to MVC and new to Entity, so I know that's holding me back. Any time I call up AuthUser, AuthRole is always nothing, so I end up doing something like:
authuser.AuthRole = db.AuthRoleSet.Find(2) 'AuthRoleID of 2 = User
This just feels clunky to me. How do I get my property to actually get the role with the user?
Here's my class structure:
Public Class AuthUser
'Class Globals
Dim db As New AuthUserContext
'Properties
Public Property AuthUserID() As Integer
<Required()> _
<Display(Name:="User Name")> _
<DomainUserValidation()> _
Public Property UserName() As String
<Display(Name:="Current Role")> _
Public Property AuthRole As AuthRole
End Class
Public Class AuthRole
Public Property AuthRoleID() As Integer
<Required()> _
<Display(Name:="Role Name")> _
Public Property RoleName() As String
<Required()> _
<Display(Name:="Is Administrator")> _
Public Property isAdministrator() As Boolean
<Required()> _
<Display(Name:="Is Active")> _
Public Property isActive() As Boolean
<Required()> _
Public Property AuthUser As ICollection(Of AuthUser)
End Class
Public Class AuthUserContext
Inherits DbContext
Public Property AuthUserSet() As DbSet(Of AuthUser)
Public Property AuthRoleSet() As DbSet(Of AuthRole)
End Class
You have 2 options (sorry c# syntax):
1 - Lazy load AuthRole when you need it - for this, your AuthRole property needs to be declared as virtual
public virtual AuthRole {get;set;}
Now, when/if you try to access AuthRole, EF will get it from database.
For this to work you need to have DbContext.Configuration.LazyLoadEnabled = true
Another alternative is to eager load it by using a query like this:
var myUserWithRole = myContext.AuthUsers.Include("AuthRole").FirstOrDefault(x=>x.Id == userId);
This will get the user and the role from the database.
I have a partial view that will not render the correct values for a model. Both of the lines below fail to render the correct value. It's as though the view won't refresh.
#Html.HiddenFor(Function(model) model.customOptionID)
<input type="text" id="customOptionID" value="#Model.customOptionID" />
I checked the modelstate before the controller returns the model and it is valid. I also verified the value was present.
I stepped through the partial view and it also shows the value is present.
I read this blog post but it doesn't seem to apply since the modelstate is valid.
Here is the controller code:
'GET: /Item/_editCustomItemChoice
Public Function _editCustomItemChoice(ByVal customOptionID As Integer) As ActionResult
Dim customOption = db.customOptions.Find(customOptionID)
If IsNothing(customOption.customItemChoice) Then
customOption.customItemChoice = New customItemChoice
customOption.customItemChoice.customOptionID = customOptionID
customOption.customItemChoice.customOption = customOption
End If
Return PartialView("_editCustomItemChoice", customOption.customItemChoice)
End Function
and the model:
Public Class customOption
Public Property customOptionID As Integer
<Required>
Public Property Title As String
<Required>
Public Property customType As String
Public Property customItemChoiceID
Public Overridable Property customItemChoice As customItemChoice
End Class
Public Class customItemChoice
Public Property customItemChoiceID As Integer
Public Property choices As String
Public Property customOptionID As Integer
<Required>
Public Overridable Property customOption As customOption
End Class
Check below link for #Html.HiddenFor extension method.
http://msdn.microsoft.com/en-us/library/ee703631%28v=vs.118%29.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
The essence of my question is how to compose these objects (see below) in a sensible way with MVC3 and Ninject (though I am not sure DI should be playing a role in the solution). I can't disclose the real details of my project but here is an approximation which illustrates the issue/question. Answers in either VB or C# are appreciated!
I have several different products with widely varying properties yet all of them need to be represented in a catalog. Each product class has a corresponding table in my database. A catalog entry has a handful of properties specific to being a catalog entry and consequently have their own table. I have defined an interface for the catalog entries with the intent that calling the DescriptionText property will give me very different results based on the underlying concrete type.
Public Class Clothing
Property Identity as Int64
Property AvailableSizes As List(Of String)
Property AvailableColor As List(Of String)
End Class
Public Class Fasteners
Property Identity as Int64
Property AvailableSizes As List(Of String)
Property AvailableFinishes As List(Of String)
Property IsMetric As Boolean
End Class
Public Interface ICatalogEntry
Property ProductId as Int64
Property PublishedOn As DateTime
Property DescriptionText As String
End Interface
Given that the DescriptionText is a presentation layer concern I don't want to implement the ICatalogEntry interface in my product classes. Instead I want to delegate that to some kind of formatter.
Public Interface ICatalogEntryFormatter
Property DescriptionText As String
End Interface
Public Class ClothingCatalogEntryFormatter
Implements ICatalogEntryFormatter
Property DescriptionText As String
End Class
Public Class FastenerCatalogEntryFormatter
Implements ICatalogEntryFormatter
Property DescriptionText As String
End Class
In a controller somewhere there will be code like this:
Dim entries As List(Of ICatalogEntry)
= catalogService.CurrentCatalog(DateTime.Now)
In a view somewhere there will be code like this:
<ul>
#For Each entry As ICatalogEntry In Model.Catalog
#<li>#entry.DescriptionText</li>
Next
</ul>
So the question is what do the constructors look like? How to set it up so the appropriate objects are instantiated in the right places. Seems like generics or maybe DI can help with this but I seem to be having a mental block. The only idea I've come up with is to add a ProductType property to ICatalogEntry and then implement a factory like this:
Public Class CatalogEntryFactory
Public Function Create(catEntry as ICatalogEntry) As ICatalogEntry
Select Case catEntry.ProductType
Case "Clothing"
Dim clothingProduct = clothingService.Get(catEntry.ProductId)
Dim clothingEntry = New ClothingCatalogEntry(clothingProduct)
Return result
Case "Fastener"
Dim fastenerProduct = fastenerService.Get(catEntry.ProductId)
Dim fastenerEntry = New FastenerCatalogEntry(fastenerProduct)
fastenerEntry.Formatter = New FastenerCatalogEntryFormatter
Return fastenerEntry
...
End Function
End Class
Public ClothingCatalogEntry
Public Sub New (product As ClothingProduct)
Me.Formatter = New ClothingCatalogEntryFormatter(product)
End Sub
Property DescriptionText As String
Get
Return Me.Formatter.DescriptionText
End Get
End Property
End Class
...FastenerCatalogEntry is omitted but you get the idea...
Public Class CatalogService
Public Function CurrentCatalog(currentDate as DateTime)
Dim theCatalog As List(Of ICatalogEntry)
= Me.repository.GetCatalog(currentDate)
Dim theResult As New List(Of ICatalogEntry)
For Each entry As ICataLogEntry In theCatalog
theResult.Add(factory.Create(entry))
Next
Return theResult
End Function
End Class
IMHO, I am not really getting any smells off this code other than having to change the factory for every new product class that comes along. Yet, my gut says that this is the old way of doing things and nowadays DI and/or generics can do this better. Suggestions on how to handle this are much appreciated (as are suggestions on a better title...)
I like to just use the default constructor on models for the view and populate them via Automapper.
I would have a view model like this:
public interface IHasDescription
{
public string DescriptionText { get; set; }
}
public class ViewModelType : IHasDescription
{
[DisplayName("This will be rendered in the view")]
public string SomeText { get; set; }
public string DescriptionText { get; set; }
}
And I have a model from the DAL like this:
public class DALModelType
{
public string SomeText { get; set; }
}
So you have something like this in your controller:
var dalModel = someRepository.GetAll();
var viewModel = Mapper.Map<DALModelType, ViewModelType>(dalModel);
And you have the Automapper setup code in some file. This way you only have the conversion code in one place instead of in multiple methods/controllers. You have a custom resolver which uses dependency injection (instead of () => new CustomResolver()) and this will house your logic for getting the display text.
Mapper.CreateMap<IHasDescription, ViewModelType>()
.ForMember(dest => dest.DescriptionText,
opt => opt.ResolveUsing<CustomResolver>().ConstructedBy(() => new CustomResolver()));
Not sure if this works with your workflow but it should be able to get you what you want.
So making a few small changes I got this to work using the Ninject Factory extension.
Biggest change is that my entities have enough info to display either type (clothes or fasteners in my contrived example) if the item is actually clothes then the fastener specific properties will be null and vice versa.
Public Interface IDescribable
ReadOnly Property DescriptionText As String
End Interface
Public Enum ProductType
CLOTHING
FASTENER
End Enum
Public Interface ICatalogEntry
Inherits IDescribable
ReadOnly Property ProductId As Int64
ReadOnly Property PublishedOn As DateTime
ReadOnly Property ProductType As ProductType
End Interface
Public Class CatalogEntryEntity
Public Property ProductId As Long
Public Property ProductType As ProductType
Public Property PublishedOn As Date
Public Property DescriptionText As String
Public Property Color As String
Public Property Finish As String
Public Property IsMetric As Boolean
End Class
Then with this in place I can define my catalog service as follows:
Public Class CatalogService
Private ReadOnly _factory As ICatalogEntryFactory
Private ReadOnly _repository As CatalogRepository
Public Sub New(entryFactory As ICatalogEntryFactory, repository As CatalogRepository)
Me._factory = entryFactory
Me._repository = repository
End Sub
Public Function CurrentCatalog(currentDate As DateTime) As List(Of ICatalogEntry)
Dim items = Me._repository.GetCatalog()
Return (From item In items Select _factory.Create(item.ProductType.ToString(), item)).ToList()
End Function
End Class
Public Interface ICatalogEntryFactory
Function Create(bindingName As String, entity As CatalogEntryEntity) As ICatalogEntry
End Interface
Ninject will provide the factory (which is awesome!) assuming I setup the bindings like this:
theKernel.Bind(Of ICatalogEntry)().To(Of ClothingCatalogEntry)().Named("CLOTHING")
theKernel.Bind(Of ICatalogEntry)().To(Of FastenerCatalogEntry)().Named("FASTENER")
theKernel.Bind(Of ICatalogEntryFactory)().ToFactory(Function() New UseFirstParameterAsNameInstanceProvider())
I've omitted the FastenerCatalogEntry for brevity; the ClothingCatalogEntry is like this:
Public Class ClothingCatalogEntry
Public Sub New(ByVal entity As CatalogEntryEntity)
...
It was this post that helped me the most to figure this out. I used UseFirstParameterAsNameInstanceProvider exactly as shown there.
I've this controller
public class AdminController : Controller
{
private IAdministratorService _administratorService;
public AdminController(IAdministratorService administratorService)
{
_administratorService = administratorService;
}
}
And I've this:
private ModelStateDictionary _modelState;
public AdministratorService(IRepository repository, ModelStateDictionary modelState)
{
_repository = repository;
_modelState = modelState;
}
I've configured Dependency Injection for the Controllers so it would load properly except for sending the ModelState from the Container. How do you do it?
Here is one way to handle this problem...
Controller...
Public Class AdminController
Inherits System.Web.Mvc.Controller
Private _adminService as IAdminService
Public Sub New(adminService as IAdminService)
_adminService = adminService
'Initialize the services that use validation...
_adminService.Initialize(New ModelStateWrapper(Me.ModelState))
End Sub
...
End Class
Service...
Public Class AdminService
Implements IAdminService
Private _repository As IAdminRepository
Private _dictionary as IValidationDictionary
Public Sub New(repository as IAdminRepository)
_repository = repository
End Sub
Public Sub Initialize(dictionary As IValidationDictionary) Implements IAdminService.Initialize
_dictionary = dictionary
End Sub
...
End Class
Wrapper Interface...
Public Interface IValidationDictionary
ReadOnly Property IsValid() As Boolean
Sub AddError(Key as String, errorMessage as String)
End Interface
Wrapper implementation...
Public Class ModelStateWrapper
Implements IValidationDictionary
Private _modelState as ModelStateDictionary
Public ReadOnly Property IsValid() As Boolean Implements IValidationDictionary.IsValid
Get
Return _modelState.IsValid
End Get
End Property
Public Sub New(modelState as ModelStateDictionary)
_modelState = modelState
End Sub
Public Sub AddError(key as string, errorMessage as string) Implements IValidationDictionary.AddError
_modelState.AddModelError(key, errorMessage)
End Class
The use of the ModelStateWrapper allows the service classes to be loosely coupled with MVC. Although, we do have a tight coupling between the AdminController and the ModelStateWrapper because of the New statement, but I don't really care because the model state is MVC specific anyway. By doing this, you would not need to register ModelStateWrapper or ModelState with StructureMap.
In your unit tests, you could call the Initialize method on the service after creating the controller to pass in your testing model state and check the validation errors.
I know you had said you were using a ModelStateWrapper, but just wanted to add a more complete example that might help others...
You should really avoid such circular references. Your service class should not depend on the controller or anything in the System.Web.Mvc assembly whatsoever. It is the role of the controller or some action filter or model binder to manipulate the ModelState according to events happening in the service layer.