Saturday 3 September 2011

Bubbling up control's property via dependency property

My today's study is a WPF user control that wraps a single combo box. The Text property of the latter I want to expose to consumers of the user control.

ReSharper 6.0 turns declaring a dependency property into an easy exercise.


public static readonly DependencyProperty SelectedCourierNameProperty =
    DependencyProperty.Register("SelectedCourierName", 
    typeof (string), typeof (CourierComboView), new PropertyMetadata(default(string)));


public string SelectedCourierName
{
    get { return (string)GetValue(SelectedCourierNameProperty); }
    set { SetValue(SelectedCourierNameProperty, value); }
}

Having the dependency property implemented,  I can proceed with creating an event handler for the SelectionChanged event of the combo box. I can do this either by modifying both the XAML and the code-behind, or just the code-behind (as below).


theCourierCombo.SelectionChanged += delegate { SelectedCourierName = theCourierCombo.Text; };

The other option, which I like much better, is invoking the SetBinding method of the user control (inherited from the FrameworkElement class).


SetBinding(SelectedCourierNameProperty,
    new Binding("Text") { Source = theCourierCombo, 
        Mode = BindingMode.OneWay });

And of course even better option would be using a view model.

Monday 15 August 2011

Microsoft Visual Studio LightSwitch 2011

After seeing an announcement, I decided to download and test Microsoft Visual Studio LightSwitch 2011. It happened to become a longer than expected task.

Before installing this version, make sure to remove LightSwitch beta version. Uninstalling the LightSwitch may take 5..10 minutes.

Other prerequisites:
- .NET Framework 4.0 -- otherwise you'll see no LightSwitch project templates in the VS
- Visual Studio 2010 with SP1 installed -- another 30 to 60 minutes of your valuable time
- SQL Server Express

The latter is the most annoying. On my main computer I have SQL Server 2008 R2 Developer Edition installed, which is an equivalent of the Enterprise version. This appears to be insufficient. The animal still wants to have SQL Server Express simply because "SQL Express is a LightSwitch prerequisite on the development machine." Supposedly it stores development configuration/metadata there. Fortunately, application deployment can be made to a machine with any SQL Server 2005+ version installed.

Congratulations Microsoft for another wise step. It will certainly boost the user base of your new product.

Data source can only include tables and views, no stored procedures and functions -- that's another smart decision. Probably Microsoft does not believe that business end-users are smart enough to deal with these two concepts.

May be I am too sceptical...

Thursday 9 June 2011

The 'VFPOLEDB.1' provider is not registered on the local machine

On 64-bit Win2008 Server, ASP.NET web page can display this error message even if Microsoft OLE DB Provider for Visual FoxPro 9.0 is properly downloaded and installed.

In my case the solution was simple: open Advanced Settings of the DefaultAppPool and set "Enabled 32-Bit Applications" to True. That of course assumes that ASP.NET application runs in the DefaultAppPool.

Thursday 2 June 2011

2010 WinForms.ReportViewer control requires at least SQL Server 2008

With ProcessingMode set to Remote, ReportViewer control requires SQL Server 2008 or later. Another reason to keep development and production tool versions close. Or using ReportViewer's Local mode.

Fetching inventory balance with a single T-SQL statement

The main challenge is the calculation of On Hand and On Order totals in each row: for that the data from the previous row must be somehow accessed. Certainly this task can be delegated to an application, but this is inelegant as much as not practical. Writing a table-valued function is another probable approach. Most likely that means opening and scanning a cursor, INSERTing data at each scan.

A solution exists solely within SQL Server query domain: use ROW_NUMBER() and CTE recursive queries. Here's the result. As you can see, the last two columns contain accurately calculated current balance for each row.



Implementation
On the first step the data from orders, receiving documents and shipping documents is collected and UNIONed ALL into csInventory1. This is simple enough and need no illustration.

On the second step, a column containing ROW_NUMBER() is added. This column is intended to be used for joining parts in subsequent CTE recursive query. In other words, it allows a row reach the data of the previous row. Here's the second step's query.

