wwBusinessPro – Article 2: Configuring Child classes (wwBusProItemList)

Note: This post if part of a complete series on the West Wind wwBusiness library.
Click here to see the full index

Another enhancement I added to wwBusinessPro was to allow for Child record collections to be easily used with a Parent BO.

When using a wwBusProParent, you set a few properties on the Parent that tell it about the Child class. Then, the Parent will create the Child class for you. So, when you’re working with your BO’s in code (after everything is all configured), you do not create the Child, you let the Parent do that. You will create a VCX library and add a Child class for each Child collection in there (based on wwBusProItemList), with its Child properties already set up. This allows the Parent to create the Child when he needs it. So you can see how this works, if you were to follow the path of the Get() method (remember, this is a key extension method I added in wwBusProParent), you will see that the Parent creates the Child object and attaches it to its oLineItems property. So, once your Child classes are configured in a VCX, you will not actually have to write any code to access them (unless you are doing some special case, custom coding), you just set up the Parent so he can find them. The Parent will handle all that for you, including the fetching of the related Child records.

Code Sample: Creating and using your first Parent Business Object

Parent Setup:

When using a wwBusProParent, you set properties on the Parent that tell it about the Child class.
Then, the Parent will create the Child class for you.
You do not create the Child when writing your code, let the Parent do that.

You need to create a VCX library and put a Child class for each Child collection in there,
with its Child properties already set up. This allows the Parent to create the Child when he
needs it. If you follow the path of the Get() method, you will see that the Parent creates a
Child class object and attaches it to its oLineItems property. So, once your Child classes are
configured in a VCX, you will not actually have to write any code to access them (unless you are
doing some special case, custom coding), you just set up the Parent so he can find them.
The Parent will handle all that for you, including the fetching of the related Child records.

You can also put your Parent objects in the VCX and set their properties in the VCX file.
This sample code is shown here simply to show which properties are required to make it all work.

Child Setup

When setting up your Child in the VCX, these are the properties you need to set on it:

cFilename (Required. Name of the dbf or Sql Server table)
cAlias (Optional. Will use cFilename by default)
cPKField (Must be an Integer. See “Important Note” below)
cFKField (Must be an Integer. Maps to Parents PK field)
cLookupField (Optional. Maps to Parents cLookupField. Use when Parent uses for String lookups)
cOrderFld (Optional. Can be used to set the ordering of child records. i.e. a sequnce field)

These properties provide the info needed by the LoadLineItems() method work out of the box.
Sometimes you may want to overwrite the LoadLineItems() method with custom code, but the default
should work in basic configurations.

IMPORTANT NOTE:
In order to save changes on the Child records, they need a unique PK that can be used. See cPKField.
String cLookupField works fine to load the records in, but it does not guarantee uniqueness when
trying to save changes back.

Again, you can use cLookupField Stings for loading the records, but you must have Integer PKs
on your Child records to allow saving changes back.

wwBusinessPro – Article 1: String Lookups

Note: This post if part of a complete series on the West Wind wwBusiness library.
 

I’ve written an extension library on top of the West Wind wwBusiness classes. You can read the original post on my work here.  In my enhanced library, which I’ve named wwBusinessPro!, one of the things I’ve added is a lookup method that will handle String ID lookup values in addition to the native handling of Integer PK’s. The base wwBusiness library only works with Integer PK lookups, and I needed an enhancement to work with String lookups to get started with wwBusiness in my app.

When using my wwBusinessPro class, the main method that you call to do a lookup is the Get() method. You will call this method even when doing a Integer PK lookup because it will expose all the other extensions I’ve added to the library (more on that later).

So, the Get() method accepts one parameter which can be an Integer or a String.

    oJob.Get(123) && An Integer PK lookup

     – Or –

    oJob.Get(‘123’) && A String lookup

Now, on each Business Object class that you create, you need to specify the following properties to allow any kind of lookup to work. (This is how the base wwBusiness functions, so this is not a new feature in wwBusinessPro!, except that I have extended it to work with String values too). The cPKField setting is required for all cases, and cLookupField is only required for String-based key values, in which case you must specify the same column names for both properties.

