Basic DataPortal / Client/Server Guidance Required...

Basic DataPortal / Client/Server Guidance Required...

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


NickTaylor posted on Monday, August 17, 2009

Hi,

I wonder if anybody can help point me in the right direction as regards setting up the data portal in a simple client / server environment.

We are in the process of converting an internal business application from Visual Foxpro to DotNet, and we have used CSLA as our base framework. All DotNet code is WinForms based and we have not written anything using ASP, and have no requirement to have the application accessed externally via the web ( for the time being at least ).

Initially as we have begun coding, the DataPortal worked out of the box, and we left it alone as our priority was to migrate some key parts of our old application to the new DotNet environment. As the application has begun to take shape its clear that data access can sometimes be slow, and there is an obvious requirement to look at the configuration of the data portal, and ideally we would like to set the business objects up to be mobile, and have the data access layer running separately on the database server and not the client workstation. Presumably everything is running on the client machine at present, as all forms and business objects etc. are all compiled into one application.

There is a lot of information in the book regarding the data-portal, but I wondered if someone could help point me in the right direction as to how I set the data portal up and indeed how the application should be structured to get it to work as a true client / server application. I suspect its fairly straight forward, but some help would be greatly appreciated....

Many thanks,

Nick   

 

JonnyBee replied on Monday, August 17, 2009

Hi,

Look at the ProjectTracker solution the samples download for Csla.

In the app.config of the PTWin client you will find setting for RemotingProxy, WcfProxy and WebServiceProxy along with sample services (and the services contain almost no code to support MobileObjects).

/jonnybee

NickTaylor replied on Monday, August 17, 2009

Thanks,

I will take a look and let you know how I get on...

Nick

JoeFallon1 replied on Monday, August 17, 2009

NickTaylor:

As the application has begun to take shape its clear that data access can sometimes be slow...

FYI -the use of a remote DataPortal will slow things down even further. (Up to 50% slower, as I recall). You pay a performance penalty for the flexibility of using a remote portal which gains you scalability. There is always a tradeoff.

Also, virtually every complaint I have ever read about CSLA being "slow" has been due to coding errors on the part of the user. CSLA is faster than Datasets. CSLA can use ADO.Net dtaareaders which are the fastest possible way to extract data from a database. If something is slow you are probably doing something incorrectly. The #1 mistake is to fill the BO using *Properties* when in fact you should be using *Fields*. By filling the BO in the dataportal using properties you unnecessarily trigger a ton of events which are not needed (yet).

Joe

 

NickTaylor replied on Monday, August 17, 2009

Hmmm, perhaps I had better just close the book and go and do something else instead...! :o(

Remember too that I am not using SQL, I am actually accessing my FoxPro data via OleDb, and my business object DataPortal commands all use this methodology. I'm forced to go this route as we are migrating away from VFP one form at a time, and its very handy to be able to write new forms which access the existing VFP data.

Typically I find two distinct requirements for accessing data, 1) via the normal CSLA business objects, i.e BoCustomer.GetByID(string customerID), and 2). via queries which return datasets such as GetAllCustomers() or GetAllSuppliers(). These are returned as DataTables rather than populated list objects, however all these queries are routed through a single ExecuteQuery object which is essentially a CSLA command object which has a single static method i.e. GetDataTable(string sqlSelectCommand). This accepts the SQL command as its input parameter and returns me a DataTable. I use these read only DataTables all over the place for populating combos for example, and for driving navigation within my maintenance forms.

There are however many instances where I return a DataSet following the entry of some information from a user. For example the ShowPartTransactionHistory() form requires the user to enter a part number after which I return a datatable containing all part transactions against that specific part. Whilst the data returned may only be a few hundred records, the underlying datatable is pretty large ( 500K records + ), the form can appear go to sleep ( it actually goes blank ) for 5 seconds or so whilst the data is returned. This doesn't make for a good user experience, and I had assumed that adopting a proper client / server architecture would be the way to go and might resolve this problem.

Perhaps I need to re-evaluate the issue, as I don't want to slow the application even more. We typically only have 50-users accessing the system, so it's not as if we have hundreds of people all hammering the system at the same time.

Can I also ask that even though I am running C# Winforms code, I should be considering the DotNet remoting technology which according to Rocky's book I should be hosting via IIS and ASP.NET. Is that right or am I barking up the wrong tree ( again )...?!

Thanks,

Nick  

 

 

tmg4340 replied on Monday, August 17, 2009

How you host your remote DataPortal depends on a lot of factors.  The current "best practice" from Microsoft is to use WCF, which the DataPortal can happily accommodate.  And you can certainly use IIS and ASP.NET - that's probably the simplest hosting option, and the one you can probably get up and running the quickest (given that there are examples of doing so).  WCF configuration is more than a PITA, so any help you can get to get up and running will make your life easier.  Hosting via IIS isn't going to run any slower than the other hosting options.