csInventory2 as (
select
   row_number() over (
      partition by product_nbr 
      order by document_dte, 
      document_tpe desc) as rowno,
   product_nbr, 
   document_dte, 
   document_tpe,
   document_nbr, 
   qty_ordered,
   qty_avail,
   qty_shipped
from csInventory1


The third step is a CTE recursive query. It's a bit long piece of code but worthy to be displayed here.

csInventory3 as (
-- anchor member definition
select
   i2.rowno,
   i2.product_nbr, 
   i2.document_dte, 
   i2.document_tpe,
   i2.document_nbr, 
   i2.qty_ordered,
   i2.qty_avail,
   i2.qty_hold,
   i2.qty_shipped,
   i2.location,
   cast(i2.qty_avail - 
      i2.qty_shipped as int) as on_hand,
   cast(i2.qty_ordered - 
      i2.qty_shipped as int) as on_order
from csInventory2 i2
where rowno=1 -- top row for each product
union all
-- recursive member definition
select
   i2.rowno,
   i2.product_nbr, 
   i2.document_dte, 
   i2.document_tpe,
   i2.document_nbr, 
   i2.qty_ordered,
   i2.qty_avail,
   i2.qty_hold,
   i2.qty_shipped,
   i2.location,
   on_hand + 
   cast(i2.qty_avail - i2.qty_shipped as int),
   on_order + 
   cast(i2.qty_ordered - i2.qty_shipped as int)
from csInventory2 i2
   inner join csInventory3 i3 on 
      i3.product_nbr = i2.product_nbr -- same product
      and i3.rowno = i2.rowno-1 -- link to the prev.row

And the final SELECT is simple.

select top 100 percent
   rowno,
   product_nbr, 
   document_dte, 
   document_tpe,
   document_nbr, 
   qty_ordered,
   qty_avail,
   qty_shipped,
   on_hand,
   on_order
from csInventory3
order by product_nbr, rowno

Wednesday 1 June 2011

Configuring Database Mail (dbmail) on SQL Server Express 2008

Database Mail (aka DBMail) is an enterprise solution for sending e-mail messages from the Microsoft SQL Server Database Engine. It is easy configurable on a full scale MS SQL Server via Database Mail Configuration Wizard. None of SQL Express versions so far has this convenience, which means on SQLExpress the DBMail has to be configured manually.

All required information can be found on this page Enabling Database Mail on SQL Server Express. The most of it is applicable to either 2005 or 2008 SQLExpress version. For my own comfort (for sure I will need this on a later occasion) I wrote down all today's steps that lead to a successful DBMail configuration.

SQL Server Express instance configured today

  • Product Version: 10.50.1600.1
  • Product Level: RTM
  • Edition: Express Edition with Advanced Services (64-bit)

DBMail Support Files
SQLExpress 2005 installation does not include DBMail support files:

  • DatabaseMail.exe
  • DatabaseMailengine.dll
  • DatabaseMailprotocols.dll

Get these files from a regular SQL Server 2005 installation (not a Express one) and copy to MSSQL\Binn folder. No registration is required for the two DLL files.

SQL Server Express 2008 does contain DBMail support files. On my computer they are located in
C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\Binn

Starting DBMail
The following script turns the Database Mail option on.
USE master

GO
sp_configure 'show advanced options', 1
GO
reconfigure with override
GO
sp_configure 'Database Mail XPs', 1
GO
reconfigure 
GO
sp_configure 'show advanced options', 0
GO

use msdb
exec dbo.sysmail_start_sp

Using Database Mail Configuration Wizard in SSMS
If you are not on Express Edition, this Wizard is a great help and convenience. Otherwise skip to the next topic.

I am surprised that neither the SSMS nor the wizard allow a profile to be scripted out.

Using SSMS template
Each SQL Server SSMS install comes with a variety of templates. "Simple Database Mail Configuration" template has all basics scripted nicely.



All you have to do is to fill in values for Profile, Account, SMTP Server, Email Address, and Display Name. Though note that this a simplified script. For example, as it is, it does not prompt where to put values for SMTP port and SSL.

Choosing email account
Before going any further, gather email account settings to be used. The following settings are absolutely required. Replace them with your values.

  • Email Address: myaddress@mydomain.com
  • Display Name: John Smith
  • Mail Server: smtp.mydomain.com
  • Port: 25 -- this is default SMTP port
  • User Name: myaddress@mydomain.com
  • Password: mypassword
  • SSL enabled: Yes/No

Creating new profile
use msdb
-- creates new profile

exec msdb.dbo.sysmail_add_profile_sp
@profile_name = 'DefaultMailProfile',  -- provide your own value
@description = 'Default email profile'  -- provide your own value
GO


-- sets new profile as the default one

exec msdb.dbo.sysmail_add_principalprofile_sp
@profile_name = 'DefaultMailProfile',  -- provide your own value
@principal_name = 'public', -- do not change
@is_default = 1 -- do not change
GO



Creating an account and adding it to the profile

use msdb
-- creates an account
exec msdb.dbo.sysmail_add_account_sp
@account_name = 'DefaultMail',
@description = 'My email account',
@email_address = 'myaddress@mydomain.com',
@display_name = 'John Smith',
@replyto_address = null,
@mailserver_name = 'smtp.mydomain.com',
@username = 'myaddress@mydomain.com',
@password = 'mypassword',
@port = 25,
@enable_ssl = True
GO




-- adds the account to the default profile
exec msdb.dbo.sysmail_add_profileaccount_sp
@profile_name = 'DefaultMailProfile',
@account_name = 'DefaultMail',
@sequence_number = 1
GO

Sending a test email

exec msdb.dbo.sp_send_dbmail 
@recipients='myfriend@myfrienddomain.com',
@subject = 'Test email sent via DBMail',
@body = 'This is a test email. Please do not reply.',
@reply_to = 'myaddress@mydomain.com'
GO

Verifying that the test email left the server

The last script normally returns message "Mail queued". There is no indication of a mail actually leaving the database server. Query msdb.dbo.sysmail_log and msdb.dbo.sysmail_mailitems tables to obtain complete results.

First of all, there should be recently created sysmail_log records indicating that "The mail queue was started...", and "DatabaseMail process is started".

A failed attempt to send an email creates sysmail_log entry with description "The mail could not be sent to the recipients because of...". Read the description and take appropriate steps. May be validate again email settings: SMTP server, port, user name and password.

DBMail tables and views
Database MSDB contains several DBMail tables. Note that these are all System Tables. The above configuration scripts make changes in DBMail tables. But of course a carefully calculated manual change is also an option.

Tables:


  • sysmail_mailitems
  • sysmail_log
  • sysmail_configuration
  • sysmail_servertype
  • sysmail_profile
  • sysmail_principalprofile
  • sysmail_account
  • sysmail_server
  • sysmail_profileaccount
  • sysmail_attachments
  • sysmail_attachments_transfer
  • sysmail_query_transfer
  • sysmail_send_retries

Views

  • sysmail_allitems
  • sysmail_eventlog
  • sysmail_faileditems

Tuesday 31 May 2011

I didn't change anything and now it's not working!

That's an explanation any developer dealing with end-users very likely have heard. In fact almost always it can be translated as "the stupid thing is not working; I have changed something that I'm not aware of; I cannot find yet a suitable person or entity to blame for; fix it fast because I'm already two days late with my report!".

Very nice, then today I am such an end-user. The data part of a .NET WinForm application throws an exception. One of the following postulates for sure must be false:
- "this morning everything worked"
- "I swear I did not change anything"

To make it short, in app.config I have created the appSettings section preceding the connectionStrings section. It is not my first visit into this little trap.

Once I remember I heard from a user that "we did not change anything really, just installed new Office version".

Tuesday 24 May 2011

Wow6432Node, accessing Windows Registry from ASP.NET app

My old ASP.NET application reads part of its configuration data from the Windows Registry. Certainly a questionable approach, but that's how it is. The application is currently running on W2K server. Soon enough it will be moved  to Windows 2008 64-bit server.

During recent tests on the new hardware I found that the application was no longer able to read from the Registry. First I guessed that the new system's stricter security was a reason. Changing registry key's permissions did not help. Creating a separate IIS application pool and running it under a privileged user account did not help either.

Meanwhile a test application had not trouble enumerating subkeys inside HKEY_LOCAL_MACHINE\SOFTWARE, but strangely it could not access HKEY_LOCAL_MACHINE\SOFTWARE\[MyApplicationRoot]. Strangely it was until I noticed that the enumerated subkeys were not exactly the subkeys the Regedit displayed.

A quick search using the Regedit revealed that HKEY_LOCAL_MACHINE\SOFTWARE enumeration on 64-bit system actually enumerated subkeys inside HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node. That's where I  moved the keys of my application and finally had this issue resolved. No source code changes were required.

Read more about registry redirection:
http://msdn.microsoft.com/en-us/library/ms724072(v=vs.85).aspx

    Private Sub ListRegistryKeys(ByVal strKey As String)

        Dim rk As Microsoft.Win32.RegistryKey = _
            Microsoft.Win32.Registry.LocalMachine.OpenSubKey(strKey)

        Response.Write("Subkeys:<ul>")

        Response.Write(String.Format("<li>{0}", _
            System.Security.Principal.WindowsIdentity.GetCurrent().Name))

        For Each skName As String In rk.GetSubKeyNames()
            Dim sk As Microsoft.Win32.RegistryKey = rk.OpenSubKey(skName)
            Response.Write(String.Format("<li>{0}", sk.Name))
        Next
        rk.Close()

        Response.Write("</ul><hr>")
    End Sub