Ok, so I was playing around with Ninject, a "Service Layer" and a "Repository Layer".
I built a simple console application to to play around, this is what I came up with:
Imports Ninject
Module Module1
Sub Main()
Dim Kernel As IKernel = New StandardKernel(New CustomerModule)
Dim Service = Kernel.Get(Of CustomerService)()
Console.WriteLine(Service.GetCustomerByID(1).Name)
Console.Read()
End Sub
End Module
#Region "Services"
Public Class CustomerService
Private _Repository As ICustomerRepository
<Inject()> _
Public Sub New(ByVal Repository As ICustomerRepository)
_Repository = Repository
End Sub
Public Function GetCustomerByID(ByVal ID As Integer) As Customer
Return _Repository.GetByID(ID)
End Function
End Class
#End Region
#Region "Repositories"
Public Interface IRepository(Of T)
Function Query(ByVal Predicate As Expressions.Expression(Of Func(Of T, Boolean))) As IQueryable(Of T)
Function GetByID(ByVal ID As Integer) As T
End Interface
Public Interface ICustomerRepository
Inherits IRepository(Of Customer)
End Interface
Public Class CustomerRepository
Implements ICustomerRepository
Public Function GetByID(ByVal ID As Integer) As Customer Implements IRepository(Of Customer).GetByID
Return New Customer With {.ID = ID, .Name = "Sam Striano"}
End Function
Public Function Query(ByVal Predicate As System.Linq.Expressions.Expression(Of System.Func(Of Customer, Boolean))) As System.Linq.IQueryable(Of Customer) Implements IRepository(Of Customer).Query
Return Nothing
End Function
End Class
#End Region
#Region "Domain Objects"
Public Class Customer
Public Property ID As Integer
Public Property Name As String
End Class
#End Region
#Region "Ninject Modules"
Public Class CustomerModule
Inherits Modules.NinjectModule
Public Overrides Sub Load()
Bind(Of ICustomerRepository).To(Of CustomerRepository)()
End Sub
End Class
#End Region
My question, or I guess my lack of understanding, lies in Main() method of the module:
Sub Main()
Dim Kernel As IKernel = New StandardKernel(New CustomerModule)
Dim Service = Kernel.Get(Of CustomerService)()
Console.WriteLine(Service.GetCustomerByID(710615).Name)
Console.Read()
End Sub
Why not just do this:
Sub Main()
Dim Service = New CustomerService(New CustomerRepository)
Console.WriteLine(Service.GetCustomerByID(710615).Name)
Console.Read()
End Sub
Basically, you are asking why you should use a DI Container instead of Pure DI.
DI is really just a set of principles and patterns that enable loose coupling. You can use those patterns to compose an application irrespective of any particular container.
However, as an application grows in complexity, and particularly when you need to manage differing lifestyles of your components, a DI Container is an excellent tool that addresses many issues that you'd otherwise have to address manually.
Dependency injection lets you decouple specific implementations of objects from their interfaces. It is difficult to justify in most of the small examples out there, but for larger systems it can be a life-saver. It can also help you to isolate your objects in unit tests.
For example, if you wanted to write tests for your CustomerService class, you could easily inject a MockRepository instead of CustomerRepository. This would let you test CustomerService without also testing CustomerRepository.
Outside of unit testing, I think the easiest example to visualize might be if you were writing a data access module for your application. You might want to support SQL Server and MySQL. You would then create Interfaces for your data access objects and create specific implementations of them for both database systems. Those implementations could be injected at runtime, thusly:
Function Setup(ByVal dbType As String) As IKernel
Dim dbModule As NinjectModule
If dbType = "SQL Server" Then
dbModule = New SQLServerModule
Else If dbType = "MySQL" Then
dbModule = New MySQLModule
End If
Return New StandardKernel(dbModule)
End Function
This also enables you to add support for other databases in the future, isolating the implementation details from the rest of the application.
Related
I understand the concept of interfaces, however I often find it difficult to find practical examples of how to use them. I have produced the following code:
Public MustInherit Class Deletion2
Implements DeletionInterface2
Public MustOverride Function Delete() As String Implements DeletionInterface2.Delete
Public Function CheckDate() As Boolean Implements DeletionInterface2.CheckDate
Return True
End Function
End Class
Public Class System1Delete
Inherits Deletion2
Implements DeletionInterface2
Overrides Function Delete() As String
Return "System 1 Deleted"
End Function
End Class
Public Class System2Delete
Inherits Deletion2
Implements DeletionInterface2
Overrides Function Delete() As String
Return "System 2 Deleted"
End Function
End Class
Public Interface DeletionInterface2
Function CheckDate() As Boolean
Function Delete() As String
End Interface
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim IDeletion As DeletionInterface2
IDeletion = New System1Delete
IDeletion.CheckDate()
IDeletion.Delete()
IDeletion = Nothing
IDeletion = New System2Delete
IDeletion.CheckDate()
IDeletion.Delete()
IDeletion = Nothing
End Sub
In the example above (in page load) I have used a reference to an interface to create an instance of an object, but I do not understand the true benefit of this.
The benefit of using an abstraction like an interface (or a MustInherit class) is that you can treat any object that implements the interface the same exact way.
For example, the System.Data namespace uses many such abstraction, meaning that implementing the different data providers is easier and since the core is using these abstractions, it doesn't need to change as new implementations are added (things about all the different providers, built in and third party - SQL Server, Oracle, PostGresSQL, MySQL etc...).
I'm doing some LINQ which requires a custom comparer, so I created a new class implementing IEqualityComparer. However, when I use it, I have to create an instance of it each time.
Dim oldListOnly = oldList.Except(newList, New MyEqualityComparer)
Dim newListOnly = newList.Except(oldList, New MyEqualityComparer)
I may be misunderstanding how .NET works, but it seems wasteful to create a new comparer each time. I really just want one instance (the equivalent of static in C++/C#).
So I tried creating a "static" class, which in vb.net is a module. But got an 'Implements' not valid in Modules error.
I then tried making the Equals and GetHashCode function shared methods on my class, but got this error: Methods that implement interface members cannot be declared 'Shared'.
Any ideas how to accomplish my goal here? Or am I simply misunderstanding what's going behind the scenes?
Your understanding is correct, although the waste is unlikely to be noticeable. For your situation, you could use the singleton pattern, which usually goes something like this:
Public Class MyEqualityComparer
Implements IEqualityComparer(Of whatever)
Private Sub New()
'no outsider creation
End Sub
Private Shared ReadOnly _instance As New MyEqualityComparer()
Public Shared ReadOnly Property Instance As MyEqualityComparer
Get
Return _instance
End Get
End Property
'other code
End Class
Why not simply do
Dim comparer = New MyEqualityComparer
Dim oldListOnly = oldList.Except(newList, comparer )
Dim newListOnly = newList.Except(oldList, comparer )
There needs to be an instance of a concrete type that implements IEqualityComparer. What you can do with a module, however, is define a public instance which is initialized to "New EqualityComparer". You can then pass that default instance to the Except method.
Something like:
Public Module MyComparer
Public acmeComparer As acmeCompareType
Public Class acmeCompareType
Implements IEqualityComparer(Of System.Drawing.Point)
Public Function Equals1(x As System.Drawing.Point, y As System.Drawing.Point) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).Equals
Return Math.Abs(x.X) = Math.Abs(y.X) AndAlso Math.Abs(x.Y) = Math.Abs(y.Y)
End Function
Public Function GetHashCode1(obj As System.Drawing.Point) As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).GetHashCode
' Note that obj is a struct passed by value, so we can safely modify it here
' (without affecting the caller's instance)
obj.X = Math.Abs(obj.X)
obj.Y = Math.Abs(obj.Y)
Return obj.GetHashCode
End Function
End Class
End Module
In our current project I am using a generic interface iService which is inherited by all other service interfaces. For instance IService is inherited by ILogService.
The ILogService interface is then implemented by LogService as below:
Public Interface IService(Of T)
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
Public Interface ILogService
Inherits IService(Of Log)
Function FindLogsByOwner(ByVal Owner As Guid, ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByVisibility(ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByType(ByVal type As LogType) As IList(Of Log)
End Interface
Public Class LogService
Implements ILogService
Public Function FindAll() As System.Collections.Generic.IEnumerable(Of Model.CSLS.Log) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindAll
End Function
Public Function FindBy(Id As System.Guid) As Model.CSLS.Log Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindBy
End Function
Public Sub Remove(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Remove
End Sub
Public Sub Save(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Save
End Sub
Public Function FindAllLogsByType(type As Model.CSLS.LogType) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByType
End Function
Public Function FindAllLogsByVisibility(visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByVisibility
End Function
Public Function FindLogsByOwner(Owner As System.Guid, visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindLogsByOwner
End Function
End Class
Help Needed: I am trying to understand that when I am implementing ILogService interface I still get the functions/subs in the LogService class containing:
method parameter T of Type IEntity instead of Log
How can I update the method signature so T is displayed as Log?
What am I doing wrong?
Are you talking about these?
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
This is very confusing, because in the above methods T is the name of a method parameter, not a generic type parameter. It could just as easily have been foo or bar. In each case the type of T is IEntity.
If the intention here was that Save and Remove should each accept an argument of type T, but that type T must implement IEntity, this is how you would express that:
Public Interface IService(Of T As IEntity)
Sub Save(ByVal entity As T)
Sub Remove(ByVal entity As T)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
#DanTao is correct if that is the intention. If, however, you just intended the name of the method parameter to be specified by the generic type, that is not possible.
However, you can specify any name you like in the implementing method, so you can use Log if you want, but you can't enforce it (and some FxCop rule will warn you haven't used the same parameter name as specified in the interface).
Can someone point me to a sample solution that I can download? I am having trouble trying to find one.
Create a class library, MyInterfaces, that houses just your interfaces:
Imports System.Runtime.Serialization
Imports System.ServiceModel
' NOTE: You can use the "Rename" command on the context menu to change the interface name "IService" in both code and config file together.
<ServiceContract()>
Public Interface IService
<OperationContract()>
Function GetData(ByVal value As Integer) As String
<OperationContract()>
Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
<OperationContract()>
Function GetCustomer() As Customer
' TODO: Add your service operations here
End Interface
' Use a data contract as illustrated in the sample below to add composite types to service operations.
Public Class Customer
<DataMember()>
Public Property Name() As String
<DataMember()>
Public Property Age() As Integer
End Class
<DataContract()>
Public Class CompositeType
<DataMember()>
Public Property BoolValue() As Boolean
<DataMember()>
Public Property StringValue() As String
End Class
Add a Web Service Library project. Set a reference to the above project that contains the above Interfaces. Add the following code to this project:
Imports MyInterfaces
' NOTE: You can use the "Rename" command on the context menu to change the class name "Service" in code, svc and config file together.
Public Class Service
Implements IService
Public Function GetData(ByVal value As Integer) As String Implements IService.GetData
Return String.Format("You entered: {0}", value)
End Function
Public Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType Implements IService.GetDataUsingDataContract
If composite Is Nothing Then
Throw New ArgumentNullException("composite")
End If
If composite.BoolValue Then
composite.StringValue &= "Suffix"
End If
Return composite
End Function
Public Function GetCustomer() As MyInterfaces.Customer Implements MyInterfaces.IService.GetCustomer
Dim x As New Customer
x.Age = 15
x.Name = "Chad"
Return x
End Function
End Class
Add a 3rd project, a client app which will consume the Service. I'll make mine a WinForm app. Have this project also reference the Interface projrect. Add this code:
Imports System.ServiceModel
Imports MyInterfaces
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim endPoint As New EndpointAddress("http://localhost:9496/WCFService4/Service.svc")
Dim binding As New BasicHttpBinding()
Dim proxy As IService = ChannelFactory(Of IService).CreateChannel(binding, endPoint)
Dim getDateResult As String = proxy.GetData(3)
Dim myCompositeType As New MyInterfaces.CompositeType
myCompositeType.BoolValue = True
myCompositeType.StringValue = "ok"
Dim result As MyInterfaces.CompositeType = proxy.GetDataUsingDataContract(myCompositeType)
MessageBox.Show(result.StringValue)
End Sub
This is just a quick and dirty example. My goal was to get rid of the generated proxy object that you normally get when you use the Add Service Wizard. I suppose that end point should come from a config file, though I am not sure how to dynamically determine what the port will be when debugging under VS' web server.
Dim endPoint As New EndpointAddress("http://localhost:9496/WCFService4/Service.svc")
The one thing that I don't like about this is that the objects I pass, eg, a Customer object, which represent a potential Business Object, seem to require that they have a default parameterless constructor. I prefer to have constructors to ensure that my object was properly initialized wherever it may have been used.
Ok... So I have created my model using EF4. Great!
I then turned off the code generation and downloaded this extension: http://visualstudiogallery.msdn.microsoft.com/23df0450-5677-4926-96cc-173d02752313
(POCO Entity Generator). Awesome!
Ran it, and it generates all of my classes. Is that all I have to do? It seems to work, my repositories get to the objects and persist to the DB.
Please have a look at the following code and let me know if I am on the right track.
** Sample Code **
Controller:
Namespace Controllers
Public Class HomeController
Inherits System.Web.Mvc.Controller
Function Index() As ActionResult
Return View(New Models.HomeModel)
End Function
End Class
End Namespace
Model:
Namespace Models
Public Class HomeModel
Private _Repository As Titan.Business.Repositories.ICustomerRepository
Private _SalesRepRepo As Titan.Business.Repositories.ISalesRepresentativeRepository
Public Property Customers As IEnumerable(Of Titan.Business.Customer)
Public Property SalesReps As IEnumerable(Of Titan.Business.SalesRepresentative)
Public Sub New()
_Repository = New Titan.Business.Repositories.CustomerRepository
_SalesRepRepo = New Titan.Business.Repositories.SalesRepresentativeRepository
_Customers = _Repository.Query(Function(x) x.LastName.StartsWith("Str"))
_SalesReps = _SalesRepRepo.Query(Function(x) x.LastName.StartsWith("Str"))
End Sub
End Class
End Namespace
Repository and Interfaces:
Namespace Repositories
Public Interface IRepository(Of T)
Function Query(ByVal Predicate As System.Linq.Expressions.Expression(Of Func(Of T, Boolean))) As IEnumerable(Of T)
Function GetByID(ByVal ID As Integer) As T
Sub Add(ByVal Entity As T)
Sub Delete(ByVal Entity As T)
Sub Save(ByVal Entity As T)
End Interface
Public Interface ICustomerRepository
Inherits IRepository(Of Customer)
End Interface
Public Interface ISalesRepresentativeRepository
Inherits IRepository(Of SalesRepresentative)
End Interface
End Namespace
Namespace Repositories
Public Class SalesRepresentativeRepository
Implements ISalesRepresentativeRepository
Public Sub Add(ByVal Entity As SalesRepresentative) Implements IRepository(Of SalesRepresentative).Add
End Sub
Public Sub Delete(ByVal Entity As SalesRepresentative) Implements IRepository(Of SalesRepresentative).Delete
End Sub
Public Function GetByID(ByVal ID As Integer) As SalesRepresentative Implements IRepository(Of SalesRepresentative).GetByID
End Function
Public Function Query(ByVal Predicate As System.Linq.Expressions.Expression(Of System.Func(Of SalesRepresentative, Boolean))) As System.Collections.Generic.IEnumerable(Of SalesRepresentative) Implements IRepository(Of SalesRepresentative).Query
Using db As New GTGContainer
Return db.SalesRepresentatives.Where(Predicate).ToList
End Using
End Function
Public Sub Save(ByVal Entity As SalesRepresentative) Implements IRepository(Of SalesRepresentative).Save
End Sub
End Class
End Namespace
Any suggestions would be so helpful to me.
Where does the service layer fit in?
What about the AutoMapper? Do I even need to use that now?
Dependency Injection? Anyone care to explain.
Thanks a bunch,
Sam
There's a great article by Scott Allen about Testing Entity Framework 4 - creating the POCO classes is a good first step, but if you want to test you business layer separate from EF you will have to introduce a Unit of Work that coordinates saving state across multiple repositories and allows DI.