I work in a VB.Net environment and have recently been tasked with creating an MVC environment to use as a base to work from. I decided to convert the latest SharpArchitecture release (Q3 2009) into VB, which on the whole has gone fine after a bit of hair pulling. I came across a problem with Castle Windsor where my custom repository interface (lives in the core/domain project) that was reference in the constructor of my test controller was not getting injected with the concrete implementation (from the data project). I hit a brick wall with this so basically decided to switch out Castle Windsor for StructureMap.
I think I have implemented this ok as everything compiles and runs and my controller ran ok when referencing a custom repository interface. It appears now that I have/or cannot now setup my generic interfaces up properly (I hope this makes sense so far as I am new to all this). When I use IRepository(Of T) (wanting it to be injected with a concrete implementation of Repository(Of Type)) in the controller constructor I am getting the following runtime error:
"StructureMap Exception Code: 202 No Default Instance defined for PluginFamily SharpArch.Core.PersistenceSupport.IRepository`1[[DebtRemedy.Core.Page, DebtRemedy.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], SharpArch.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b5f559ae0ac4e006"
Here are my code excerpts that I am using (my project is called DebtRemedy).
My structuremap registry class
Public Class DefaultRegistry
Inherits Registry
Public Sub New()
''//Generic Repositories
AddGenericRepositories()
''//Custom Repositories
AddCustomRepositories()
''//Application Services
AddApplicationServices()
''//Validator
[For](GetType(IValidator)).Use(GetType(Validator))
End Sub
Private Sub AddGenericRepositories()
''//ForRequestedType(GetType(IRepository(Of ))).TheDefaultIsConcreteType(GetType(Repository(Of )))
[For](GetType(IEntityDuplicateChecker)).Use(GetType(EntityDuplicateChecker))
[For](GetType(IRepository(Of ))).Use(GetType(Repository(Of )))
[For](GetType(INHibernateRepository(Of ))).Use(GetType(NHibernateRepository(Of )))
[For](GetType(IRepositoryWithTypedId(Of ,))).Use(GetType(RepositoryWithTypedId(Of ,)))
[For](GetType(INHibernateRepositoryWithTypedId(Of ,))).Use(GetType(NHibernateRepositoryWithTypedId(Of ,)))
End Sub
Private Sub AddCustomRepositories()
Scan(AddressOf SetupCustomRepositories)
End Sub
Private Shared Sub SetupCustomRepositories(ByVal y As IAssemblyScanner)
y.Assembly("DebtRemedy.Core")
y.Assembly("DebtRemedy.Data")
y.WithDefaultConventions()
End Sub
Private Sub AddApplicationServices()
Scan(AddressOf SetupApplicationServices)
End Sub
Private Shared Sub SetupApplicationServices(ByVal y As IAssemblyScanner)
y.Assembly("DebtRemedy.ApplicationServices")
y.With(New FirstInterfaceConvention)
End Sub
End Class
Public Class FirstInterfaceConvention
Implements ITypeScanner
Public Sub Process(ByVal type As Type, ByVal graph As PluginGraph) Implements ITypeScanner.Process
If Not IsConcrete(type) Then
Exit Sub
End If
''//only works on concrete types
Dim firstinterface = type.GetInterfaces().FirstOrDefault()
''//grabs first interface
If firstinterface IsNot Nothing Then
graph.AddType(firstinterface, type)
Else
''//registers type
''//adds concrete types with no interfaces
graph.AddType(type)
End If
End Sub
End Class
I have tried both ForRequestedType (which I think is now deprecated) and For.
IRepository(Of T) lives in SharpArch.Core.PersistenceSupport.
Repository(Of T) lives in SharpArch.Data.NHibernate.
My servicelocator class
Public Class StructureMapServiceLocator
Inherits ServiceLocatorImplBase
Private container As IContainer
Public Sub New(ByVal container As IContainer)
Me.container = container
End Sub
Protected Overloads Overrides Function DoGetInstance(ByVal serviceType As Type, ByVal key As String) As Object
Return If(String.IsNullOrEmpty(key), container.GetInstance(serviceType), container.GetInstance(serviceType, key))
End Function
Protected Overloads Overrides Function DoGetAllInstances(ByVal serviceType As Type) As IEnumerable(Of Object)
Dim objList As New List(Of Object)
For Each obj As Object In container.GetAllInstances(serviceType)
objList.Add(obj)
Next
Return objList
End Function
End Class
My controllerfactory class
Public Class ServiceLocatorControllerFactory
Inherits DefaultControllerFactory
Protected Overloads Overrides Function GetControllerInstance(ByVal requestContext As RequestContext, ByVal controllerType As Type) As IController
If controllerType Is Nothing Then
Return Nothing
End If
Try
Return TryCast(ObjectFactory.GetInstance(controllerType), Controller)
Catch generatedExceptionName As StructureMapException
System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave())
Throw
End Try
End Function
End Class
The initialise stuff in my global.asax
Dim container As IContainer = New Container(New DefaultRegistry)
ControllerBuilder.Current.SetControllerFactory(New ServiceLocatorControllerFactory())
ServiceLocator.SetLocatorProvider(Function() New StructureMapServiceLocator(container))
My test controller
Public Class DataCaptureController
Inherits BaseController
Private ReadOnly clientRepository As IClientRepository()
Private ReadOnly pageRepository As IRepository(Of Page)
Public Sub New(ByVal clientRepository As IClientRepository(), ByVal pageRepository As IRepository(Of Page))
Check.Require(clientRepository IsNot Nothing, "clientRepository may not be null")
Check.Require(pageRepository IsNot Nothing, "pageRepository may not be null")
Me.clientRepository = clientRepository
Me.pageRepository = pageRepository
End Sub
Function Index() As ActionResult
Return View()
End Function
The above works fine when I take out everything to do with the pageRepository which is IRepository(Of T).
Any help with this would be greatly appreciated.
I had a similar issue yesterday with instantiating IRepository(Of MyEntity).
I had to state y.ConnectImplementationsToTypesClosing(GetType(IRepository(Of ))) in my Scan delegate to make StructureMap map generic types to their implementation.
Like this:
Private Shared Sub SetupCustomRepositories(ByVal y As IAssemblyScanner)
y.Assembly("DebtRemedy.Core")
y.Assembly("DebtRemedy.Data")
y.WithDefaultConventions()
y.ConnectImplementationsToTypesClosing(GetType(Of ));
End Sub
Make sure you are only creating one container.
I also converted a C# project from Castle Windsor to StructureMap. The original CW-based project instantiated a Container in Application_Start() (MVC2 project) and passed it around for configuration. I kept the same approach without thinking, kinda when you translate from Spanish to English litterally, and it's just as bad. :)
What happened is that I ended up creating a second SM container. StructureMap's container is static, and so there's always one "in the background". If you new up a container, you actually create a second, independent container. if you aren't careful, you end up sometimes using one, sometimes the other, and get a plague of " No Default Instance" errors at various points when you know it's defined..
The way I came across it is that I ended up littering my code with WhatDoIHave() calls, which was fortunate because I noted that sometimes I saw a configured container (the second) and sometimes I saw the static one (the first), which had not been configured. Different GUID names was the giveaway.
Check if the same is happening in your VB code.
Not that familiar with this, but it looks like it may not be registered with the container or because the resolver is greedy it might choose a constructor that does not have registered items. The following URL looks very similar to the same problem take a look...
http://learningbyfailing.com/2010/02/structuremap-exception-no-default-instance-defined-for-pluginfamily-iformsauthentication/
Related
I want to create instance of PerRequestResourceProvider using ninject InRequestScope:
public class PerRequestResourceProvider: IPerRequestResourceProvider
{
priavte readonly _perRequestResorceInstance;
public PerRequestResourceProvider()
{
_perRequestResorceInstance = new PerRequestResource();
}
public PerRequestResource GetResource()
{
return _perRequestResorceInstance;
}
}
public interface IPerRequestResourceProvider
{
PerRequestResource GetResource();
}
In my NinjectDependencyResolver:
.....
kernel.Bind<IPerRequestResourceProvider>.To<PerRequestResourceProvider>().InRequestScope();
I inject IPerRequestResourceProvider in few classes. But when I add breakpoint to PerRequestResourceProvider constructor I see that PerRequestResourceProvider is created three times during one request and not single per request. What's wrong?
Update: source code ttps://bitbucket.org/maximtkachenko/ninjectinrequestscope/src
There are two issues with your code:
Ninject is not getting initialized correctly.
You need one of the Ninject.MVCx packages (according to the MVC version you are using). To configure it correctly, see: http://github.com/ninject/ninject.web.mvc
You are injecting PerRequestResourceProvider (the class type), and not IPerRequestResourceProvider (the interface type) into HomeController, thus the .InRequestScope() defined on the IPerRequestResourceProvider binding is not taking any effect. Change the HomeController constructor to get the inteface type injected and you're good.
Ninject does not require bindings for instantiatable (non-abstract,..) classes. This is why it is not obvious when the wrong binding is used.
I am currently upgrading our software from VB6 to VB.NET and doing so one DLL at a time. The EXE will be last since it has a lot of third party UI controls.
One issue that I found is that the VB6 version of the DLL housed an IProgressCallBack class that was acting like an interface class (which does not officially exist in VB6) and allowed the EXE to get progress reports from the DLL and display it on the screen for the user.
When this DLL is migrated the IProgressCallBack class can be set as an Interface class, but then it cannot be 'Set' to the instance of the form using it. But if I create it as just a normal class, it still will not let me 'Set' it to the instance of the form using it since they are two different object types.
I need to know how do I either make the IProgressCallBack class work between VB6 and VB.NET or what is an alternate solution to keep the VB6 UI informed of the progress of the .NET DLL?
Here is an example of the VB6 version of both and how they work:
VB6 DLL: IProgressCallBack
Option Explicit
Public Function NotifyProgress(ByVal Progress As Single, ByRef isCancel As Boolean) As Boolean
End Function
VB6 DLL: MainFunctions Class
Public ProgressNotify As IProgressCallBack
Public Sub DoWork()
Dim l As Long
For l = 1 to 100
Sleep 10 'sleep for 1/100 of a second
ProgressNotify.NotifyProgress 1, False
Next
End Sub
VB6 EXE
Implements IProgressCallBack
Dim m_oMainFunc As MainFunctions
Private Sub cmdStart_Click()
Set m_oMainFunc = New MainFunctions
Set m_oMainFunc.ProgressNotify = Me
m_oMainFunc.DoWork
Set m_oMainFunc = Nothing
End Sub
Public Function IProgressCallBack_NotifyProgress(ByVal Progress As Single, isCancel As Boolean) As Boolean
lblStatus.Caption = CStr(Round(Progress)) & "%"
pbStatus.Value = Round(Progress)
Me.Refresh
End Function
Thanks,
Chris
I have not exactly found the answer to the question I posted above, but I have found a nice workaround.
Essentially I pass in the form as an object to the class pretending to be an interface and directly call the interface method that resides on the form to update the screen.
Here is the code sample of how I do this:
VB.NET DLL: IProgressCallBack Class
Dim m_oForm As Object
Public Sub New(ByRef oForm As Object)
m_oForm = oForm
End Sub
Public Function NotifyProgress(ByVal Progress As Single, ByRef isCancel As Boolean) As Boolean
m_oForm.IProgressCallBack_NotifyProgress(Progress, isCancel)
End Function
VB.NET DLL: MainFunctions Class
Public ProgressNotify As IProgressCallBack
Public Sub New()
'Need Public Sub New() with no parameters for all Com Classes
MyBase.New()
End Sub
Public Sub Initialize(ByRef oForm As Object)
ProgressNotify = New IProgressCallBack(oForm)
End Sub
Public Sub DoWork()
For l As Long = 1 to 100
Threading.Thread.Sleep(10) 'sleep for 1/100 of a second
ProgressNotify.NotifyProgress(l,False)
Next
End Sub
VB6 EXE
Dim m_oMainFunc As TestDLL_Net.MainFunctions
Private cmdStart_Click()
Set m_oMainFunc = New TestDLL_Net.MainFunctions
m_oMainFunc.Initialize Me
m_oMainFunc.DoWork
Set m_oMainFunc = Nothing
End Sub
Public Function IProgressCallBack_NotifyProgress(ByVal Progress As Single, isCancel As Boolean) As Boolean
DoEvents
lblStatus.Caption = CStr(Round(Progress)) & "%"
Me.Refresh
pbStatus.Value = Round(Progress)
End Function
This provides the same result as the VB6 DLL to VB6 EXE example posted above, and the changes in the EXE are minimal to make it work.
Thanks,
Chris
I'll start by telling my project setup:
ASP.NET MVC 1.0
StructureMap 2.6.1
VB
I've created a bootstrapper class shown here:
Imports StructureMap
Imports DCS.Data
Imports DCS.Services
Public Class BootStrapper
Public Shared Sub ConfigureStructureMap()
ObjectFactory.Initialize(AddressOf StructureMapRegistry)
End Sub
Private Shared Sub StructureMapRegistry(ByVal x As IInitializationExpression)
x.AddRegistry(New MainRegistry())
x.AddRegistry(New DataRegistry())
x.AddRegistry(New ServiceRegistry())
x.Scan(AddressOf StructureMapScanner)
End Sub
Private Shared Sub StructureMapScanner(ByVal scanner As StructureMap.Graph.IAssemblyScanner)
scanner.Assembly("DCS")
scanner.Assembly("DCS.Data")
scanner.Assembly("DCS.Services")
scanner.WithDefaultConventions()
End Sub
End Class
I've created a controller factory shown here:
Imports System.Web.Mvc
Imports StructureMap
Public Class StructureMapControllerFactory
Inherits DefaultControllerFactory
Protected Overrides Function GetControllerInstance(ByVal controllerType As System.Type) As System.Web.Mvc.IController
Return ObjectFactory.GetInstance(controllerType)
End Function
End Class
I've modified the Global.asax.vb as shown here:
...
Sub Application_Start()
RegisterRoutes(RouteTable.Routes)
'StructureMap
BootStrapper.ConfigureStructureMap()
ControllerBuilder.Current.SetControllerFactory(New StructureMapControllerFactory())
End Sub
...
I've added a Structure Map registry file to each of my three projects: DCS, DCS.Data, and DCS.Services. Here is the DCS.Data registry:
Imports StructureMap.Configuration.DSL
Public Class DataRegistry
Inherits Registry
Public Sub New()
'Data Connections.
[For](Of DCSDataContext)() _
.HybridHttpOrThreadLocalScoped _
.Use(New DCSDataContext())
'Repositories.
[For](Of IShiftRepository)() _
.Use(Of ShiftRepository)()
[For](Of IMachineRepository)() _
.Use(Of MachineRepository)()
[For](Of IShiftSummaryRepository)() _
.Use(Of ShiftSummaryRepository)()
[For](Of IOperatorRepository)() _
.Use(Of OperatorRepository)()
[For](Of IShiftSummaryJobRepository)() _
.Use(Of ShiftSummaryJobRepository)()
End Sub
End Class
Everything works great as far as loading the dependecies, but I'm having problems with the DCSDataContext class that was genereated by Linq2SQL Classes.
I have a form that posts to a details page (/Summary/Details), which loads in some data from SQL. I then have a button that opens a dialog box in JQuery, which populates the dialog from a request to (/Operator/Modify). On the dialog box, the form has a combo box and an OK button that lets the user change the operator's name. Upon clicking OK, the form is posted to (/Operator/Modify) and sent through the service and repository layers of my program and updates the record in the database. Then, the RedirectToAction is called to send the user back to the details page (/Summary/Details) where there is a call to pull the data from SQL again, updating the details view.
Everything works great, except the details view does not show the new operator that was selected. I can step through the code and see the DCSDataContext class being accessed to update the operator (which does actually change the database record), but when the DCSDataContext is accessed to reload the details objects, it pulls in the old value. I'm guessing that StructureMap is causing not only the DCSDataContext class but also the data to be cached?
I have also tried adding the following to the Global.asax, but it just ends up crashing the program telling me the DCSDataContext has been disposed...
Private Sub MvcApplication_EndRequest(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.EndRequest
StructureMap.ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects()
End Sub
Can someone please help?
Got this response back from Jeremy Miller on the StructureMap google group:
Easy money, you're creating an instance of your DataContext object -yourself- in the registration, which de facto makes that a singleton throughout the StructureMap ecosystem.
This code:
'Data Connections.
[For](Of DCSDataContext)() _
.HybridHttpOrThreadLocalScoped _
.Use(New DCSDataContext())
Needs to define the DataContext using -deferred- execution rather than using the pre-built "New DCSDataContext()"
If you were in C# (because I don't know the VB syntax), you would do:
For<DCSDataContext>().HybridHttpOrThreadLocalScoped().Use(() => new DCSDataContext());
I ran this through a C# to VB converter and it gave me this:
[For](Of DCSDataContext)() _
.HybridHttpOrThreadLocalScoped _
.Use(Function() New DCSDataContext())
Which works great!
As part of a unit test I am trying to mock the return value of FormsIdentity.Ticket.UserData
The following will NOT work but it should give an idea of what I am trying to do:
var principal = Mock<IPrincipal>();
var formsIdentity = Mock<FormsIdentity>();
formsIdentity.Setup(a => a.Ticket.UserData).Returns("aaa | bbb | ccc");
principal.Setup(b => b.Identity).Returns(formsIdentity.Object);
The code I am trying to test looks something like this:
FormsIdentity fIdentity = HttpContext.Current.User.Identity as FormsIdentity;
string userData = fIdentity.Ticket.UserData;
All I want to do in my unit test is fake the return value from FormsIdentity.Ticket.UserData. But when I run the code in the first section I get an error when trying to mock the FormsIdentity. The error says the type to mock must be an interface, abstract class or non-sealed class.
I tried to use IIdentity instead of FormsIdentity (FormsIdentity is an implementation of IIdentity) but IIdentity doesn't have .Ticket.UserData.
So how can I write this test so that I get a value from FormsIdentity.Ticket.UserData?
I'm not a Unit Test expert, by any means, just getting my feet wet in the area.
Isn't it overkill to mock out the Identity in a unit test, because the Identity code is code that you can assume works already in isolation? (ie. it's Microsoft's code?) For example, when unit testing your own code, you wouldn't need to mock out one of the Framework objects. I mean, would you ever need to mock a List or a Dictionary?
That being said, if you REALLY want to test your code in isolation or for some reason have super fine control over the data returned in Userdata, can't you just write an Interface for the interaction between the Identity and your code?
Public Interface IIdentityUserData
Readonly Property UserData As String
End Interface
Public Class RealIdentityWrapper
Implements IIdentityUserData
Private _identity as FormsIdentity
Public Sub New(identity as FormsIdentity)
'the real version takes in the actual forms identity object
_identity = identity
End Sub
Readonly Property UserData As String Implements IIDentityUserData.UserData
If not _identity is nothing then
Return _identity.Ticket.UserData
End If
End Property
End Class
'FAKE CLASS...use this instead of Mock
Public Class FakeIdentityWrapper
Implements IIdentityUserData
Readonly Property UserData As String Implements IIDentityUserData.UserData
If not _identity is nothing then
Return "whatever string you want"
End If
End Property
End Class
'here's the code that you're trying to test...modified slightly
Dim fIdentity As FormsIdentity= HttpContext.Current.User.Identity
Dim identityUserData As IIdentityUserData
identityUserData =
'TODO: Either the Real or Fake implementation. If testing, inject the Fake implementation. If in production, inject the Real implementation
Dim userData as String
userData = identityUserData.UserData
Hope this helps
I have a class that takes an array of interfaces in the constructor:
public class Foo<T1, T2> : IFoo<T1, T2>
{
public Foo(IBar[] bars)
{
...
}
}
My container registration looks as follows:
container.Register(AllTypes.Pick().FromAssemblyNamed("...")
.WithService.FirstInterface());
container.AddComponent("foo", typeof(IFoo<,>), typeof(Foo<,>));
I have several implementations of IBar, and the container can definately locate them, as calling ServiceLocator.Current.GetAllInstances<IBar>() works fine.
However, if I try to get an instance of IFoo, it throws an exception saying it couldn't satisfy the deoendency... "which was not registered".
If I change the constructor to take a single instance of IBar it works fine.
Any ideas?
Add the ArrayResolver:
container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));