Of CollectionBases, Enumerations and DataBinding

  • Thread starter Thread starter Good Enchiladas
  • Start date Start date
G

Good Enchiladas

I have an object model that exposes a class inheriting from collection base.

I would like to make this class available to an ASP.NET data grid, such as
in the following code:

grid.DataSource = MyObjectModel.MyCustomers.Filter("Smith%")

The problem is that MyCustomers is a lazy loading property of MyObjectModel
that, by default, returns ALL customers. So
"MyObjectModel.MyCustomers.Filter("Smith%")" first does the lazy load of ALL
customers THEN loads the customers filtered to the names beginning with
"Smith." The undesired initial lazy load is wasted.

How do I prevent this initial lazy load when a Filter method is called as in
above, but still retain the ability to make the very clean syntax of a call
such as "grid.DataSource = MyObjectModel.MyCustomers" when I want all
customers?
 
Hi Good,

Just overload it,

Function() as dataset
Do your code without test
Function(byval search as string) as dataset
Do your code with test

I hope this helps?

Cor
 
if you are working w sql server make a stored proc that dous the filtering,
that way only the filtered data is sent. (you could do it as a sql string
from code but this gives troubles with ' ...

example of sp (i automized those things here would be to much work to do
this for 30 tables otherwise)
(and it's easyer to work w than it looks ;p )

/* Stored Procedure FilterColl tblDocument*/
CREATE PROCEDURE spFilterColltblDocument

-- FK tblDocuType.DOCTID
@docDOCTID int,
@blndocDOCTID bit,
-- FK tblDossier.DOSID
@docDOSID int,
@blndocDOSID bit,
@DocNr varchar(50),
@blnDocNr bit,
@docTotBedragExcl float,
@blndocTotBedragExcl bit,
@docTotBtw float,
@blndocTotBtw bit,
@docAlgOms varchar(2000),
@blndocAlgOms bit,
@docOref varchar(50),
@blndocOref bit,
@docUref varchar(50),
@blndocUref bit
As SELECT *
FROM tblDocument
WHERE (docDOCTID = @docDOCTID OR @blndocDOCTID = 0)
AND
(docDOSID = @docDOSID OR @blndocDOSID = 0)
AND
(DocNr like @DocNr OR @blnDocNr = 0)
AND
(docTotBedragExcl = @docTotBedragExcl OR @blndocTotBedragExcl = 0)
AND
(docTotBtw = @docTotBtw OR @blndocTotBtw = 0)
AND
(docAlgOms like @docAlgOms OR @blndocAlgOms = 0)
AND
(docOref like @docOref OR @blndocOref = 0)
AND
(docUref like @docUref OR @blndocUref = 0)

RETURN
GO

Public Function FilterDocument(Optional ByVal intdocDOCTID AS Integer = 0,
Optional Byval blnintdocDOCTID AS Boolean = False, Optional ByVal
intdocDOSID AS Integer = 0, Optional Byval blnintdocDOSID AS Boolean =
False, Optional ByVal strDocNr AS String = "", Optional Byval blnstrDocNr AS
Boolean = False, Optional ByVal dbldocTotBedragExcl AS Double = 0, Optional
Byval blndbldocTotBedragExcl AS Boolean = False, Optional ByVal dbldocTotBtw
AS Double = 0, Optional Byval blndbldocTotBtw AS Boolean = False, Optional
ByVal strdocAlgOms AS String = "", Optional Byval blnstrdocAlgOms AS Boolean
= False, Optional ByVal strdocOref AS String = "", Optional Byval
blnstrdocOref AS Boolean = False, Optional ByVal strdocUref AS String = "",
Optional Byval blnstrdocUref AS Boolean = False) as DataSet

Dim arParms() as SqlParameter = New SqlParameter(15) {}

arParms(0) = new system.Data.SqlClient.sqlParameter("@docDOCTID",
SqlDbType.int)
arParms(0).Value = intdocDOCTID
arParms(1) = new system.Data.SqlClient.sqlParameter("@blndocDOCTID",
SqlDbType.bit)
arParms(1).Value = blnintdocDOCTID

arParms(2) = new system.Data.SqlClient.sqlParameter("@docDOSID",
SqlDbType.int)
arParms(2).Value = intdocDOSID
arParms(3) = new system.Data.SqlClient.sqlParameter("@blndocDOSID",
SqlDbType.bit)
arParms(3).Value = blnintdocDOSID

arParms(4) = new system.Data.SqlClient.sqlParameter("@DocNr",
SqlDbType.varchar, 50)
arParms(4).Value = strDocNr
arParms(5) = new system.Data.SqlClient.sqlParameter("@blnDocNr",
SqlDbType.bit)
arParms(5).Value = blnstrDocNr

arParms(6) = new system.Data.SqlClient.sqlParameter("@docTotBedragExcl",
SqlDbType.float)
arParms(6).Value = dbldocTotBedragExcl
arParms(7) = new system.Data.SqlClient.sqlParameter("@blndocTotBedragExcl",
SqlDbType.bit)
arParms(7).Value = blndbldocTotBedragExcl

arParms(8) = new system.Data.SqlClient.sqlParameter("@docTotBtw",
SqlDbType.float)
arParms(8).Value = dbldocTotBtw
arParms(9) = new system.Data.SqlClient.sqlParameter("@blndocTotBtw",
SqlDbType.bit)
arParms(9).Value = blndbldocTotBtw

arParms(10) = new system.Data.SqlClient.sqlParameter("@docAlgOms",
SqlDbType.varchar, 2000)
arParms(10).Value = strdocAlgOms
arParms(11) = new system.Data.SqlClient.sqlParameter("@blndocAlgOms",
SqlDbType.bit)
arParms(11).Value = blnstrdocAlgOms

arParms(12) = new system.Data.SqlClient.sqlParameter("@docOref",
SqlDbType.varchar, 50)
arParms(12).Value = strdocOref
arParms(13) = new system.Data.SqlClient.sqlParameter("@blndocOref",
SqlDbType.bit)
arParms(13).Value = blnstrdocOref

arParms(14) = new system.Data.SqlClient.sqlParameter("@docUref",
SqlDbType.varchar, 50)
arParms(14).Value = strdocUref
arParms(15) = new system.Data.SqlClient.sqlParameter("@blndocUref",
SqlDbType.bit)
arParms(15).Value = blnstrdocUref



Return SqlHelper.ExecuteDataset(gstrCnn, CommandType.StoredProcedure,
"spFilterColltblDocument", arParms)

End Function
 
EricJ said:
if you are working w sql server make a stored proc that dous the filtering,
that way only the filtered data is sent. (you could do it as a sql string
from code but this gives troubles with ' ...

I am already using stored procedures to do the filtering.

And the idea of automatically generating CRUD stored procedures using
sysobjects and syscolumns is a great idea that I've already suggested to my
team. You are right, it IS very easy for everyone to do.

Thank you.
 
Just overload it,
Function() as dataset
Do your code without test
Function(byval search as string) as dataset
Do your code with test

Maybe code will help visualize what I'm trying to do.

Public Class MyObjectModel
Private _customers As MyCustomerCollectionBaseClass
'NOTE: MyCustomerCollectionBaseClass inherits from
CollectionBase

Public ReadOnly Property Customers()
Get
'Lazy load of all customers
If _customers Is Nothing Then
_customers.LoadAll()
End If
Return _customers
End Get
End Property

Public Function Filter(ByVal nameSearch As String) As Customers
'search login goes here -- uses stored proc, etc.
End Function

End Class

Maybe you are right. Maybe I should use a overloaded function instead of a
readonly property. I'll take a look at it.

I am just worried that I am going to lose the clean syntax of:
grid.DataSource = MyObjectModel.Customers() 'will the parentheses have
to be here?
but I guess:
grid.DataSource = MyObjectModel.Customers(CustomerFind.FindByName,
"Smith")
isn't too bad (CustomerFind.FindByName is an Enum value for a name-based
search). I guess I just preferred:
grid.DataSource = MyObjectModel.Customers.Find(CustomerFind.FindByName,
"Smith")
but this resulted in an initial load of all customers first then a filtered
load (using a stored proc).

Thanks.
 
not only sp's :p
also a complete base class calling the sp's containing elements of other
classes ..., a wrapper for the base class and a collection class based on
the wrapper :p. I was thinking on doing base manage forms 2 but that will be
for next time.

my programmers (and my boss) love it :)
 
Good,
How do I prevent this initial lazy load when a Filter method is called as in
above, but still retain the ability to make the very clean syntax of a call
such as "grid.DataSource = MyObjectModel.MyCustomers" when I want all
customers?
The only way I can think of off hand is to defer the lazy load within the
MyCustomers collection itself. Don't do it in the MyObjectModel.MyCustomers
property itself, but in each method of the MyCustomers class. I would
probably use a Strategy pattern (via delegates possible) to implement the
lazy load. The constructor of MyCustomers would set the delegate field to
the load routine, the last thing the load routine does is

The Filter property itself would not lazy load the collection itself.

Instead of (in addition to) the pattern you've started, I would consider
using the Finder Pattern as identified in Martin Fowler's book "Patterns of
Enterprise Application Architecture" from Addison Wesley.
http://www.martinfowler.com/books.html#eaa

The Finder pattern was identified as part of the Data Mapper Pattern
(http://www.martinfowler.com/eaaCatalog/dataMapper.html). I would implement
the Finder pattern as a Shared Property of the respective class.

Public Interface ICustomerFinder

' finds by primary key
Function FindByID(id As Integer)
' finds by filtering name
Function FindByName(name As String)

End Interface

Public Class Customer

Public Shared Readonly Property Finder As ICustomerFinder

End Class

The ICustomerFinder is setup for the Separated Interface pattern, where the
Data Mappers & Finders are in a separate assembly from the Domain (business)
classes.

The above allows you to:

' return a list of customers
grid.DataSource = Customer.Finder.FindByName("Smith%")

' return a single customer
aCustomer = Customer.Finder.FindByID(123)

Martin's book also covers a couple of Lazy Load options.

Hope this helps
Jay
 
Back
Top