Saturday, December 29, 2007

Getting SQL 2005 Column information for field validation

Form fields should be validated to ensure that they are within the maximum allowable length. For database applications, ultimately that length is determined by the field size in the database. Any complex database application should build an keep of the database schema in memory and use that information to validate fields on a form. The following SQL 2005 query returns a single table of the most relevant information for a database, assuming the SQL login has permission to view this information. The information can be kept in memory in a dataset, or can be loaded into a dictionary for quick retrieval. Replace 'AdventureWorks' with the name of your database.

USE AdventureWorks

SELECT C.Table_Name, C.Column_Name, C.Ordinal_Position, C.Column_Default, C.Is_Nullable, C.Data_Type, C.Character_Maximum_Length, C.Numeric_Precision, C.Numeric_Scale, C.DateTime_Precision

FROM information_schema.columns C

JOIN Information_Schema.Tables T ON C.Table_Name=T.Table_Name

WHERE Table_Type = 'Base Table'

ORDER BY C.table_name, C.Ordinal_Position

Hope that helps.
Joe Kunk
Okemos, MI USA

Tuesday, December 11, 2007

Use a CheckBox instead of a ToggleButton for .Net windows form

Sometimes it is the little things that can drive you nuts. In VB6 I was used to having a ToggleButton control but I just went to use the equivalent in a .Net form for the first time and it was nowhere to be found.

Turns out that if you need this effect on a .Net winform, use the CheckBox control and set the Appearance property to "Button". The button appears "down" when Checked is true. Simple question, simple answer (once you find it).

Hope that helps.

Joe Kunk
Okemos, MI

Saturday, December 08, 2007

Post-build event command line

I am working on building a plugin dll to a commercial software product. That dll must be placed in a particular sub-folder within the application's Program Files folder in order to be loaded at application startup.

I want to keep the freshly built plugin.dll and plugin.pdb files in the standard Visual Studio project build folder to be consistent with other projects, but it is a pain to manually copy those two files to the application's reserved folder after each build.

As typical in Visual Studio, when you think "there must be a better way", there usually is.

Go to the project properties, Build Events, and specify a post-build event command line that copies the freshly built assembly to the desired location. Thus it will just automatically be placed there after each successful build. You have the option to execute the post-build event command line even if the build is not successful.

The event command line can be any command that will execute properly in the operating system's command line interface (File, Run, Cmd {enter}). For more complex tasks, you can call a batch file or even a console project executable in your solution.

If the build event fails, your build will fail as well. This is a good thing since you want to be sure that your post-build event completed successfully. A pre-build event command line is available too.

The single line command that i used is below. The /y parameter overwrites any existing file of the same name without prompting. It uses pre-defined project constants that are listed for you when you are in the editor box of that property page. Visual Studio refers to them as "macros".

{commercial software reserved folder location} refers to the specific reserved plugin folder of the software. Note the use of quotes since the build path and destination path may both contain spaces. Note that the macros will expand properly even within quoted strings.

copy /y "$(TargetName).*" "C:\Program Files\{commercial software reserved folder location}\"

Hope that helps.

Joe Kunk
Okemos, MI

Tuesday, December 04, 2007

VistaDB Table Index Names May Repeat Across Tables

A common database storage requirement is for the items contained in a drop-down list on the user entry/update form. I've seen these items referred to as a PickList or LOV for List-Of-Values. While all PickList values across the application could be combined into a single table, I've usually seen separate tables for each of the application's major PickList sets.

I'm currently working on an application with almost two dozen PickList tables. To make working with them easier, the table schemas are identical except for the first field, the identity seed primary key field.

The data import routine needs to determine if an incoming field is already contained within its corresponding PickList table or not. To make searching easier, I created an index for the display-text field on each of the PickList tables.

To make coding easier, I wanted the index name to be identical on each of the tables, but I was concerned that might cause a problem. Turns out that it was fine, VistaDB had no problem allowing me to create identically named indexes across all the PickList tables. Good news! :<)

Hope that helps.

Joe Kunk
Okemos, MI

VistaDB is a commercial product of VistaDB Software, Inc. All rights reserved.

Sunday, December 02, 2007

