Peer review of my DataPortal code required

Peer review of my DataPortal code required

Old forum URL: forums.lhotka.net/forums/t/6303.aspx


sroberts posted on Saturday, January 31, 2009

Can someone help me?

I'm a pre-version 3.0 CSLA'er attempting to come to the brave new world of v3.6.1 with managed properties and lazy loading…

I would very much appreciate (I can beg if needed) a peer-review on a some excerpts from my DataPortal code using the traditional (Pre-LINQ) method of data access with SQL server;

The excerpts below are from a Quotation (BusinessBase) object that is parent to a QuotationItems (BusinessListBase) collection, I’ll just get the code listed first and then ask my questions at the end, please paraphrase through the code, it’s not 100% complete as I’ve chopped a lot of property loading/saving, the examples given should be enough for someone to tell me if I’m on the right track (or not…)

Child Property Definition/Accessor

    Private Shared QuotationItemsProperty As PropertyInfo(Of QuotationItems) = _
        RegisterProperty(Of QuotationItems)(New PropertyInfo(Of QuotationItems)("QuotationItems", "QuotationItems"))
    Public ReadOnly Property QuotationItems() As QuotationItems

        Get
            If Not FieldManager.FieldExists(QuotationItemsProperty) Then _
                LoadProperty(Of QuotationItems)(QuotationItemsProperty, QuotationItems.GetQuotationItems(Me.QuotationID))
            Return GetProperty(Of QuotationItems)(QuotationItemsProperty)
        End Get

    End Property


DataPortal methods

   <RunLocal()> _
    Protected Overrides Sub DataPortal_Create()

        'CREATE NEW OBJECT WITH DEFAULT VALUES
        LoadProperty(Of Integer)(HighestRevisionProperty, 1)
        LoadProperty(Of CreatedUpdated)(CreatedUpdatedProperty, New CreatedUpdated)
        LoadProperty(Of Integer)(CustomerIDProperty, -1)
        LoadProperty(Of Integer)(ContactIDProperty, -1)
        LoadProperty(Of String)(CompanyNameProperty, "")
        LoadProperty(Of String)(ContactNameProperty, "")
        LoadProperty(Of Decimal)(DiscountProperty, 0)
        LoadProperty(Of DeliveryAgent)(DeliveryAgentProperty, New DeliveryAgent(-1))
        LoadProperty(Of Decimal)(DeliveryCostProperty, 0)
        LoadProperty(Of QuotationItems)(QuotationItemsProperty, Nothing)
        ValidationRules.CheckRules()

    End Sub

   Private Overloads Sub DataPortal_Fetch(ByVal dr As SafeDataReader)

        'CREATE NEW OBJECT USING DATA PASSED IN
        With dr
            .Read()
            LoadProperty(Of Integer)(HighestRevisionProperty, .GetInt32("HighestRevision"))
            LoadProperty(Of CreatedUpdated)(CreatedUpdatedProperty, New CreatedUpdated(dr))
            LoadProperty(Of Integer)(CustomerIDProperty, .GetInt32("CustomerID"))

etc, etc...


        End With
        LoadProperty(Of QuotationItems)(QuotationItemsProperty, Nothing)

   End Sub

   Private Overloads Sub DataPortal_Fetch(ByVal criteria As Criteria)

      Using cn As New SqlConnection(BSM.BLL.Common.Security.Login.ConnectionString)
               cn.Open()
               Using cm As SqlCommand = cn.CreateCommand
                   cm.CommandType = CommandType.StoredProcedure
                   cm.CommandText = "vsp_GetQuotation"
                        cm.Parameters.AddWithValue("@QuotationID", Criteria.QuotationID)
                   Using dr As New SafeDataReader(cm.ExecuteReader)
                       DataPortal_Fetch(dr)
                   End Using
               End Using
           End Using

   End Sub

   Protected Overrides Sub DataPortal_Insert()

       DoInsertUpdate()

   End Sub

   Protected Overrides Sub DataPortal_Update()

       DoInsertUpdate()

   End Sub

   Private Sub DoInsertUpdate()

       If MyBase.IsDirty AndAlso MyBase.IsSavable Then
            Using cn As New SqlConnection(BSM.BLL.Common.Security.Login.ConnectionString)
                cn.Open()
                Using cm As SqlCommand = cn.CreateCommand
                    cm.CommandText = "vsp_StoreQuotation"
                    With cm
                        .CommandType = CommandType.StoredProcedure
                        .Parameters.AddWithValue("@QuotationID", ReadProperty(Of BSM_ID)(BSM_IDProperty).ThisID)
                        .Parameters.AddWithValue("@CustomerID", ReadProperty(Of Integer)(CustomerIDProperty))
