Manager Classes that Extend Entity Classes

Posts   
 
    
GabeNodland avatar
Posts: 65
Joined: 31-Dec-2004
# Posted on: 16-Sep-2008 08:05:28   

I was just looking into a way to keep my business logic all in one place, and to be able to extend entities and I wonder what people think about using extension methods to do this.

extension methods would allow me to do this:


dim doc as DocumentEntity = documentManager.FetchByPrimaryKey(1)

If doc.IsInDocumentGroup(5)
    'do something
End If

rather than this:



dim doc as DocumentEntity = DocumentManager.FetchByPrimaryKey(1)

If DocumentManager.IsInDocumentGroup(doc,5)
    'do something
End If

Adding a method to an instance of an entity. Still you are limited to a static/shared method, just like in a manager class, however the syntax when calling is a bit clearer.

The reason i want to be able to do this, rather than adding to entities the dbgeneric project is quite similar to the reason we need IoC for validation of entities, this is business logic and you don't want it with the entities, and sometimes you want to make DB calls within your business logic.

Now here is the question that I have where is the line drawn. Should this only be used to extend the fields, or should it be used to extend actions like save and delete? Or are extension methods bad because they will limit some other functionality that I am not thinking about.

I create a manager class per entity. I auto generate:

Fetch
FetchUsingPrimaryKey
FetchUsingUniqueConstraint

FillFieldsMappedOnRelations     
'this allows you to do this documentManager.FillDocumentGroups(document)
'I call these postfetches (the lazy cousin of the prefetch)

Save
SaveCollectin
Delete
DeleteCollection

and then I define any custom methods that I need like


UserManager.Logout(user as userEntity)
UserManager.HasRight(user as userEntity, rightId as integer)

I think a lot of these could also be changed to extension methods so you could use:


user.Save
user.Delete

If user.Rights is Nothing 'postfetch / kindof lazyload
     user.FillRights()
End If

user.HasRight(18)
user.Logout

additionally if the extension attribute is applied directly to your shared/static manager functions you can call things both ways.

Anybody have any ideas on this?

Gabe

daelmo avatar
daelmo
Support Team
Posts: 8152
Joined: 28-Nov-2005
# Posted on: 18-Sep-2008 09:57:49   

Hi Gabe,

In my opinion, your approach make sense when using SelfServicing pattern, and you should be able to do that using the SelfServicingTwoClasses preset.

However, using Adapter pattern, I wouldn't recommend putting the functionality in entity extended objects.

Are you planning this for SelfServicing?

GabeNodland avatar
Posts: 65
Joined: 31-Dec-2004
# Posted on: 18-Sep-2008 19:33:50   

HI,

I am using adapter too, that is the part that is kind of cool.

It is basically just a short cut to your manager class, and all of your business logic is still where it is supposed to be. If you are passing the entities somewhere that doesn't have access to your manager dll the extended methods are not there. Additionally you only expose methods that someone accessing your manager classes could access anyhow.

So if you don't want them to delete an entity, delete is not there. and it is also not in your manager class.

so you can call

User.Logout()

UserManager.Logout(User)

or

User.Save(True)

UserManager.Save(User, True)

both calls use the exact same function in your manager class

you could also do this for fetch methods, like the self-service, but i think that syntax is a bit confusing.


dim u as new userEntity()
u.fetchByPrimaryKey(5)

to me this is clearer to someone reading the code.


dim u as new userEntity()
u= UserManager.fetchByPrimaryKey(5)

I have attached one of my manager classes

here is some code showing with and without exntesions

        'WITH EXTENSIONS
        Dim document = DocumentManager.FetchUsingPrimaryKey(1)
        'this fills the related Company Entity (postfetch)
        document.FillCompany()
        document.DocumentDescription = "new description" & document.Company.CompanyName
        document.Save(True)

        Dim documents = DocumentManager.FetchByPart(New PartEntity(5), True, True)
        For Each doc In documents
            Response.Write("<br>" & doc.SummaryHtml) 'this is nice
            doc.DocumentDescription = "change"
        Next

        docs.SaveAll(False)

        'WITHOUT EXTENSIONS
        Dim document = DocumentManager.FetchUsingPrimaryKey(1)
        'this fills the related Company Entity (postfetch)
        DocumentManager.FillCompany(document)
        document.DocumentDescription = "new description" & document.Company.CompanyName
        DocumentManager.Save(document, True)

        Dim documents = DocumentManager.FetchByPart(New PartEntity(5), True, True)
        For Each doc In documents
            Response.Write("<br>" & DocumentManager.SummaryHtml(doc))
            doc.DocumentDescription = "change"
        Next

        DocumentManager.SaveAll(docs, False)