In terms of improving your response time, Joe is right - adding a remote call is not going to make your app go faster.  You've added a whole new communication layer that requires that objects be serialized and de-serialized.  So if you're getting slow response now, going to an "n-tier" arrangement isn't going to make it faster.  What you're probably running into for speed is simply the amount of data you're dealing with.  In addition, since you're accessing FoxPro data, you probably aren't getting any help from any indexes you may have.  I have a limited amount of experience accessing dBase data in .NET, and IIRC indexes were pretty much ignored.  So your OLE DB driver is likely doing a table scan - and in the case of your transaction-history table, that's a lot of data to move through.

Outside of that, I'd also take Joe's recommendation to move my CSLA data-access code away from DataTables/DataSets and into DataReaders if possible.  You'll eliminate a data-transformation step - ADO.NET uses DataReaders internally, so your data path currently looks something like this: DataReader -> DataTable -> CSLA object.  If you cut out the middleman (DataReader -> CSLA object), you will also see some speed improvement.

Finally, you can look into asynchronous access.  Ultimately, all these suggestions aren't likely to get you the speed you need (though I'd still do as many of them as you can.)  So you need to move the data access off the main thread, so that your UI doesn't block while it waits for data.  The data won't return any faster, but the form won't freeze on you, so it will feel to the users like the app is more responsive.  There are a bunch of techniques for doing that, though in the WinForms world the easiest is probably to utilize the BackgroundWorker component.

HTH

- Scott

NickTaylor replied on Tuesday, August 18, 2009

Thank you both for your reply...

I'm certainly going to have a look at the BackgroundWorker component as I haven't used this before. There are only certain forms which are causing problems, and as you would expect they are the ones that are accessing data from within large tables.

Just to clarify the point regarding the data objects, typically each table in my FoxPro application now has an associated CSLA business object. Just to make matter worse our old application is written in FoxPro ver 2.6 which had no concept of a database, so all the tables are free tables thus eliminating the user of stored procedures etc. (ouch!). In fact I have a helper/developer routine that reads the table structure and then generates the basic business object class for me so I dont have to keep writing essentially the same code over and over. Every fetch/insert/update command has the same basic structure, typically for a fetch command something along the lines of:-

private void DataPortal_Fetch(Criteria criteria)

{

OleDbConnection connection = KELplanAPP.Connection;

connection.Open();

// Command object

OleDbCommand Command = new OleDbCommand(criteria.SelectCMD, connection);

OleDbDataReader dr = Command.ExecuteReader();

// Update BoUserTitle

dr.Read();

_key_id = (string)dr["key_id"];

_title_ref = dr["title_ref"].ToString().Trim();

_title = dr["title"].ToString().Trim();

_comments = (string)dr["comments"].ToString().Trim();

_active = (bool)dr["active"];

connection.Close();

}

So I am updating the BO straight from the datareader, and this should be in line with your suggestions (hopefully) ?

However when I just want to fetch some data for the purposes of populating a grid or allowing the user to browse something, I use my ExecuteQuery command object. Here I have no choice other than to return a DataTable as the datareader is active only while the connection is open isn't it ? The main methods looks like this:-

public static DataTable GetDataTable(string selectCMD)

{

ExecuteQuery result;

result = DataPortal.Execute<ExecuteQuery>(new ExecuteQuery(selectCMD));

return result.ResultData;

}

protected override void DataPortal_Execute()

{

// Open the conection

OleDbConnection connection = KELplanAPP.Connection;

connection.Open();

// Command object

OleDbCommand Command = new OleDbCommand(_selectCMD, connection);

OleDbDataReader dr = Command.ExecuteReader();

_dtResult.Load(dr);

_rowCount = _dtResult.Rows.Count;

connection.Close();

return;

}

So I dont think I can do anything else but return a DataTable object from this method. If i'm missing the point here let me know as I would be interested to learn if there is a better way of doing this...

Thanks again for your input...

Nick

 

JoeFallon1 replied on Tuesday, August 18, 2009

The first part looks fine.

The 2nd part with the Datatable does work but...

Why aren't you building a CSLA collection instead? A collection can be populated with a datareader which reduces the overhead of using a dataset. (Not a huge difference - but not zero either.) Also a CSLA collection could be readonly or read/write. Either way the contained object could have fewer fields than an entire table (maybe your datatable does not have all fields either.)

Finally, is there any way to get the command to use the Fox indexes? That could speed things up if you could avoid table scans. Google is your friend here.

Joe

 

NickTaylor replied on Tuesday, August 18, 2009

Ok Joe,

I will now launch myself into another discussion...

At present in addition to the normal CSLA business object ( one per table ), I have another set of objects that I call "entity" objects. A customer gets one, inventory gets one, a job gets one, a user gets one and so forth. Typically these "entity" objects contain lots of static methods that simply return datasets to my application. For example: Inventory.GetInventoryByPartNumber(string partNumber) looks like:

// Returns all inventory for the specified part number

public static DataTable GetInventoryByPartNumber(string partNumber)