etc, etc...

                        'IF INSERTING A NEW RECORD THEN UPDATE THE PRIMARY RECORD ID TO BE THAT AS RETURNED BY THE STORED PROCEDURE

                        If ReadProperty(BSM_IDProperty).ThisID < 0 Then
                            Dim pResult As SqlDataReader = .ExecuteReader
                            If pResult.Read Then
                                ReadProperty(BSM_IDProperty).ThisID = CInt(pResult(0))
                            End If
                        Else
                            .ExecuteNonQuery()
                        End If
                    End With
                End Using
                cn.Close()
            End Using
        End If

        DataPortal.UpdateChild(ReadProperty(Of QuotationItems)(QuotationItemsProperty), Me)

    End Sub


Questions

1) Am I initialising, loading and saving properties correctly in the Create/Fetch and InsertUpdate methods? (Ignore child object access, that’s Q2 below)

2) All of the lines in blue deal with initialisation, loading and saving of the QuotationItemsProperty (my child object collection), given I want to use lazy loading, I wasn't sure if I should be setting it to Nothing or something else initially….

3) QuotationID is an identity column in the SQL databse and so in my Stored Proc I'm doing a SELECT @@SCOPE_IDENTITY to return the new QuotationID on an INSERT, the lines in orange deal with getting this new identity value back into the object. Then when I call UpdateChild, I pass the Quotation object (me) to it as a parameter which in turn uses the new QuotationID for updating into the child object records as I save each; I assume this is the correct way to handle foreign key updates where the parent is a new entry in the database?

Finally, a plea to Rocky…It seems that in the latest 2008 C# book, the traditional data access technique (ADO.Net + SQL Server) have pretty much been put aside in favour of LINQ, which is fine but a couple of examples of retrieving and saving managed properties using the old (!) method would be nice, also if one of the child classes in the sample project tracker could be written to use the same it would be help us newbies upgrading to 3.6.1

As a side issue, I do now worry that I am perhaps in a minority of people using CSLA and ADO.Net with SQL Server and that I must learn LINQ as quickly as I can to stay in step with Rocky's progress or maybe I should be learning about the ADO.NET Entity Framework
....Smile [:)]

JoeFallon1 replied on Saturday, January 31, 2009

I do not think you are in the minority with ADO.Net.

Look what happened to LINQ To SQL since the book came out - MS "killed" it already. Well they stopped working on it anyway. EF is just the next greatest technology from MS.

I plan to stick with "raw" ADO.Net into the future as it should be the underpinnings of whatever MS comes up with next. Besides not every database is designed using relationships etc so some of these technologies can't be leveraged as much as you might want.

I have not made the move to Managed properties yet so I won't try to respond to your other comments.

Joe

 

 

RockfordLhotka replied on Saturday, January 31, 2009

sroberts:

As a side issue, I do now worry that I am perhaps in a minority of people using CSLA and ADO.Net with SQL Server and that I must learn LINQ as quickly as I can to stay in step with Rocky's progress or maybe I should be learning about the ADO.NET Entity Framework
....Smile [:)]

I took a gamble on L2S with the book, and lost. But I really couldn't win. ADO.NET EF wasn't released yet, so I really couldn't use it (and I find version 1 to be iffy anyway). And I personally use raw ADO.NET as a general rule.

But if the book didn't use "current" technology then some people would see the book (and/or CSLA) as irrelevant because it uses "old" technology.

The reality is that the ADO.NET code in ProjectTracker 3.0 is the same for 3.6. The data portal hasn't changed that radically. The only difference is the new Child_XYZ methods. You had to write those before too - but you got to pick the method name. Now the data portal dictates the method name (and reduces the code you have to write), but the ADO.NET parts should be unchanged.

(except that you probably want to use the ConnectionManager class now, as that makes things a lot easier)

rsbaker0 replied on Saturday, January 31, 2009

I digress, but one of the main lessons I took from the 2005  book is never to depend on Microsoft sticking with any particular data access technology. Our legacy app we are replacing first used ODBC, then DAO was the greatest thing since sliced bread (sorry, I'm dating myself ;), then we went back to ODBC for SQL Server and Oracle. Then Microsoft came out with .NET and essentially orphaned the entire C++ community and also moved to dissimilar data access technologies (e.g. ADO.NET, which really doesn't even have anything resembling a fully scrollable result set).