cPKField (This property is required. Choose either and Integer Pk column or a String key value column)

For Integer PK’s set this value to the name of the Integer PK column in the table. (basic wwBusiness feature)

For String PK’s set this value to the name of the String key value column in the table. (new feature)

Note: this key value is what will be used to locate your record when saving changes to existing records. For String key values, be sure you do not use Set Exact Off or else you may update the wrong record based on a partial string match.

cLookupField (optional)

Leave blank if you do not have a String value lookup field. (This is an extension property that I added to handle String lookups.)

For example, in an oJob class, you would set the cLookupField to the name of the column that has the string lookup key in it. For my Job table, the cLookupField property is set to ‘job_num’ which is a string column. Now it knows to look for the string value ‘123’ in the ‘job_num’ column. I also have Integer PK’s on that same table, so I also set the cPKField property to ‘ipkey’. Now my processing can also be done by the PK Integer value, if I know it. So I can now do lookups either by Strings or Integer PKs.

How it works:

The Get() method will determine what kind of lookup you are trying to do based on the data type of the value you pass in (integer or string) and will make the correct method call to other methods to do the actual lookup, all of which eventually drill down into the base wwBusiness class to do the lookup. Just pass in a string or an integer and it will happen like magic. You can then check the oJob.lFound property to determine if the requested record was found.

Here’s the code for the Get() method:

image

 

More about string lookups:

For string lookups, if you need more flexibility on the cLookupField, you can bypass the Get() method and use a lower-level lookup that accepts an alternate lookup column. It’s called LoadByRef() and accepts a string lookup value and a column name in which to perform the lookup. Like this:

oJob.LoadByRef(‘53443’, ‘po_num’) && Load a job by the po number.

And, to take it one step further, there is one more way you can further control the lookup, and that is by setting a property called cAdditionalFilter. Rather than accepting it as a parameter into the LoadByRef() method, it works as a property that you set before calling the method. This powerful feature allows, for example, find the first record with Status=”A” for Customer “DEN001”. You will just have to be creative and explore all the ways this method can be used. For DBF mode, the base wwBusiness method uses the LOCATE command, so the lookup is from the top down, so it will find the first record. For Sql Server mode, you can even include an Order By clause at the end of the cAddtionalFilter to all effectively working from the bottom up.

Example:

    oJob.cAdditonalFilter = ‘Status = [A]’
    oJob.LoadByRef(‘DEN001’, ‘cust_num’)

You can use any complex expression you want to in the cAdditionalFilter property:

    oJob.cAdditonalFilter = ‘Status = [A] and Amount>1000’

Remember, in my methods, I am still eventually using the base wwBusiness methods to do the work, it’s just that I have built a wrapper around it to make it easier to use.

Here’s the method code for LoadByRef()

image

 

 

Note: This post if part of a complete series on the West Wind wwBusiness library.

Exploring my wwBusiness extension library

Updated 02/01/2010 1:26 pm

I’ve written an extension library on top of the West Wind wwBusiness classes for object oriented data access in Visual FoxPro, which I’ve named wwBusinessPro. In this blog series I will explain some of the basics about the base West Wind wwBusiness data access library and my extension library wwBusinessPro, which is built on top of it.

You can read the wiki pages  and download the wwBusinessPro library from the BitBucket repository.

I’ve prepared several articles explaining some basics of the main wwBusiness library from West-Wind Technologies and how my wwBusinessPro extension library adds support for String lookups and Parent-Child data models for your applications.

It’s not hard to get started… I recommend this strategy:

1. Before you try to jump in, take time to read each article listed below.

2. When you are ready to start, follow the instructions in the post Setting up wwBusinessPro.vcx to work with wwBusiness.vcx

3. Now create a new class library for your Business Objects and add a Parent BO class inherited from wwBusProParent, and one or more Child BO classes (ones that are related to the Parent you created) which inherit from the wwBusProItemList class.