{

string selectCMD = "SELECT Stkfile.key_id, Stkfile.issue_no, Stkfile.bin_id, Stkfile.bin_number, Stkfile.locn_code, " +

"Stkfile.lot_number, Warehous.title, Warehous.location, Stkfile.qty_in_stk, Stkfile.inspect_id " +

"FROM stkfile LEFT OUTER JOIN warehous ON Stkfile.locn_code = Warehous.locn_code " +

"WHERE Stkfile.our_partno = '" + partNumber + "' ORDER BY lot_number";

return ExecuteQuery.GetDataTable(selectCMD);

}

I think this feels right, and its fairly scalable as I can just keep slotting in new methods as and when required. I thought initially that having a collection object and subsequent child object type for each query might be more difficult to manage and hence I steered clear of that strategy. Is this the way you do it ?

I will have to do some more research on the Fox indexes issue. As you correctly point out a trawl around Google will probably throw something up...


Many thanks,

Nick 

ajj3085 replied on Wednesday, August 19, 2009

Well, to take full advantge of Csla, you shouldn't be returning any Sytem.Data classes to your UI layer; you should be returning Csla based objects instead. This will decouple your data access from the UI, which is exactly what you want.

Build the BOs according to what you need to fulfill a use case, and your UI should ONLY use those BOs to fulfull the use case.

If you do this, in the end you can then move away from Foxpro, and you'll only need to change code in your DataPortal_XZY methods... your UI will remain unchanged.

NickTaylor replied on Wednesday, August 19, 2009

Ok, I see the logic in that. I guess I am being a bit lazy by returning datatables rather than CSLA collections and this will inevitably catch up with me later down the line when I undoubtedly want to change my application. The CSLA collections should decouple the UI completely. Understood.

But all of the data I return via my datatable methods is read only. Its only used for displaying grid based data or for populating combo's etc. You are suggesting that every query requires a collection and associated business object. In the case of the GetAllCustomerSales() method for example then I would still require a collection class for this, and a bespoke business object which contains the relevant properties populated by the DataPortalFetch() method. As this is a reporting requirement only ( and not a true business object ) it wouldn't need any DataPortal_Insert() and DataPortal_Update() methods as they will never be used for the purposes of updating the underlying database.

Have I read you correctly...?

Thanks,

Nick 

 

ajj3085 replied on Thursday, August 20, 2009

Yes, thats absolutely correct. You have a use case which customers sales need to be returned and display, you should have some Csla business objects which can fulfill that use case. For this, you'd create subclasses of ReadOnlyBase and ReadOnlyListBase, which would only implement fetch methods.

The advantage is that it doesn't matter where your data comes from your BOs "know" where to get the data to assembly the model of the data. If you leave Foxpro, only the data access code in the BOs needs to change. Your UI can remain unaffected. Also, you can then throw away your UI, keeping your investment in the business layer. For example, if you had a WinForms csla app, and then want to move to WPF, you can do that without losing all your business logic. With your current setup, you likely have business rules in the UI, so any change to your database or UI can break the whole app.

Its unlikely the concept / use case around customers sales will change, but your UI can, and your database might.

Fintanv replied on Thursday, August 20, 2009

Just to add my 2 cents; If you use some sort of code generation tool then the creation of these read-only CSLA collection/child classes that map directly to your database, becomes a trivial exercise.  There are various tools out there with varying initial up-front investment (in money and setup), including CodeSmith and MyGeneration.

NickTaylor replied on Friday, August 21, 2009

Thank you again...

I realise that whilst I am part way to my n-tier structure, I am not where I should be and there is still too much sitting in the UI layer... I think the business list suggestion will also help rationalise some of my current object organisation which has become a bit messy in places. For examply my new CSLA CustomerSales object can have a bunch of static methods which return all sorts of different sales analysis as the underlying CSLA list properties will be the same in each case. Currently these sit as methods in a Customer "entity" object which is not really a true BO.

In respect of the code generation tools, then I wrote a FoxPro program which generates CSLA object code for each of my free tables. It works a treat. I will extend this to produce list objects too :o)

Many thanks again for your advice. I can see the logic of your thinking...

Nick 

ajj3085 replied on Wednesday, August 19, 2009

tmg4340:
In terms of improving your response time, Joe is right - adding a remote call is not going to make your app go faster.  You've added a whole new communication layer that requires that objects be serialized and de-serialized.  So if you're getting slow response now, going to an "n-tier" arrangement isn't going to make it faster.


Well, that's not necessarly true. Assuming your database server is not on the client machine, remoting or wcf CAN make the application faster. The reason is that db communication by its very nature is exteremly chatty. I hit this exact wall when some of my users moved to our other office location in the next town. The builders are linked via a T1, but performance from there was awful.

Enabling remoting + compression DID improve the speed of the application, at least in the network communication pieces.

Instead of sending 30 to 50 sql commands back and forth one by one, remoting sends a compressed stream, all the db access occurs on the app server, and then one more message is sent back with the new object graph.

Copyright (c) Marimer LLC