Attachments
Filename File size Added on Approval
DocumentManager.vb 24,246 18-Sep-2008 19:34.46 Approved
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 19-Sep-2008 20:00:08   

Ok Gabe.. I need pick your brain if you allow me to wink

If I understand you right, you would extend the entities with methods that would have been in a manager class to start with. But because you are writing the shared (static) methods to be called in the extended syntax, the UI developer can only call these methods in the extended syntax not in the manager-class syntax. In a nutshell, you have turned the adapter architecture into self-service but with the added benefit that behavior (methods) live in a separate assembly (the BL).

Now I can imagine the argument would be, which syntax is cleaner. For me, I must put this to test in a real project to get "the feeling" of the syntax.

GabeNodland avatar
Posts: 65
Joined: 31-Dec-2004
# Posted on: 20-Sep-2008 01:41:54   

Omar,

The examples i listed above can be mix and match. once a method is set as an extension, it may be called either way. which ever you prefer.

The once the UI developer references the business layer the entities will all get the new methods, and they will be able to call the manager classes as well.

so with my current manager classes I can do this


dim user as UserEntity = UserManager.FetchByPrimaryKey(5)

response.write user.FullName() 'this is an extension my user class only has first and last

response.write UserManger.FullName(user) 'exact same call as above

user.FirstName="Otis"
user.Save(true) 'calls the extension method

user.FirstName="Frans"
UserManager.Save(user,true) 'again calls the save method

I think that adding these extensions gives the UI developers (me) an easy way to see things they can do with the entity, or entity collection. It doesn't take away anything, and seems to be a nice shortcut.

I have added this to my manager templates and I am using it on my current project.

So far I like it!

Also be sure to check out the manager class I attached in my last post, The forum doesn't make it easy to see there is an attachment if you don't know where to look.

Gabe

pat
User
Posts: 215
Joined: 02-Mar-2006
# Posted on: 19-Nov-2008 19:55:19   

Hi Gabe,

I like your approach to the manager classes. Is there any chance you could share your template to generate them so that I/we might be able to use it as a base?

Thanks a lot, Patrick pub # patrickwolf@net

Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 27-Jan-2009 00:02:31   

pat wrote:

Hi Gabe,

I like your approach to the manager classes. Is there any chance you could share your template to generate them so that I/we might be able to use it as a base?

Thanks a lot, Patrick pub # patrickwolf@net

I would also like to get these templates.

Can you provide them?

Thanks,

Fishy

Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 03-Feb-2009 00:01:33   

Well, I did not receive anything from Gabe. So, I was wondering if anyone else had some Manager Templates for Adapter/VB/Linq.

Thanks,

Fishy

GabeNodland avatar
Posts: 65
Joined: 31-Dec-2004
# Posted on: 04-Feb-2009 22:51:29   

Pat & Fishy

Sorry for the slow reply, I haven't been working on the LLBL stuff too much lately. However I will share my manager template file.

I really want to rework a lot of this, there are a lot of functions that are generated that are there from a previous version of my template. I would like to yard a lot of this stuff out. Having LINQ makes most of this stuff unnecessary since now you can get data so easily.

I would like to say that the Fill methods are one thing I really like about my template. It is very simple but gives you the option to do "on demand lazy Loading" Like This:

OrderHeader.FillOrderDetails or

If OrderHeader.Account is Nothing Then OrderHeader.FillAccount End If

Also i changed my managers from a classes to a modules so i could extend methods without having to write a wrapper function in an extension module. I feel like this may have been a mistake. I would like the ability to have a manager base to handle some of the common stuff. As it stands this code is duplicated, which i feel causes a bit of code bloat and slow compile time.

The biggest problem I have is when to allow access. For example I don't want to allow OrderManager.Save(orderEntity) out of the box, I want to be able to control this. To solve this problem I renamed my Save to SaveEntity and made it private. In order to call this in the UI , i Define a new function in my usercode area called Save, and it calls SaveEntity.

When i get a chance I am going to move to a shared/static manager base class, and have each manager as a shared class. Then create a companion Extension Module for each Manager. I'm not exactly sure but here are some of the options.