4. Next, add data rows to the wwBusinessObjects.dbf table to setup your Parent and Child business objects. (see wiki page for screenshot). There will be one row for the Parent, and a row for each Child that relates to the Parent.

Now you’re ready to go… With a Parent and a Child BO properly configured, you can actually drive the whole thing from the Command Window, which is a good way to play around with it and see it work. See this code sample for a typical code snippet that is used to work with BO’s. After a little testing you’ll be ready to create (or modify) your forms to use the Parent/Child BO pair. It’s not all that hard, you just need a simple get-started case to help you see it all come together.

Articles

Intro and overview articles:

Read my original review of the West Wind wwBusiness library here 

Article 1 – String Lookup enhancements in wwBusinessPro

Article 2 – Parent-Child enhancements in wwBusinessPro

 

Specific Steps to use wwBusinessPro BOs in your code and forms:

Step 1 – Setting up wwBusinessPro.vcx to work with wwBusiness.vcx

Step 2 – Creating and using your first Parent Business Object (wwBusProParent class)

            (also see this Help entry from the base wwBusniess Help File)

Step 3 – Setting up Child cursors with Parent BOs

           (also see this Help page from the base wwBusiness Help File)

 

Other wwBusiness resources:

Main wwBusiness Help File from West Wind (click Class Reference –> Class wwBusiness)

Article by Kevin Cully about wwBusiness (PDF)

 

Licensing:

wwBusinessPro is a free open-source project, but you’ll need to purchase your own license for the main wwBusiness library from West Wind.  It is included as part of the West Wind Web Connection package and  also included in the West Wind Client Tools package at the West Wind web site.

 

If you have any questions or need specific help please comment below.

Detecting Session Timeout in Asp.Net

I recently had the need in my Asp.Net Webforms application to determine if a Session Timeout had occurred while the user was logged in to the app. As you know, while working in a Line Of Business app, many users can be drawn away from the app for extended periods of time after logging in. Upon returning to the app and making their next postback (by clicking some link or button on the form), most applications will have a mechanism to check the timeout limit set in Web.config and force the user to login again if the timeout limit has been passed. This is a basic security mechanism that is broadly used in web apps, and I wanted this behavior in my application.

Well, it turns out there is no built-in event or flag in the .Net framework that indicates when a Session Timeout has occurred. As I began what I thought would be a routine Google search, I found various solutions offered up by many developers who I’m sure are much smarter than me. After filtering through all the variations and approaches I saw on the web, I have trimmed it down to a simple method that works well for me, so I wanted to offer it up here to help anyone else who may encounter this need. You can add this method to your base form or master page code behind.

This simple method will return true if a Session Timeout has occurred, or false if not. You can call this method and take the desired action if true is returned. In my case, I log the user out and redirect them to the login page, passing a querystring parameter that triggers my login page to notify the user what has happened.

Method CheckForSessionTimout():

public bool CheckForSessionTimeout()
{
  if (Context.Session != null && Context.Session.IsNewSession)
  {
    string cookieHeader = Page.Request.Headers["Cookie"];
    if ((null != cookieHeader) && (cookieHeader.IndexOf("ASP.NET_SessionId") >= 0))
     return true;
    else
     return false;
   }
   else
    return false;
}

Here is how I call the above method from the OnInit() method of my MasterPage:

if (CheckForSessionTimeout())
{
 if (Page.Request.IsAuthenticated)
 {
   FormsAuthentication.SignOut();
 }
 NavigateToLoginPage(true);
}

And here is the code for my NavigateToLoginPage() method, where the parameter true means “alert the user that a timeout has occurred” :

public void NavigateToLoginPage(bool TimeOutHasOccurred)
{
  Response.Redirect("~/Login.aspx?mode=timeout");
}

image

On the Login.aspx page, I look for the QueryString parameter of “mode” with a value of “timeout” and display this beautiful yellow <div> to tell the user what has happened.

Grid Column DataBinding feature for VFP Grids

