In my project I have my Linq To SQL dbml file, A Repository Layer for each DB Table and a Service Layer for each Repository.
In my service I have some MetaData for Validation as well as I extend each (table) Class to add some custom information to the object (you'll see this in the code below).
My question is, Should I consider building a Custom ViewModal for each (table) Class instead of using the extended Class in the service layer?
Below is an example of what I have now.
Repository
Namespace Domain
#Region "Interface"
Public Interface IUserRepository
Sub AddUser(ByVal openid As OpenID)
Function GetUsers() As IQueryable(Of User)
Sub UpdateUser(ByVal user As User)
Sub SubmitChanges()
End Interface
#End Region
#Region "Repository"
Public Class UserRepository : Implements IUserRepository
Private dc As MyDatabaseDataContext
Public Sub New()
dc = New MyDatabaseDataContext
End Sub
Public Sub AddUser(ByVal openid As OpenID) Implements IUserRepository.AddUser
Dim user As New User
user.MemberSince = DateTime.Now
openid.User = user
dc.OpenIDs.InsertOnSubmit(openid)
End Sub
Public Function GetUsers() As IQueryable(Of User) Implements IUserRepository.GetUsers
Dim users = (From u In dc.Users
Select u)
Return users.AsQueryable
End Function
Public Sub UpdateUser(ByVal user As User) Implements IUserRepository.UpdateUser
Dim _user = (From u In dc.Users
Where u.ID = user.ID
Select u).Single
With _user
.About = user.About
.BirthDate = user.BirthDate
.Email = user.Email
.isClosed = user.isClosed
.isProfileComplete = user.isProfileComplete
.RegionID = user.RegionID
.Reputation = user.Reputation
.UserName = user.UserName
.WebSite = user.WebSite
End With
End Sub
Public Sub SubmitChanges() Implements IUserRepository.SubmitChanges
dc.SubmitChanges()
End Sub
End Class
#End Region
End Namespace
Service
Imports System.ComponentModel.DataAnnotations
Namespace Domain
#Region "Validation"
<MetadataType(GetType(UserMetaData))> _
Partial Public Class User
Public Property UserRegion As String
Public Property LastSeen As DateTime
Public ReadOnly Property Slug(ByVal user As User) As String
Get
Return Replace(user.UserName, " ", "-")
End Get
End Property
End Class
''' <summary>
''' Validation for all User data.
''' </summary>
''' <remarks>All validation is done at the Service Layer</remarks>
Public Class UserMetaData
<DisplayName("name")> _
<Required(ErrorMessage:="Username is required.")> _
<StringLength(30, ErrorMessage:="Username cannot exceed 30 characters.")> _
<RegularExpression("^\w{3,30}$", ErrorMessage:="Not a valid username.")> _
Public Property UserName As String
<DisplayName("email")> _
<StringLength(50, ErrorMessage:="Email Address cannot exceed 50 characters.")> _
<RegularExpression("^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})$", ErrorMessage:="Not a valid email address.")> _
Public Property Email As String
<DisplayName("website")> _
<StringLength(256, ErrorMessage:="Web Address cannot exceed 256 characters.")> _
<RegularExpression("^http(s?)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$", ErrorMessage:="Not a valid website address.")> _
Public Property WebSite As String
<DisplayName("about")> _
<StringLength(2000, ErrorMessage:="Profile cannot exceed 2000 characters.")> _
Public Property About As String
<DisplayName("region")> _
<Required(ErrorMessage:="Region is required.")> _
Public Property UserRegion As Integer
<DisplayName("birthdate")> _
<DisplayFormat(ApplyFormatInEditMode:=True, ConvertEmptyStringToNull:=True, DataFormatString:="{0:MM/dd/yyyy}")> _
Public Property BirthDate As DateTime
End Class
#End Region
#Region "Interface"
Public Interface IUserService
Sub AddUser(ByVal claimedidentifier As String, ByVal notes As String)
Function GetAllUsers() As IList(Of User)
Function GetUserByID(ByVal id As Integer) As User
Sub UpdateUser(ByVal user As User)
Sub SubmitChanges()
End Interface
#End Region
#Region "Service"
Public Class UserService : Implements IUserService
Private _UserRepository As IUserRepository
Public Sub New(ByVal UserRepository As IUserRepository)
_UserRepository = UserRepository
End Sub
Public Sub AddUser(ByVal claimedidentifier As String, ByVal notes As String) Implements IUserService.AddUser
Dim openid As New OpenID
openid.ClaimedIdentifier = claimedidentifier
openid.UserNotes = notes
_UserRepository.AddUser(openid)
End Sub
Public Function GetAllUsers() As System.Collections.Generic.IList(Of User) Implements IUserService.GetAllUsers
Return _UserRepository.GetUsers().Where(Function(u) (Not u.isClosed)).ToList
End Function
Public Function GetUserByID(ByVal id As Integer) As User Implements IUserService.GetUserByID
Return _UserRepository.GetUsers().Where(Function(u) (Not u.isClosed And u.ID = id)).SingleOrDefault
End Function
Public Sub UpdateUser(ByVal user As User) Implements IUserService.UpdateUser
_UserRepository.UpdateUser(user)
End Sub
Public Sub SubmitChanges() Implements IUserService.SubmitChanges
_UserRepository.SubmitChanges()
End Sub
End Class
#End Region
End Namespace
And currently in my Controller I send Modal Data to my View like this
Dim user As Domain.User = UserService.GetUserByID(id)
Return View(user)
Now one thing I have run into is the need send the user object to the Slug property whenever I need to use the Slug
Dim user As Domain.User = UserService.GetUserByID(id)
user.Slug = user.Slug(user) ''# this seems like a bit of a pain in the ass
Return View(user)
So because of this, is it better for me to create a custom ViewModal for each (table) Class and simply do the following
Dim user As Domain.UserViewModal = New Domain.UserViewModal(UserService.GetUserByID(id))
''# The UserViewModal will automatically do all the work to create the
''# Slug as well as other pertinent information
Return View(user)
It seems to me like the separation is a good thing, but still requires a butt load of time to build. Just wondering about the trade off benefit or if there is a better way to accomplish the same thing?