ManagerBase <-- OrderManager + OrderManagerExtensions this model the custom/modified code would go directly in my OrderManagerClass within a defined code region.

the other option i am thinking may be to do:

ManagerBase <-- OrderManagerBase <-- OrderManager + OrderManagerExtensions

the OrderManagerBase would be generated, and the OrderManager woudl be just user code. This would allow for the ability to allow or disallow calls to private / public base methods.

or I may just move out a lot of the stuff to a Manager Helper class and add wrapper calls to the save delete and methods that I want to make public.

I am sure there are more options i haven't had much time to think about it yet. I would appreciate any design ideas if you have them

Gabe

Attachments
Filename File size Added on Approval
ManagerTemplateFiles.zip 3,794 04-Feb-2009 22:51.46 Approved
Fishy avatar
Fishy
User
Posts: 392
Joined: 15-Apr-2004
# Posted on: 04-Feb-2009 23:00:49   

Thanks for replying. I have made a little progress by taking what you had as an example. Here is the code which I have not yet created a template for:

Imports MedfordSchoolDistrict.Sis
Imports MedfordSchoolDistrict.Sis.EntityClasses
Imports MedfordSchoolDistrict.Sis.FactoryClasses
Imports MedfordSchoolDistrict.Sis.HelperClasses
Imports MedfordSchoolDistrict.Sis.DatabaseSpecific
Imports MedfordSchoolDistrict.Sis.Linq.LinqMetaData
Imports SD.LLBLGen.Pro.ORMSupportClasses
Imports SD.LLBLGen.Pro.LinqSupportClasses
Imports MedfordSchoolDistrict.SisBl.LLBLGenProQueryExtender

Public Class StudentMstrManager

    Public Enum PrefetchItemEnum
        None
        Attendance
        StudActive
        StudData
    End Enum

    Private Shared Function CreatePathEdges(ByVal ParamArray prefetchItem() As PrefetchItemEnum) As IPathEdge()
        Dim wp As New List(Of IPathEdge)

        For Each pa In prefetchItem
            Select Case pa
                Case PrefetchItemEnum.Attendance : wp.Add(New PathEdge(Of AttendanceEntity)(StudentMstrEntity.PrefetchPathAttendance))
                Case PrefetchItemEnum.StudActive : wp.Add(New PathEdge(Of StudActiveEntity)(StudentMstrEntity.PrefetchPathStudActive))
                Case PrefetchItemEnum.StudData : wp.Add(New PathEdge(Of StudDataEntity)(StudentMstrEntity.PrefetchPathStudData))
            End Select
        Next

        Return wp.ToArray
    End Function

    ''' <summary>
    ''' Fetches the entity by the primary key along with specified
    ''' Prefetch Items.
    ''' </summary>
    ''' <param name="studentId"></param>
    ''' <param name="prefetchItem"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FetchEntityByPrimaryKey(ByVal studentId As String, ByVal ParamArray prefetchItem() As PrefetchItemEnum) As StudentMstrEntity

        Dim meta As New Linq.LinqMetaData(New DataAccessAdapter)

        Dim q = From d In meta.StudentMstr Where d.StudentId = studentId Select d

        Dim c = q.WithPath(CreatePathEdges(prefetchItem)).ToEntityCollection(Of StudentMstrEntity)()

        If c.Count > 0 Then Return c(0)

        Return Nothing
    End Function


    Public Shared Function FetchCollection(ByVal prefetchItem As PrefetchItemEnum) As EntityCollection(Of StudentMstrEntity)

        Dim meta As New Linq.LinqMetaData(New DataAccessAdapter)

        Dim q = From d In meta.StudentMstr Select d

        Select Case prefetchItem
            Case PrefetchItemEnum.StudData
                q = q.WithPath(New PathEdge(Of StudDataEntity)(StudentMstrEntity.PrefetchPathStudData))
        End Select

        Return q.ToEntityCollection(Of StudentMstrEntity)()

    End Function

End Class

I want to add additional Fetches (Unique Contraints, Predicates) but have not done so yet.

My biggest struggle right now is with Template Studio, but I am hoping that I will figure out how to use it properly by the end of the day. If you could attach your template files, that may help me finish it up.simple_smile Thanks,

Fishy

[Edit] I just saw your attachment and is pending approval. Thanks simple_smile