Visual Studio 2008 Multi-Targeting feature does not apply to Visual Studio 2003 (.Net Framework 1.1 and 1.0)

Visual Studio 2008 has a feature called multi-targeting that allows the visual environment to enforce or target development for a specific framework. The advantage is that you no longer need to maintain multiple copies of Visual Studio on your computer in order to support applications written in prior versions.

This feature supports .Net framework versions 2.0, 3.0, and 3.5. As a result, this feature does not apply to the .Net framework version 1.1 or version 1.0, used by Visual Studio 2003. If you have older applications that require those versions, you will need to keep Visual Studio 2003 on your computer.

Hope that helps.

Joe Kunk
Okemos, MI

Thursday, November 22, 2007

Generic Add Record method with VistaDB DDA in C#

I wrote a generic C# method to add any standard System.Data.Datarow to a VistaDB 3.2 table using the Direct Data Access (DDA) methods for optimal performance. The routine requires the following conditions:
  1. A member variable _databaseName that has the full path to the database file.
  2. A member variable _password that has the database password if applicable.
  3. A parameter that indicates whether to opent the table in exclusive mode or not
  4. Each table has an autoincrement field primary key as its first field.
  5. The datarow is from a typed datatable that matches the VistaDB table in name and fields.
  6. A null value in any incoming field indicates to refrain from placing a value in the VistaDB row for that field.

I hope you find this useful.

Joe Kunk