I want to share some code from my custom grid class that creates an easy way to programmatically bind columns in a grid to a cursor that is opened in the workspace. Basically, this method binds each column’s ControlSource property to the field in the cursor that you specify. It  also handles calculated fields, Casting, and a few more things I’ll show in the code samples below.

You do have a custom grid class that you use in your apps, right? So, now just do two things to your grid class:

  • Create a new placeholder method on your grid class named SetColumnControlSources().
  • Create a new method named BindColumn() and add my code from the BindColumn() code sample below.

Now, in each instance of the grid that you use on a form or class or subclass, create the required columns on the grid and set the header captions to match what you intend to show in the grid from the cursor. Be sure to give each column a smart Name property for easy coding (see example below). You do not set the ControlSource of each column while designing the grid. That’s what my fancy BindColumn() method does for you, along with a few more things you’ll see in the code sample. You will make calls to the BindColumn() method from your SetColumnControlSources() method to do the real work. I’ll show you how…

In the SetColumnControlSources() method of each grid instance, you’ll write code like this (specific to each grid<—>cursor matching that you want to build):

Sample SetColumnControlSources() method code:

Local lcRecordSource

With this
.BindColumn(.colItem, ‘item’)
.BindColumn(.colPrint, ‘print’)
.BindColumn(.colPartNo, ‘part_no’)
.BindColumn(.colAltPartNo, ‘altpartno’)
.BindColumn(.colDwgNo, ‘dwg_no’)
.BindColumn(.colDesc, ‘desc’)
.BindColumn(.colQty, ‘qty’)
.BindColumn(.colPrice, ‘price’)
.BindColumn(.colTotal, ‘qty * _RecordSource.price’)
.BindColumn(.colLaborCost, ‘laborcost’)
.BindColumn(.colLaborHrs, ‘laborhrs’)
.BindColumn(.colMachRate, ‘laborcost / _RecordSource.laborhrs’, ‘N(10,2)’)
.BindColumn(.colMtlCost, ‘mtlcost’)
.BindColumn(.colMtlMarkup, ‘mtlmarkup’)
.BindColumn(.colPartCost, ‘laborcost + _RecordSource.mtlcost + _RecordSource.mtlmarkup’)
.BindColumn(.colTotalCost, ‘qty * (_RecordSource.laborcost + _RecordSource.mtlcost + _RecordSource.mtlmarkup)’)
EndWith

*– Other fancy stuff you can do in addition to the above —

lcRecordSource = this.RecordSource

If !Empty(this.RecordSource)
this.colItem.DynamicFontUnderline = ‘iif(‘ + lcRecordSource + ‘.divider=.t., .t., .f.)’
this.colprice.DynamicCurrentControl = ‘iif(‘ + lcRecordSource + ‘.no_quote, [lblNoQuote], [Text1])’
EndIf

Notice how we can build calculated columns that are not even in the cursor, and easily setup a Cast() to control decimals and some DynamicXXX formatting too. Plus, it gives me one nice code window to see all my column binding without having to use the Property Sheet to see what each column is bound to.

So the above is *your* code, and now here’s *my* custom BindColumn() method code that you need to add to your grid class to handle the code you’ll write like the above. This methods assigns the Column.ControlSource property, expanding any _RecordSource references, and also handles the optional Cast() function (see code for .colMachRate above to see how to specify a Cast() format in the third parameter of the BindColumn method).

BindColumn() method code:

Procedure BindColumn()  (Add this method to your grid class)

Lparameters toColumn, tcBindExpression, tcCast

Local lcFieldExpression, lcBindExpression, lcRecordsource

If Vartype(toColumn) = ‘O’ and Upper(toColumn.baseclass) = ‘COLUMN’
lcRecordsource = toColumn.parent.RecordSource