This is exactly why I went with an ORM (which I'm really just using as ADO.NET SQL generator). It supports some 12 different back-end databases (of which we currently support 4 or so), and CSLA was easy to bolt on top of it. When we can't use it anymore, I can swap it out with something else without having laced the application with some technology that MS decides is now obsolete (although I suppose that can and will eventually happen with C#, etc.).

I really liked that CSLA 2.x was more data layer agnostic, but it is certainly true that the new L2S features built in are a convenience that you can choose to use or not, and you can still use whatever data access method you want. You can (and should IMHO) design your app so that the DAL is swappable and you can replace it with something else down the road without a complete rewrite.

RockfordLhotka replied on Saturday, January 31, 2009

Do you really think v2.x was more data layer agnostic?

 

Certainly I’ve added some optional helpers to deal with ADO.NET, LINQ to SQL and ADO.NET EF, but I’ve also added ObjectFactory which opens up whole new avenues of being DAL agnostic. And all the 2.x concepts work as always.

 

Rocky

 

 

From: rsbaker0 [mailto:cslanet@lhotka.net]
Sent: Saturday, January 31, 2009 5:49 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Peer review of my DataPortal code required

 

I digress, but one of the main lessons I took from the 2005  book is never to depend on Microsoft sticking with any particular data access technology. Our legacy app we are replacing first used ODBC, then DAO was the greatest thing since sliced bread (sorry, I'm dating myself ;), then we went back to ODBC for SQL Server and Oracle. Then Microsoft came out with .NET and essentially orphaned the entire C++ community and also moved to dissimilar data access technologies (e.g. ADO.NET, which really doesn't even have anything resembling a fully scrollable result set).

This is exactly why I went with an ORM (which I'm really just using as ADO.NET SQL generator). It supports some 12 different back-end databases (of which we currently support 4 or so), and CSLA was easy to bolt on top of it. When we can't use it anymore, I can swap it out with something else without having laced the application with some technology that MS decides is now obsolete (although I suppose that can and will eventually happen with C#, etc.).

I really liked that CSLA 2.x was more data layer agnostic, but it is certainly true that the new L2S features built in are a convenience and you can (and should IMHO) design your app so that the DAL is still swappable and you can replace it with something else down the road without a complete rewrite.



sroberts replied on Sunday, February 01, 2009

Thanks for the responses so far, I'm reassured that my ADO.NET to SQL skills are still relevant. Although I still can't find any explicit examples of using ADO.NET and SQL with the new managed properties way of doing things....(remember I'm coming from a CSLA v2 world)

If someone could answer my original 3 questions, even if it's just a simple; yes, yes, yes, that would be very much appreciated and I can move on to my next issues (data binding a child collection....a new post I think.)

Rocky,

Do you intend removing the LINQ code in the project tracker sample and perhaps put in ADO.NET SQL type data access with the managed properties

RockfordLhotka replied on Sunday, February 01, 2009

There are a couple ways to get/set the property values.

 

One is to use ReadProperty() and LoadProperty():

 

LoadProperty(IdProperty, dr.GetInt32(“Id”));

 

cm.Parameters.AddWithValue(“@id”, ReadProperty(IdProperty));

 

If you’d like, you can replace the ReadProperty() values with the use of BypassPropertyChecks:

 

using (BypassPropertyChecks)

{

  cm.Parameters.AddWithValue(“@id”, Id);

}

 

And if you always have a set block on your properties (use a private set block for read-only properties) then you can use BypassPropertyChecks for loading properties too:

 

using (BypassPropertyChecks)

{

  Id = dr.GetInt32(“Id”);

}

 

The results are the same regardless, so it is really a matter of style and what you find simpler. I tend to use BypassPropertyChecks these days – though I still need to use LoadProperty()/ReadProperty() in some cases, because I don’t always have accessible property get/set blocks.

 

Rocky

rsbaker0 replied on Sunday, February 01, 2009

RockfordLhotka:

Do you really think v2.x was more data layer agnostic?

Certainly I’ve added some optional helpers to deal with ADO.NET, LINQ to SQL and ADO.NET EF, but I’ve also added ObjectFactory which opens up whole new avenues of being DAL agnostic. And all the 2.x concepts work as always.

Rocky

Well, maybe this was a slight overstatement, but after our last discussion of transaction management and the CSLA ContextManager, my inital reaction was "Great, maybe I can use that instead of my home-grown solution". Then I went and looked at it, and it turned out (at least to my first read) that it's only useful if your using Linq.

RockfordLhotka replied on Sunday, February 01, 2009

There are several manager classes:

 

ConnectionManager (for any ADO.NET model)

ContextManager (for L2S)

ObjectContextManager (for ADO.NET EF)

 

My guess is that you just looked at ContextManager, which is L2S only.

 

But ConnectionManager manages a normal ADO.NET connection object, so any DAL that uses a connection may be able to use it. Certainly if you create your own ADO.NET based DAL, you could use it within your DAL. Or if you are using a DAL based on some other technology, the only requirement is that you be able to pass the connection into the DAL rather than having the DAL open its own connection.

 

For example, L2S will automatically open a connection to do a query. But you can, if you want, give it a pre-opened connection before doing the query, and it will use that pre-opened connection. Most Microsoft data access technologies work that way, because they want to leave the flexibility for you to open and manipulate the connection, and still use that connection for working with things like L2S or EF or a TableAdapter.

 

Rocky

sroberts replied on Monday, February 02, 2009

Thank you for your replies so far Rocky.

With regard to ConnectionManager, I must admit I've not read up on that helper as yet, currently as you can see I create a SqlConnection and command object on the fly in the DataPortal methods using a connection string held by my Authentication module (the user has to use a login screen to connect to the server/database, this builds the connection string and then makes it available to the whole engine). I'll look into ConnectionManager to see what benefits that offers.

I'm almost 100% happy that my property loading/unloading methods are correct now....however the final issue I would like resolving is concerning the initialisation/loading/updating of the child object collection, which are the lines in blue concerning QuotationItems.

Am I correct to CREATE and FETCH this property as Nothing ? (I did this because I didn't want an 'empty' list nor did I want to have to retrieve the data until it was actually called for via the property Get.


rsbaker0 replied on Monday, February 02, 2009

sroberts:
...With regard to ConnectionManager, I must admit I've not read up on that helper as yet, currently as you can see I create a SqlConnection and command object on the fly in the DataPortal methods using a connection string held by my Authentication module (the user has to use a login screen to connect to the server/database, this builds the connection string and then makes it available to the whole engine). ...

I'm curious -- since the login screen is presented on the "client" side of the data portal, how are you getting this information to over to your module so that it is accessible on the "server" side, where the connection is actually made?

sroberts replied on Monday, February 02, 2009

That's a good question and one that I suspect I haven't had to deal with as yet, everything runs on the client with not remote dataportal.

So I guess the scope of the application allows access to my authentication library instance and hence the connection string it maintains.

I do need to review my data connection methods to ensure this app is future proofed for a possible client/server setup.

Thanks for pointing that out.

RockfordLhotka replied on Monday, February 02, 2009

So you want a lazy loaded child object?

 

In that case you simply ignore that child field/property in the create/fetch process. And you include code in your property to get the child object on-demand:

 

public ChildType Child

{

  if (!FieldManager.FieldExists(ChildProperty))

    LoadProperty(ChildProperty, ChildType.GetChild(this.Id)); // pass params necessary to identify child

  return GetProperty(ChildProperty);

}

 

Then in your insert/update/delete methods, you need to use the FieldExists() method to determine if the child is there, and if so, then insert/update/delete its data. If you use FieldManager.UpdateChildren() to update your child objects it will do the FieldExists() check for you.

 

Rocky

 

 

From: sroberts [mailto:cslanet@lhotka.net]
Sent: Monday, February 02, 2009 4:22 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: Peer review of my DataPortal code required

 

Thank you for your replies so far Rocky, I'm almost 100% happy that my property loading/unloading methods are correct now.

However the final issue I would like resolving is concerning the initialisation/loading/updating of the child object collection, which are the lines in blue concerning QuotationItems.

Am I correct to CREATE and FETCH this property as Nothing ? (I did this because I didn't want an 'empty' list nor did I want to have to retrieve the data until it was actually called for via the property Get.


rfcdejong replied on Monday, February 02, 2009

Sorry, maybe offtipic but..

That reminds me:

Using the ObjectFactory i can't access the FieldManager.FieldExists() that easy.
PS: Yes, i'm using lazy loading combined with the ObjectFactory, it works perfectly for fetching.

Copyright (c) Marimer LLC