public int AddRecordDDA(System.Data.DataRow dro, bool Exclusive) {

// Get the table name based on the schema for the row's table
string tableName = dro.Table.TableName;

if ((db == null) || (db.IsClosed))
db = VistaDBEngine.Connections.OpenDDA().OpenDatabase(_databaseName, _openmode, _password);

// Open the table exclusive or shared based on the method parameter
VistaDB.DDA.IVistaDBTable tbl = db.OpenTable(tableName,Exclusive,false);

// Insert a new row to hold the data
int autoIncrement = -1;

// For each column, add it to the newly created row based on its column data type
foreach (System.Data.DataColumn dc in dro.Table.Columns) {
string columnName = dc.ColumnName;
string columnType = dc.DataType.Name;
int columnIndex = dc.Table.Columns.IndexOf(columnName);
switch (columnType.ToLower()) {
case "binary": // SQL Server 2005 type Image, VarBinary
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutBinary(columnIndex, (byte[])dro.ItemArray[columnIndex]);
case "boolean": // SQL Server 2005 type Bit
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutBoolean(columnIndex, (Boolean)dro.ItemArray[columnIndex]);
case "byte": // SQL Server 2005 type TinyInt
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutByte(columnIndex, (Byte)dro.ItemArray[columnIndex]);
case "datetime": // SQL Server 2005 type DateTime
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutDateTime(columnIndex, (DateTime)dro.ItemArray[columnIndex]);
case "decimal": // SQL Server 2005 type Decimal, Money, SmallMoney
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutDecimal(columnIndex, (Decimal)dro.ItemArray[columnIndex]);
case "double": // SQL Server 2005 type Float
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutDouble(columnIndex, (Double)dro.ItemArray[columnIndex]);
case "guid": // SQL Server 2005 type UniqueIdentifier
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutGuid(columnIndex, (Guid)dro.ItemArray[columnIndex]);
case "int16": // SQL Server 2005 type SmallDateTime, SmallInt
if ((!dc.Table.Columns[columnIndex].AutoIncrement) &&
(dro.ItemArray[columnIndex] != DBNull.Value))
tbl.PutInt16(columnIndex, (Int16)dro.ItemArray[columnIndex]);
case "int64": // SQL Server 2005 type BigInt
if ((!dc.Table.Columns[columnIndex].AutoIncrement) &&
(dro.ItemArray[columnIndex] != DBNull.Value))
tbl.PutInt64(columnIndex, (Int64)dro.ItemArray[columnIndex]);
case "single": // SQL Server 2005 type
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutSingle(columnIndex, (Single)dro.ItemArray[columnIndex]);
case "int32": // SQL Server 2005 type Int
if ((!dc.Table.Columns[columnIndex].AutoIncrement) &&
(dro.ItemArray[columnIndex] != DBNull.Value))
tbl.PutInt32(columnIndex, (Int32)dro.ItemArray[columnIndex]);
case "string": // SQL Server 2005 type Char, Text,
if (dro.ItemArray[columnIndex] != DBNull.Value)
tbl.PutString(columnIndex, (String)dro.ItemArray[columnIndex]);
MessageBox.Show(string.Format("AddRecordDDA needs data type '{0}' added to its switch statement.",columnType.ToLower()));

// Commit the row to the table

autoIncrement = (int)tbl.Get(0).Value;

// Close the table since row was added correctly

// Return the identity seed primary key for the new row.
return autoIncrement;

Monday, October 29, 2007

Quick method to initialize a strongly-typed DataRow in C#

Last night I needed a routine to initialize all the fields in a datarow of a strongly-typed dataset. I'm working in Visual Studio 2005 and the .Net Framework 2.0. Below is the code that I came up with. Note that I'm slowing switching over to C# for new projects.

This code is not comprehensive of all the possible data types, but it did the trick for me:

private void InitializeNewDataRow(DataRow dr, System.Data.DataColumnCollection dcs) {
object[] fields = dr.ItemArray;
DataColumn[] dca = new DataColumn[fields.Length];
for (int i=1; i < fields.Length; i++) {
if (dca[i].DataType == typeof(int)) fields[i] = 0;
if (dca[i].DataType == typeof(Int16)) fields[i] = 0;
if (dca[i].DataType == typeof(Int32)) fields[i] = 0;
if (dca[i].DataType == typeof(Int64)) fields[i] = 0;
if (dca[i].DataType == typeof(bool)) fields[i] = false;
if (dca[i].DataType == typeof(string)) fields[i] = string.Empty;
if (dca[i].DataType == typeof(decimal)) fields[i] = 0.0;
if (dca[i].DataType == typeof(DateTime)) fields[i] = DateTime.MinValue;
dr.ItemArray = fields;
Hope it helps.
Joe Kunk

Monday, October 08, 2007

Server and PC virtualization as a Disaster Recovery strategy

Please excuse me as I take a short diversion from application development topics for this post.

I spent a stint as a Information Technology Director several years ago. One of the difficult challenges that I faced was implementing a viable disaster recovery plan to protect the business and ensure continued operations in the event of any kind of business interruption.

The best option available at the time was to contract with a firm that would provide compatible servers and PCs within a specified timeframe in the case of a business interrruption - basically a mobile computer center in the back of a semi-truck. Using recent off-site backup tapes, we would recreate our computing environment and continue to operate.

My concern was that backup tapes and off-site tape procedures are often less than 100% reliable. The slightest error on one of the tapes could invalidate the restore. Differences between the supplied hardware and that used in-house could adversely affect the operation of the server. While test sessions were available from the vendor to help ensure that everything was ready to go prior to an interruption, there was always the knowledge that bringing up a duplicate server/PC environment would be difficult and time-consuming under the best conditions. To be clear, I was much more concerned about successfully recreating the server environment than the end-user PC environment.

The reason for this post is to point out that server and PC virtualization software systems have advanced to the point that they can serve as the basis for a new and more effective approach to disaster recovery. I won't mention vendor or product names but a quick Internet search for "server virtualization" will provide the current list.

In a nutshell, virtualization software allows the creation of a virtual machine image that represents all the resources of a computer as a single file or collection of files, including the hardware, operating system, disk drives, applications, data, etc. The host application loads and runs the virtual machine and allocated physical resources among the virtual machines.

This layer of virtualization does have a performance penalty but with the latest multi-core servers and PCs available, it can be a minor concern.

Now consider this. What if you implemented all your daily production servers in your data center as virtual servers such that that actual physical servers rarely ran more than the virtualization software?

Virtual server backup simply becomes a file copy to an external disk drive device. One Terabyte (1,000 Gigabyte or 1 million Megabyte) external drives are available now for just a couple hundred dollars each. By rotating backups among at least two external drives, you always have your latest server backup off site.

Most PC users will not want to work inside a virtual machine image on a regular basis. It is best to create a master end-user PC configuration that has all the necessary software installed and properly configured. This configuration can be written to an image file by software designed to provide hard drive cloning and restored onto a client PC when needed. Restoration tends to be fast and has the advantage of providing a uniform end-user starting point for the disaster recovery environment.

Under this scenario, disaster recovery becomes much more viable since to get properly running servers, all you have to do is get any sufficiently powerful server that can run the virtualization host software, load and run the saved server virtual machine file, and you're up! You are no longer dependent on getting server hardware in an emergency that is configured exactly as your in-house servers. In my opinion, this can make the difference between a successful disaster recovery scenario and one that is disappointing at the least.

Attach a couple PCs that have been updated with the reference end-user configuration image, and you are up and running in your own business environment, just the way that you are used to seeing it.

I am no longer responsible for implementing this kind of strategy but it just makes so much sense to me that I wanted to share it in hopes that it can be beneficial to those that are facing this challenge yet today.

Hope it helps,

Joe Kunk

Friday, October 05, 2007

WOW! - Microsoft to Release the Source Code for the .NET Framework Libraries

Scott Guthrie's blog on last Wednesday announced the upcoming availability of source code to portions of the .Net Framework. This is huge! What better way to learn .Net ?!

For more information, see his post at

Joe Kunk

Thursday, October 04, 2007

Dynamically create a simple array in VB.Net

Using an array is more efficient than a collection if an array can suffice, but in the past I have found myself resorting to using a collection simply because I did not know in advance the number of elements needed until runtime. Turns out it is easy to create a simple system array at runtime and specify the length of the array at that time.

For example, to create a string array of 5 elements (0 to 4):
Dim MyArray as System.Array
MyArray = System.Array.CreateInstance(GetType(String),5))

which is equivalent to the following code at compile time:
Dim MyArray(4) as String

The same technique can be used to dynamically create arrays of any type at runtime.

Note that the above example assumes Option Strict is off. If Option Strict is on, you must use the .SetValue and .GetValue methods to access the array elements. If Option Strict is on, a proper code sample would be:

'Option Strict On
Dim mya As System.Array
mya = System.Array.CreateInstance(GetType(String), 3)
mya.SetValue("Word1", 0)
mya.SetValue("Word2", 1)
mya.SetValue("Word3", 2)
MessageBox.Show("Element (2) is " & _
Convert.ToString(mya.GetValue(2))) 'Returns "Word3"

Hope that helps.
Joe Kunk

Sunday, August 19, 2007

A public Thank You! to Jeff McWherter

As President of the GLUGnet user group for .Net developers, I'm very concerned that each monthly meeting be a great experience for our members in every possible way. This usually means that I worry too much about a lot of things that never happen. Or almost never.

On the morning of our last meeting on 8/16, our scheduled speaker sent an email in the morning that he had the flu and could not make the meeting. With only hours left until the meeting, Jeff McWherter of A.J. Boggs, Inc. kindly offered to do a presentation on ASP.Net Performance and Optimization. As we got to the meeting, I found out that he suffered a hard-disk crash that morning and was forced to reinstall Visual Studio and also finish the presentation.

Despite these challenges, he gave a very interesting presentation that was well received by the audience. We all learned some very valuable lessons. Jeff is a Board Member of GLUGnet and this is just another example of how he contributes so much to the group and we all benefit from it.

Great job Jeff! Thank you so much!


Thursday, August 16, 2007

GLUGnet Meeting Wednesday August 16, 2008 - ASP.Net Optimization

Greater Lansing User Group .net ( August Meeting

Where: 1145 Engineering Building, Michigan State University, East Lansing, Michigan

When: Thursday August 16th 6:00-8:00 PM

What: Free swag including Suite, Telerik Rad Controls for Winforms,Telerik Reporting, FarPoint Spread for Windows, FarPoint Spread for Web, DevLink conference pass, free pizza and more.

Topic: Optimizing and Performance Tuning your Applications

Jeff McWherter is the Director of Software Development at A.J. Boggs and Company in Okemos, MI. He graduated from Michigan State University with a degree in Telecommunications and Computer Science, and has twelve years of professional experience in software development. He is a founding member and current Program Director for the Greater Lansing Users for .NET ( He enjoys profiling code, applying design patterns, finding obscure namespaces, and long walks in the park. His lifelong interest in programming began with a Home Computing Magazine in 1983, which included an article about writing a game called Boa Alley in BASIC.

Friday, August 03, 2007

Easy and effective desktop publishing / page layout software

Over the years I have often needed to make high-quality visually-attractive flyers, announcements, brochures, etc. I needed specialized desktop publishing / page layout software that is inexpensive and very easy to use since I didn't want to spend a lot of time learning complex publishing software.

I am once again putting together a flyer this weekend and I am back to using the latest version of the same program that has always worked very well for me in the past and still works just great.

It is such an impressive solution and great piece of software that I want to give it some much deserved public credit with this blog entry. If you ever have the need to go beyond Microsoft Word to get a nice looking document, you likely can't go wrong by checking out PagePlus X2 by Serif Ltd.

Hope that helps,
Joe Kunk

Wednesday, July 18, 2007

Greater Lansing User Group .net (GLUGnet) July 2007 Meeting

Where: 1145 Engineering Building, Michigan State University

When: Thursday July 19th 2007 6:00-8:00 PM

What: We always have give away hundreds or thousands of dollars worth of swag along with free pizza and pop for everyone. This month we will also be announcing the opening of the Flint branch and our Vice President Vivek has secured all our membership 100 MB of free hosting from Verio.

Nationally known Julie Lerman will be our first female speaker. She will be presenting on ADO.NET Entity Framework.

Visit our website for more details:

Wednesday, July 11, 2007

Windows Server® 2008, Visual Studio® 2008 and Microsoft SQL Server™ 2008 Joint Launch Announced

Today, at the Microsoft® Worldwide Partner Conference 2007 in Denver, CO, Kevin Turner, Microsoft's Chief Operating Officer, announced that Windows Server® 2008, Visual Studio® 2008 and Microsoft SQL Server™ 2008 will launch together at an event in Los Angeles on Feb. 27, 2008, kicking off hundreds of launch events around the world.

For more info, click here .

Joe Kunk

Friday, June 22, 2007

.Net in-memory data table can have columns of any data type

Every once in a while you encounter an overlooked feature of a class that turns out to be really useful. I noticed recently that in-memory data tables in .Net 2.0 can have fields of any type, not limited to just the data types that SQL Server supports. Potentially very useful.

Joe Kunk

Thursday, June 21, 2007

Lansing MI area .Net user group meeting tonight

The Greater Lansing User Group for .Net is meeting tonight at room 1145 Engineering Building on Michigan State University Campus. We have a great speaker - Josh Holmes, Architecture Evangelist from Microsoft, will be speaking on What Does an Architect Do Anyway?. The meeting is from 6:00pm - 8:00pm. Free pizza and soda will be provided and it is a free event to anyone interested in seeing the presentation. We will have door prizes valued over $2,000 retail. So if you are in the Lansing MI area, please attend to hear a great presentation, eat some free food, and maybe win some awesome door prizes.

More information is available at the GLUGnet web site.

Joe Kunk
GLUGnet President

Friday, May 25, 2007

Can't use DIME attachment method in WSE 3.0

I have been exploring the Amazon Web Services using .Net SOAP, especially the Amazon S3 service. One of the issues with using SOAP (as opposed to REST) is that inline file uploads are limited to 1 megabyte in size. If you want to send larger attachments via SOAP, Amazon supports WSE 2.0's DIME technology.

All other things being equal, I prefer to develop in the latest version of any toolset. Since WSE 3.0 has been released, I was curious to know if I could use DIME from within WSE 3.0 instead of WSE 3.0's MTOM technology. The answer is no. As far as I can determine, in order to use DIME, you must use WSE 2.0.

I hope you find this helpful. Often I have what I believe is a simple question and when I Google for the expected simple answer, all I find is a 10+ page article. While is it nice to have lots of background information, at times it is nice to get a simple answer to a simple question.

Joe Kunk

DIME - Direct Internet Message Encapsulation
MTOM - Message Transmission Optimization Mechanism
WSE - Web Services Enhancements for Microsoft .NET

Amazon Web Services
How to: Add Attachments to a SOAP Message by Using DIME
What's New in WSE Version 3.0

Saturday, February 03, 2007

Basics.Net: Structures

The fundamental data type in .Net is a value type. In the .Net 2.0 framework, these include SByte, Byte, Int16, Int32, UInt32, Int64, Single, Double, Decimal, Char, Boolean, IntPtr, and DateTime. These are the types that developers initially become familiar with when learning the language. See more on C# value types, VB.Net value types

Each of these logically represent a single value, are a fixed size in memory of 16 bytes or less, are kept on the high-performance stack, and the assignment of the value to another variable makes another copy of the data.

A very useful extension of the value type is the known as a Structure (VB.Net) or struct (C#). These are essentially custom classes that, like all classes, can have constructors (the new operator), public/private properties, and public/private methods. The private variables within the structure must consist solely of value types and not exceed 16 bytes total in size.

Like primitive value types, structures logically represent a single value, are a fixed size in memory of 16 bytes or less, are kept on the high-performance stack, and the assignment of the value to another variable makes another copy of the data.

Structures allow you to create high-performance single-value custom data types with custom behaviors and characteristics enforced by its methods and properties. Structures can be combined in a [reference type] custom class to create a rich application object.

For example, an Employee class might contain a structure for Age that enforces a minimum value of zero and maximum value of 100, a structure for zip code that enforces a valid U.S. zip code, a structure for email that enforces a properly formatted email address, etc. You may wish to create a library of common structures to be used by multiple applications.

Finally, below is an source code example of a simple structure, a more complex structure, and and example of using them both. The source code is presented in both C# and VB.Net.

Click here to view C# Struct Code:

Click here to view the C# Code to use the struct:

Click here to view the VB.Net Structure Code:

Click here to view theVB.Net Code to use the structure:

Back to Basics

As Vice-President of the user group GLUGnet (Greater Lansing User Group for .Net), I work with the other Board members to help bring in guest speakers each month that explore the latest technologies. Focusing on what is new and interesting is appropriate for a monthly 90-minute presentation, and we will continue to do so.

That said, many of our members are newer to the .Net platform and would appreciate and benefit from more introductory material to help them successfully traverse what is clearly a substantial learning curve. The curse of a rich framework and a sophisticated development environment is that there is a lot to learn!

To address this need, I will start posting here a series of "Basics.Net" blog posts that will explain core concepts, with examples in both VB.Net and C#. These posts will be "bite-sized" in that each will cover only a single concept and can be reviewed and committed to memory in 15 minutes or less. Whenever possible, the significance of the concept will be included, making it easier to mentally organize the concept within the greater .Net concept space. I am confident that you will find these posts to be a fresh and useful treatment of the concepts.

I am happy to offer this service to our GLUGnet members or any other member of the .Net community. I am happy to accept suggestions for topics that are of interest, so please let me know of any topics or concepts that you would like to have covered.

Joe Kunk

Wednesday, January 17, 2007

SQL Server Compact Edition

On January 17th, Microsoft released SQL Server Compact Edition ( I'm very excited about this release as it finally makes the development of the disconnected smart client practical. It is ideal for any application that needs a very small but capable single-user database to ship with their product, and it is free. Not surprisingly, it has features to synchronize with Microsoft SQL Server and Microsoft Access. There are too many features to mention in this post, but check it out!

GLUGnet (Lansing MI .Net User Group) meeting 01/18/2007

Think LINQ!

Think LINQ doesn't affect you? Think again. LAnguage Integrated Query sits on top of the newest, coolest and perhaps the most powerful extensions to OOP in years and is a central figure in ADO.NET (3.0) Entity Framework and may change the way you approach database development forever.

In this presentation, your always unpredictable but seldom funny President introduces you to new .NET 3.0 and 3.5 features like extension methods, lambda calculus, dynamic types and LINQ. Be sure not to miss it!

Speaker: Paul Kimmel is the VB Today columnist for and and has written several books on object-oriented programming, including the recently released Visual Basic .NET Power Coding from Addison-Wesley and the upcoming Excel VBA 2003: Programmer's Reference from Wiley. He is the Chief Architect for Software Conceptions and is available to help design and build your next application. Paul is also the president of GLUGNET and he has done many presentations for the group before.

Need directions? Just follow this link:

As always, please tell friends, co-workers and colleagues about the group. Everyone is welcome, they don't have to be geeks. Pizza and drinks will be served. Sorry for the short notice. Hope to see you there.

Membership Director