If !Empty(lcRecordsource) and !Empty(tcBindExpression)
  lcFieldExpression = Alltrim(Strtran(Upper(tcBindExpression), ‘_RECORDSOURCE’, lcRecordSource))

  *– Combine RecordSource.FieldExpressiopn
  lcBindExpression = lcRecordSource + ‘.’ + lcFieldExpression

  *– Wrap in Cast() statement if casting was passed in
  If Vartype(tcCast) = ‘C’
   lcBindExpression = ‘Cast(‘ + lcBindExpression + ‘ as ‘ + tcCast + ‘)’
  EndIf

  *– Finally, set controlsource on column…
  toColumn.ControlSource = lcBindExpression
EndIf

Else

Return

EndIf

Finally, to make it all work, you simply set the RecordSource property on the grid and then call SetColumnControlSources():

thisform.Grid1.RecordSource = ‘csrDataCursor’

thisform.Grid1.SetColumnControSources()

 

I found having this in place to be an effective way to handle times when I need to kill the cursor, rebuild it, and then re-bind to it.

Like this:

thisform.Grid1.RecordSource = ‘’

*—Now rebuild the cursor

thisform.Grid1.RecordSource = ‘csrDataCursor’

thisform.Grid1.SetColumnControSources()

 

So there you go. Several tricks could be used to pass in the cursor field that you want to bind to.  I’ve also thought about including a provision for passing in the Header caption too.

Enjoy, revise, and post comments to share any other ideas you have for extending this.

Model, View Model, View – No way (for now)…

I get all the Model <—> ViewModel <—> View stuff.  It’s great theory, but come on! A View Model class that has the same exact properties as the stinkin’ Model that it is wrapping, just so we can say “I used MVVM”.

My little Asp.Net CRUD app is binding the UI controls directly the data properties on a BusinessObject.Entity, where the Entity is a Typed class from a LinqToSql query. (My BO’s are based on the West Wind wwLinqToSql Business Object Wrapper from the West Wind Web Tools for Asp.Net ).  And guess, what… It works! It loads and saves data when the user clicks the buttons.  Imagine that. I’ll be darned if I’m gonna wrap a ViewModel class around that data Entity so I can bind my UI controls to that ViewModel, rather than hooking straight to the Entity properties.  Just too much stinkin’ code required.

I’ve also added a few additionally needed "presentation properties" into my Business Objects to *supplement* the Entity properties that came from the data base row. And, yes, when working with my BO’s, you have to access some properties right off the BO (the custom presentation properties I added) and some come from the main BO.Entity (the real data from the database row). So, you do have to know where to get the data you are after. By doing this, most of my UI binding is against the BusinessObject.Entity properties, and a few UI bindings go against the supplemental presentation properties I’ve added right on the BO.

So, I don’t mind some bit of a hybrid ViewModel approach, but not a separate class where I have to re-host every single property that is on the Entity. And, since this is done on the Business Object class level, all these supplemental  "presentation properties" can still be accessed by other UI apps that work with the same Business Object wrapper classes.

From a theoretical standpoint, sure, it breaks a few rules. But from a real world impact, we are talking about only a handful of supplemental properties. In all the BOs I’ve built (Customer, Invoice, Quote, Shipper, User, Employee, etc.), maybe I’ve added 3 to 5 additional properties on each one to serve what is usually one view, but sometimes two or three different views at the most. If things really get crazy, there might be as many as 10 presentation properties to handle all the views. It still seems like less of an overall burden on the application as a whole to add a few presentation properties right on the BO, rather than dropping an entire Model-to-ViewModel Mapper implementation on my app (homegrown or commercial). Talk about a burden.

Now look, these additional properties will be implemented somewhere. Why not stick them onto something that already exists (the BO) as little light-weight tag-a-longs, rather than create an entirely free-standing ViewModel object, where I put these innocent properties and then I have to REPLICATE all the real properties from the Model too. Is that how this magic ViewModel character makes things "cleaner"? Yuck! Oh, wait, I almost forgot… but look how clean my BO is! No thanks.

Here’s a use case from my app… I want to bind a UI control to the Customer Name for an Invoice Object that I have pulled from the database. Well, rather than attempting to bind through the main Invoice Entity and into the Customer sub-Entity (which LinqToSql handles nicely for me BTW) I’ve added a presentation property on the BusinessObject to access the Customer Name easily, and also guard against several null object cases that can arise.

Example: Attempting to bind a UI control to directly to this property (see below) could crash the UI generation if the Entity object or Customer Entity is null:

busInvoice.Entity.CustomerEntity.Name

Since it’s possible that the busInvoice object’s .Entity property is not even saturated yet, or that the CustomerEntity is null, this could cause problems generating the UI.  And, I certainly don’t want to test for all the potential problems in my UI pages every time I want to display that property. So, I added a property on the busInvoice object to host that value and deal will all the required testing.

Something like:

public string CustomerName
{
get
   {
     if (this.Entity != null && this.Entity.CustomerEntity != null)
       return this.Entity.CustomerEntity.Name;
     else
       return "";
   }
}

Anyway, back to my main rant about going overboard with ViewModels…

So look at this sample I found from a notable Asp.Net guru.  It is a ProductView class that returns a ProductView object from a passed in Product (that you got from your DAL somewhere), and it can return a Product object from a passed in ProductView object, basically by unwrapping the ProductView object and casting the string properties back to the original data types. Look at all this code! Especially look at the ToProduct() method. Wow!  ( code from http://weblogs.asp.net/craigshoemaker/archive/2009/11/03/vm-workshop-model-view-viewmodel-mvvm-and-the-presentation-model-pattern-in-5-ui-platforms.aspx )

It looks like a maintenance nightmare.  I need more convincing that this is the way the pros do it. Perhaps I’ll be a rookie just a little bit longer.

    public class ProductEditView : IUrlSerializer
    {
        public string ProductId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string QuantityOnHand { get; set; }
        public string Price { get; set; }
        public string ReleaseDate { get; set; }

        public string SerializeToUrlString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("productId={0}&", this.ProductId);
            sb.AppendFormat("title={0}&", this.Title);
            sb.AppendFormat("description={0}&", this.Description);
            sb.AppendFormat("quantityonhand={0}&", this.QuantityOnHand);
            sb.AppendFormat("price={0}&", this.Price);
            sb.AppendFormat("releasedate={0}", this.ReleaseDate);
            return sb.ToString();
        }

        public static ProductEditView ToProductEditView(Product product)
        {
            ProductEditView view = new ProductEditView();
            view.ProductId = product.ProductId.ToString();
            view.Title = product.Title;
            view.Description = product.Description;
            view.Price = string.Format("{0:c}",product.Price);
            view.QuantityOnHand = product.QuantityOnHand.ToString();
            view.ReleaseDate = product.ReleaseDate.ToShortDateString();
            return view;
        }
        public static Product ToProduct(ProductEditView view)
        {
            double db;
            int num;
            DateTime dt;

            Product product = new Product();
            product.Title = view.Title;
            product.Description = view.Description;

            if (double.TryParse(view.Price.TrimStart(‘$’), out db))
            {
                product.Price = db;
            }
            else
            {
                throw new ArgumentException("Price is malformed or not a valid value.");
            }

            if (int.TryParse(view.ProductId, out num))
            {
                product.ProductId = num;
            }
            else
            {
                throw new ArgumentException("ProductId is malformed or not a valid value.");
            }

            if (int.TryParse(view.QuantityOnHand, out num))
            {
                product.QuantityOnHand = num;
            }
            else
            {
                throw new ArgumentException("QuantityOnHand is malformed or not a valid value.");
            }

            product.ProductId = Convert.ToInt32(view.ProductId);
            product.QuantityOnHand = Convert.ToInt32(view.QuantityOnHand);

            product.ReleaseDate = Convert.ToDateTime(view.ReleaseDate);
            if (DateTime.TryParse(view.ReleaseDate, out dt))
            {
                product.ReleaseDate = dt;
            }
            else
            {
                throw new ArgumentException("ReleaseDate is malformed or not a valid value.");
            }

            return product;
        }

Adobe Acrobat PDF Printer output filename on 64-bit OS…

Now that I’ve moved over to Windows 7 64-bit on my primary workstation, I’m in a new world of IT support that I didn’t expect from those extra 32 bits of OS horsepower. My MicroTek scanner is not supported, my HP 2800 Business Inkjet is not supported, and I recently discovered that a technique I use in my Visual FoxPro business application to pre-define the PDF output filename when using the Adobe Acrobat PDF printer driver mysteriously does not work on Windows 64-bit. So, I want to show the remedy in case others encounter this same problem. I’m explaining this from the perspective of a Visual FoxPro application, but the issue applies to all Windows applications, and the remedy can come from many programming languages.

Background… It’s well know that you can prevent the Adobe PDF printer driver from prompting the user to choose a path and filename for the output file by programmatically setting a certain registry key with a string value that contains the desired path and filename of PDF file you want to output. I use this technique to automatically set the path and filename for my users for the various PDF reports we generate to send out to our customers and vendors.

This magic registry key is located at: HKEY_CURRENT_USER\Software\Adobe\Adobe Distiller\PrinterJobControl.  So, you create a string value under that key with the path and name of the *application* that is printing the PDf, and then you set the value of that string value to the path and filename of the PDF you want to create.

Now, the path and filename of the application that is doing the printing is not always easy to figure out, so I can’t solve that mystery here for any given app that you may be using, but I do know (and I forget where I learned this) that for a Visual FoxPro application (.exe), the path and application name can be discovered from the VFP variable “_VFP.ServerName”. Of course, it’s up to you to decide the path and filename of the PDF output file you’re want to create.

So, to bring it all together, you add a string value named after the app that is doing the printing (for instance) “C:\Program Files\Microsoft Visual FoxPro 9\vfp9.exe” and you set the value of that string value to the filename you want the PDF to be called; i.e. “C:\TEMP\JMC_PO_MS2741C.pdf”

Well, that’s how it works on good ‘ole 32-bit OS.  Now, enter into the 64-bit world…

The issue is that the *application* name that the 64-bit OS sees as doing the printing is no longer the actual exe the user is running,  but rather some odd Windows exe named “splwow64.exe” (some spooler-like thingy that actually handles the printing which I learned about here http://www.acrobatusers.com/forums/aucbb/viewtopic.php?pid=57794#p57794 ). So that’s the application name you have to use, and you do have to include the path. Since this spooler thingy exe runs from C:\Windows, the actual registry key that must therefore be created is “C:\Windows\splwow64.exe”.

PDF_Reg

So, with the PrinterJobControl registry key all programmatically setup as described above, when you actually fire off the Adobe PDF printer, the output name is already set and the user will not see the prompt dialog to choose the output path and filename.

Also, be aware that after *you* programmatically create and set the registry value, the Adobe PDF driver will actually delete that string value from the key right after the file is printed. Perhaps this is so the next app that prints won’t use the same name.

Finally, here’s the VFP code I use to set the values for both 32-bit and 64-bit OS’s. (This code uses an instance of the Registry class from the FoxPro FFC classes to work on the Windows registry). Again, this all works the same way whether you are using .Net, VFP, VB, or whatever. The main thing is to setup the registry value the right way.

   oRegTools=createobject(‘registry’)

        m.lcKeyPath = "Software\Adobe\Acrobat Distiller\PrinterJobControl"

       *– This RegKey setting works on 32-bit OS…
        oRegTools.OpenKey(@lcKeyPath, HKEY_CURRENT_USER , .T.)
        lcPrintingApplicationName = _VFP.ServerName
        oRegTools.SetKeyValue(lcPrintingApplicationName, “C:\TEMP\JMC_PO_MS2741C.pdf”)
        oRegTools.CloseKey()

        *– This is the "Application" name that the OS sees as doing the printing when running on a 64-bit OS…
        oRegTools.OpenKey(@lcKeyPath, HKEY_CURRENT_USER , .T.)
        lcSplWow64ProcessName = ‘C:\Windows\splwow64.exe’ && <— This is what the OS sees as what’s doing the printing…
        oRegTools.SetKeyValue(lcSplWow64ProcessName, “C:\TEMP\JMC_PO_MS2741C.pdf”)
        oRegTools.CloseKey()

 

Hopefully, this insight will save someone out there a little headache when working with the Adobe PDF printer driver on these super-duper 64-bit OS’es.

LINQ to SQL, Lazy Loading and Prefetching

I want to recommend this interesting article that explains times when LinqToSql’s native behavior of “Lazy Loading” is not a good thing (from a database performance standpoint), and explains a pre-fetching technique that you can can use when you want to avoid lazy loading hits to SQL Server.

It’s from the the great Rick Strahl. See his blog post at:

http://west-wind.com/Weblog/posts/38838.aspx

I also recommend that you review his LinqToSql Business Object wrapper framework and sample app that he created to show a real-world working app using the framework.

Original article and download, September 27, 2007 (read this first):

http://www.west-wind.com/WebLog/posts/160237.aspx

He also posted an update article on February 05, 2008:

http://www.west-wind.com/Weblog/posts/246690.aspx

And, Rick has continued to update the LinqToSql Business Object wrapper framework since those articles were written, and you can download the latest version which is included as part of the WestWind Web Toolkit for Asp.Net (tons of other goodies are in the entire Toolkit, so check out the free license details, which is a full working version) :

http://www.west-wind.com/westwindwebtoolkit/

 

In closing… You know, they’ve said that LinqToSql was kind of dead, but at least they did do a little work on it for .Net 4 framework. That’s encouraging to me. I think a LOT of people use LinqToSql, and I personally think it will pick up even more, rather than die off. It has a fine place in small-to-mid sized apps, and I frequently hear plenty of notables in the community speak of it.

Here’s is an article on the updates to LinqToSql that included in the .Net 4 framework:

http://damieng.com/blog/2009/06/01/linq-to-sql-changes-in-net-4

Uninstalling Visual Studio 2010 Beta 2

Why am I writing about Un-installing Visual Studio Beta 2 when it was just released yesterday, October 19, 2009? (video announcement can be seen at this link). Well, in the past whenever I’ve used the beta versions of some of Microsoft’s developer tools, I’ve found that it was impossible to uninstall them properly (or fully) when the RTM version was finally released.

Now, I know that VM’s can be used to test out beta software, and I do use VM’s on my machine, as I did when I installed VS2010 Beta 1 on a Windows 7 Beta VM running on VirualBox VM.  But sometimes I still like running right on the bare metal so I will have access to the rest of the tools I need in a full dev environment. Especially now that it’s Beta 2, I’d feel pretty good running it on my live dev machine. But, I was unsure about being able to easily remove it later when the RTM version comes out on March 22, 2010.

So, I posted a question on the Visual Studio 2010 Install and Setup forum of the newly restyled  http://www.msdn.com/forums to see if anyone could address this issue.

My question is titled Will we be able to un-install VS2010 Beta 2, when final version is released?.

Fortunately, I got a very quick reply from a MSFT moderator named Aaronru who claims a very easy and successful uninstall of VS2010 Beta 2 has been tested and WILL allow for an easy upgrade path to VS2010 RTM.

The exact reply was…

For helpful instructions on how to install Visual Studio, we’ve published some detailed instructions for installing and maintaining Visual Studio 2010 Beta 2.

If you evaluated Visual Studio 2010 Beta 1, you will be happy to hear that no reformatting is required. Additionally, simple path exists to appraise Beta 2 on the same machine. It involves uninstalling your Visual Studio 2010 Beta1 product and then installing one of the Visual Studio 2010 Beta2 offerings.  Please refer to the following blog post for more details.

As part of our Beta 2 testing, we did test the uninstall of Beta 2 to ensure that the Beta 2 could be uninstalled sufficiently to the end that you could uninstall Beta 2 and install RTM with as seamless UX as possible.

You can read the entire thread here.

So go for it! The download of VS2010 Beta 2 will be available to the public on October 21, 2009, with final RTM to be released on March 22, 2010.

Doctor of Code