Malisa Ncube - C# and .NET Delights

Mumblings about the software development using Microsoft technologies.

  Home  |   Contact  |   Syndication    |   Login
  22 Posts | 0 Stories | 36 Comments | 0 Trackbacks

News

Twitter












Tag Cloud


Archives

Post Categories

Thursday, October 22, 2009 #

We have scheduled for a .NET Usergroup meeting next week on the 30th October 2009. I arranged with our training department and they have agreed to ensure that we have the room, support in resources including a laptop, projector and anything else which may be necessary. Happy days! They have also guaranteed that we can have the room each month on the last Friday. I had been in communication with Craig Murphy, who took up my tweet and made it his task to see how we can get helped in making this possible while aligning to Microsoft principles.

I’m very excited about this opportunity and believe that we will take it up as developers in Uganda and share, learn, educate, socialise and make value out of our usergroup and engagement in the development of software using Microsoft technologies.

It will also be nice to have a number of activities e.g. NerdDinner and GeekBeer or something like that.

If you are wondering how to get to our training centre, just get to Mulago and enter the hospital complex through the main gate and just proceed straight ahead and you will see the building below.

 clip_image002

Our Training room is shown below.

 clip_image002[5]

NOTE: If you are going to attend i will need to know so that we can reserve space and help you get here, so drop an email to me at malisa [dot] ncube [et] gmail [dot] com.

_______________________________________________________________________________________________

Uganda .NET User Group Meeting

Venue: Infectious Diseases Institute (Training Centre)

Date: 30th October 2009

Time: 6:00 PM to 8:00 PM (Prompt)

Speaker: Malisa Ncube

Topic: How we are to run the Usergroup (Discussion)

 

Technorati Tags: ,

Monday, July 06, 2009 #

Some of the feedback from the users. ICEA stands for Integrated Clinic Enterprise Application, the EMR system which we have just implemented.

image

I thank the users for being patient with us during requirements gathering, and for not laughing at some of our stupid questions.

Technorati Tags:

Friday, June 12, 2009 #

This post is just to show some of the screenshots of the EMR software that we developed and deployed in the past few weeks in our clinic. Some of the features include:

- Patient Registration

- Return visit entry and scheduling

- Entry for Patient history, Vital signs, ART History, Online Prescriptions, Online lab orders, Counseling, Workflow

- Role base security with windows based authentication

- Office 2007 Themes (Don’t know whether to call this a feature) Thanks to Component Factory

 

image

 

The lab requisitions view

image

Some technical summary

- Based on a custom plugin framework

- SQL Server 2005 (can be configured for any database)

- Uses Krypton Components from Component Factory

- Uses Fastreport.NET from

- .NET Framework 3.5 and Visual Studio 2008

- C#

- Nant for unit testing

- Cruise Control  & Subversion for Continuous Integration

 

Other

- Currently about 30 users online and using the system, simultaneously, they will increase to 60 before the end of the month as we include more users.

 

Things to do in the soon future

- Axum – there is need to fully utilise the processor power, therefore a parallel approach will increase efficiency of out applications.

- Biometrics – verification of U are U.

- SQL Server Business Intelligence suite – for analytical purposes

-  SQL Server reporting services / Some custom reporting portal

- Touch screen computers for vital signs / embedded software supporting devices for vital signs

- Add plugins for research modules


Tuesday, June 09, 2009 #

clip_image002

1. Introduction

This posting is a continuation from the previous post on Managed Extensibility Framework (MEF). If you are a new to MEF you will need to visit the MEF blog at Codeplex or Glenn’s Blog here. You will need to download the framework so you can also find out how it leverages the power of extensibility into your applications.

In the previous post I showed how you can quickly create a MEF application and use the attributes to import and export different parts, and use the container to compose the parts. My example used parts from the same assembly - using Assembly.GetExecutingAssembly(); but in this example I will include separately compiled assemblies (plugins) which are read by the DirectoryCatalog from a specific location.

So essentially what you will see the following in this post

  1. The Aggregate Catalog
  2. The Directory Catalog
  3. Making the host accessible from a plugin.
  4. A bit of Metadata attributes

Let look at what we had to do to come up with the new example.

2. The Aggregate Catalog

To do this we had to combine the AssemblyCatalog and the DirectoryCatalog into an AggregateCatalog. The AggregateCatalog combines a number of catalogs into one, and that is exactly what we wanted to do because we wanted the exports from the external assemblies to satisfy the imports of the executing assembly.

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

static void Main()

{

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

Program program = new Program();
var host = program.Compose();

Application.Run(host as Form1);

}

/// <summary>
/// This will compose the parts as defined
/// </summary>
/// <returns>IPluginHost</returns>

public IPluginHost Compose()

{
var catalog = new AggregateCatalog(); //The special type of catalog that combines a number of catalogs
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog(@".\Plugins", "*.dll"));

//At this poing the catalog contains the (1) ExecutingAssembly and (2) the assenblies in \Plugin folder
CompositionContainer container = new CompositionContainer(catalog);

return container.GetExportedObject<IPluginHost>();
}

3. Contracts

The first thing to do was to create a project which contains contracts. These are simply interfaces that will be shared between the host application and the plugins. The contracts define the expected common operations, data items between plugins and host application. They make a uniform way in which you can interact with those objects, while oblivious about the implementation details. As shown below the PluginHost is the contract implemented by the host application and IPlugin by plugins. You will also notice that we have included the IPluginHost in the IPlugin interface… This is because we want each of the plugins to perform some functions on the plugin host, for example show the progress bar, change theme and so forth.

public interface IPluginHost

{
void ShowProgress(string message, int percentage);
void ChangeTheme(Color color);
}

public interface IPlugin

{
IPluginHost Host {get;}
string Version { get; }
ToolStripItem[] ToolStripItems { get; }
}

4. Plugins

The next thing is to create the plugins. The code below shows that the plugin imports the IPluginHost, which in our case is the main form. We access the host / shell using Host, and as shown in the example below in the button1_Click method. We can send commands to the host ‘s progress bar using Host.ShowProgress("Processing...", i).

We have also decided to decorate the plugin with some ExportMetadata attribute which we will access from the container.

[Export(typeof(IPlugin))]
[ExportMetadata("Name", "Process Starter Plugin")]
public partial class ProcessPlugin : UserControl, IPlugin
{

[Import(typeof(IPluginHost))]
private IPluginHost host;

public IPluginHost Host
{
get
{
return host;
}
}

public ProcessPlugin()
{
InitializeComponent();
}

#region IPlugin Members
public string Version
{
get
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}

public ToolStripItem[] ToolStripItems
{
get { return null; }
}

#endregion

private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
/* Some long process*/ for (int b = 0; b < 10000000; b++) ;
Host.ShowProgress("Processing...", i);
}
Host.ShowProgress("", 0);
}

}


5. The Plugin Host / Shell

This is the main form which hosts the plugins. In it, we have declared an ExportCollection of IPlugin and decorated it with the [Import] attribute, so that during composition we will have all the IPlugin objects created and assigned to it. The advantage of using the ExportCollection than the IEnumerable is that it enables us to also extract the metadata from the exported part.

[Export(typeof(IPluginHost))]

public partial class Form1 : Form, IPluginHost

{

/// <summary>
/// Plugins will be imported into this property
/// </summary>
[Import(typeof(IPlugin))]
ExportCollection<IPlugin> Plugins { get; set; }
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
//Show plugins

listBox1.Items.Clear();
foreach (var plugin in Plugins)
{

//get the Plugin
var plg = plugin.GetExportedObject();

//Check if it has toolstrip items and add them to the main form
if (plg.ToolStripItems != null)
toolStrip1.Items.AddRange(plg.ToolStripItems);

//Add the visible area of the plugin to the main form
tableLayoutPanel1.Controls.Add(plg as Control);

//Read the metadata and show the Name Property and the Version value from the plugins
listBox1.Items.Add(string.Format("{0} {1}", plugin.Metadata["Name"].ToString(), plg.Version));
}
}

#region IHost Members
/// <summary>
/// Show Progress bar with text message. This is controllable from the plugins because it is part
/// of the IPluginHost interface which is known by all plugins.
/// </summary>
/// <param name="message">Message shown on status bar</param>
/// <param name="percentage">progress indicator value</param>
public void ShowProgress(string message, int percentage)
{
toolStripStatusLabel1.Text = message;
toolStripProgressBar1.Value = percentage;
Application.DoEvents();
toolStripStatusLabel1.Visible = percentage > 0;
toolStripProgressBar1.Visible = toolStripStatusLabel1.Visible;
}

/// <summary>
/// This changes the color of the background and is controllable from the Plugins.
/// </summary>
/// <param name="color">Color to be use on background</param>
public void ChangeTheme(Color color)
{
BackColor = color;
}

#endregion
}


6. Conclusion

This example has attempted to illustrate how you can use the Managed Extensibility Framework to create a small plug-in application. We have also tried to make the host application and plugins independent, and have them abide to the contracts. The contracts were defined and used by the plugins to perform some actions on the plugin-host / shells regardless of the implementation details. The plugins also implemented the contracts which enabled them to have uniformity and to expose common methods and properties.

When the application starts; the running assembly and the plugins are read and mappings made between imports and exports (composition). We then get the exported object called IPluginHost which we know is the main form and run it.

 

7. Source Code

Download source here

 


Tuesday, May 26, 2009 #

This is the first posting on MEF, in which I will be explaining some things that I discovered while playing around with MEF. I should say that all the postings on my blog, including this subject do not represent my employer or any other organization, they are merely my mumblings based on my work that I do and whatever in find interesting and would like to share. Use it at your own risk, but it works on my machine.

clip_image002

To start with I would like you to read about the architecture of the MEF framework from the MEF Codeplex site.

In my MEF101, I present an example which shows how you can import composable parts from your assemble and let MEF compose and satisfy imports and enable you to come up with a simple interface made from different parts.

I created the IPlugin interface which I use in my exports.

/// <summary>

/// IPlugin Interface

/// Used by all the composable parts which will be used as plugins

/// </summary>

public interface IPlugin

{

}


 

All rectangle items visible are simple UserControls which implement the IPlugin contract. Notice that I have placed an [Export] attribute. This informs MEF that during composition that if there is something which needs [Imports] IPlugin, then the MEF will create it and satisfy that import.

/// <summary>

/// Plugin1

/// Example Plugin

/// </summary>

[Export(typeof(IPlugin))]

public partial class Plugin1 : UserControl, IPlugin

{

public Plugin1()
{
InitializeComponent();
}
}

 

All the plugins in this example have this same structure. I just changed the background color in the designer to so that we can tell that they are different.

The Main form has the following code.

 

/// <summary>
/// Form1
/// The main form. We need to have the [Export] attribute on it because on the Program.cs
/// we compose it using MEF and get its instance as an ExportedObject.
/// </summary>
[Export]
public partial class Form1 : Form
{
/// <summary>
/// Plugins
/// These imports will be satisfied by instances of the IPlugin 's
/// which in this case is the user controls with different colors.
/// </summary>
[Import]
public IEnumerable<IPlugin> Plugins;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
// We loop through all the instances of the plugins an add them to the tableLayoutPanel
// and because we know that all of them are UserControls we can cast the as Control.
foreach (var plugin in Plugins)
{
(plugin as Control).Dock = DockStyle.Fill;
tableLayoutPanel1.Controls.Add(plugin as Control);
}
}
}

The Program.cs has the following code.

 

static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

var form1 = Compose();
Application.Run(form1);
}

public static Form1 Compose()
{
//Create a catalog - in this case we want to import the composable parts which exist in this
//assembly, so we will use the AssemblyCatalog against the current assembly.
//
//It is also possible to import assemblies from external assemblies using the DirectoryCatalog.
//Furthermore you can combine a number of catalogs into an AggregateCatalog.
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

//Create the Container - then pass the catalog into the container
var container = new CompositionContainer(catalog);

//Return the instance of Form1.
return container.GetExportedObject<Form1>();
}
}

And that’s all. Look at how we create the instance of Form1 class and use the Application class to run it.

You can download the source here

 


clip_image002

1. Introduction

Have you ever been in a situation where the users are unable to explain how an error was displayed on their computer and moreover describe the details of the error message? Well… I wonder why I asked when I already knew the answer you’ll say.

A few weeks ago, the some users started testing the system; I realized our method of tracking bugs was not nearly good. We had an excel spreadsheet which had bug details and how we can reproduce the bug. We had problems with that each tester was now using their own version of a bug tracking spreadsheet, since excel does not allow shared access, so I downloaded the opensource Bugtracker.NET from codeplex to see if it could be of help, since we do not have  the Team Foundation Server.

2. Trapping unhandled exceptions

The first thing to do is to catch all unhandled exceptions, and to do that you added an event handler in the Application.

Program.cs

static class Program
{

static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
....
....
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
....
....
}


static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
Bug bug = Bug.Create();
string screenshotFile = bug.CaptureScreenShot();

bool sendScreenShot = false;
bool bugSent = false;
TaskDialog exceptionDialog = new TaskDialog();
exceptionDialog.MainIcon = TaskDialogIcon.Error;
exceptionDialog.MainInstruction = e.Exception.Message;
exceptionDialog.UseCommandLinks = false;
exceptionDialog.WindowTitle = "Error Occured!";
exceptionDialog.Content = string.Format("Unhandled Exception: {0}\nSource: {1}\nObject: {2}", e.Exception.Message, e.Exception.Source, e.Exception.TargetSite.ToString());
exceptionDialog.ExpandedByDefault = false;
exceptionDialog.ExpandedInformation = e.Exception.StackTrace;

exceptionDialog.VerificationText = "Do you want to send screenshot?";
exceptionDialog.VerificationFlagChecked = true;
exceptionDialog.FooterIcon = TaskDialogIcon.Information;

TaskDialogButton sendButton = new TaskDialogButton();
sendButton.ButtonId = 101;
sendButton.ButtonText = "Send";

TaskDialogButton cancelButton = new TaskDialogButton();
cancelButton.ButtonId = 102;
cancelButton.ButtonText = "Don't Send";

exceptionDialog.Buttons = new TaskDialogButton[] { sendButton, cancelButton };
int result = exceptionDialog.Show(null, out sendScreenShot);

if (result == sendButton.ButtonId)
{
bug.BugUrl = "http://server:88/insert_bug.aspx";
bug.MailText = string.Format("Unhandled Exception: {0}\nSource: {1}\nObject: {2}\nTrace: {3}",
e.Exception.Message, e.Exception.Source, e.Exception.TargetSite.ToString(), e.Exception.StackTrace);
bug.ProjectId = "ICEA.NET";
bug.UserName = "tester";
bug.Password = "tester";
bug.Subject = "User: [" + WindowsIdentity.GetCurrent().Name + "] "+ e.Exception.Message;

if (sendScreenShot)
bugSent = bug.WithAttachment(screenshotFile)
.ComposeMail()
.SendBugReport();
else
bugSent = bug.ComposeMail()
.SendBugReport();

if (bugSent)
MessageBox.Show("Details of error have sent to development team! Thanks.", "Message Sent", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("Could not send error details to development team!", "Message Failure", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}


}

The above code snippet will ensure that if an exception is unhandled then the method Application_ThreadException will be executed and the exception details are passed as an argument.

3. Capturing the screenshot

This is a very important part, because the screenshot will be attached to the email which is then sent to the server, so we can know what the user was doing when the exception occurred. The image is then converted to base64 format. This is simply changing the image to text so we can send it over HTTP.


public string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)
{
using (MemoryStream ms = new MemoryStream())
{
// Convert Image to byte[]
image.Save(ms, format);
byte[] imageBytes = ms.ToArray();

// Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes);
return base64String;
}
}

private string CaptureImage(Point SourcePoint, Point DestinationPoint, Rectangle SelectionRectangle)
{
using (Bitmap bitmap = new Bitmap(SelectionRectangle.Width, SelectionRectangle.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(SourcePoint, DestinationPoint, SelectionRectangle.Size);
}
//return the string which represent the image
return ImageToBase64(bitmap, ImageFormat.Jpeg);
}
}

public string CaptureScreenShot()
{
Rectangle bounds = Screen.GetBounds(Screen.GetBounds(Point.Empty));
return CaptureImage(Point.Empty, Point.Empty, bounds);
}

4. Composing the Bug Mail

public Bug ComposeMail()
{
mail = "&username=" + HttpUtility.UrlEncode(_UserName) +
"&password=" + HttpUtility.UrlEncode(_Password) +
"&from=" + HttpUtility.UrlEncode(_UserName) +
"&short_desc=" + HttpUtility.UrlEncode(_Subject) +
"&projectid=" + HttpUtility.UrlEncode(_ProjectId);

if (_Attachment != string.Empty)
mail += "&message=" + HttpUtility.UrlEncode(_Attachment);
else
mail += "&message=" + HttpUtility.UrlEncode(_MailText);

return this;
}

5. Making the Web Request (HTTP)

Thanks to fiddler, I was able to analyse the details of the contents that are posted to the Bugtracker.NET. The tool downloadable with BugTracker.NET called bt2312 is written in C++. I translated part of it to C# and luckily .NET has very interesting libraries which allow you to perform most of the things out of the box.

clip_image006

Fiddler enables you to inspect your web requests and that was helpful, because after i inspected the web requests from bt2312 i was able to replicate the behaviour.

clip_image008

The labels above indicate the steps that have been made during the web request.

1. The application sends the data using HTTP.

2. The data sent (as text).

3. The response.

public bool SendBugReport()
{
bool Success = false;
ASCIIEncoding encoding = new ASCIIEncoding();


byte[] buffer = encoding.GetBytes(mail);

// Prepare web request...
HttpWebRequest myRequest =
(HttpWebRequest)WebRequest.Create(_BugUrl);

// We use POST ( we can also use GET )

myRequest.Method = "POST";

// Set the content type to a FORM
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.UserAgent = Application.ProductName;
// Get length of content
myRequest.ContentLength = buffer.Length;

// Get request stream
Stream newStream = myRequest.GetRequestStream();

// Send the data.
newStream.Write(buffer, 0, buffer.Length);

// Close stream
newStream.Close();


// Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable.
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myRequest.GetResponse();

// Display the contents of the page to the console.
Stream streamResponse = myHttpWebResponse.GetResponseStream();

// Get stream object
StreamReader streamRead = new StreamReader(streamResponse);
Char[] readBuffer = new Char[256];

// Read from buffer
int count = streamRead.Read(readBuffer, 0, 256);

while (count > 0)
{
// get string
String resultData = new String(readBuffer, 0, count);
// Write the data
Success = resultData.Contains("OK:");
Console.WriteLine(resultData);
// Read from buffer
count = streamRead.Read(readBuffer, 0, 256);

}
// Release the response object resources.
streamRead.Close();
streamResponse.Close();

// Close response
myHttpWebResponse.Close();

return Success;
}


I have attached the Bug.cs class which contains all the code for creating a Bug report and sending it to and BugTracker.NET server. Apologies, i have not put a lot of comments in my code, if i revise this post i will probably do something about it.

Download Bug.cs


Tuesday, April 21, 2009 #

In last few months i have been looking into a number of technologies, methodologies and tricks on how i can design and expedite the development of the EMR software. Some of the critical success factors of this EMR were extensibility because the institution has a huge research component and many research studies have various requirements and new ones come through anytime. Therefore, the EMR architecture is supposed to allow the developer to simply create a plug-in using a VS template and simply add his/her new business objects and compile it and add it to the plug-in folder so it can be loaded by the shell application at runtime.

We also used the Krypton Suite for our user interface. We bought the with source, and we modified the components so that they expose the KryptonRibbonGroup, KryptonRibbon, KryptonRibbonTab and KryptonRibbonRecentDoc. These make it easy for us to create ribbon elements in plug-ins and the shell application will register them at runtime automatically.

While i was doing my research i stumbled on the managed Extensibility Framework (http://www.codeplex.com/MEF) from Microsoft, and think it is really cool. I particularly like that fact that you can use metadata(attributes) to define dependencies in your architecture and those get satisfied at runtime.  There is also more stuff on http://www.codeplex.com/MEFContrib that is interesting about MEF.

In the next couple of weeks i will be frequently blogging about my experiences in working with my custom plug-in framework and my understanding of MEF why i think it has a place in the future of software applications that need to evolve. I will also put my thoughts around what i think would be good as cosmetics or syntactic sugar in MEF and features that i think are vital for my kind of applications.

Cheers!


In should say that the first time I looked lambda and linq, making pivot tables was not the main thing I thought about. After helping a few people including one at the msdn (http://is.gd/oG8q) social site, I realized that I also needed to use the lambda in my own EMR project.

The problem was to have the lab data of a patient transformed into a pivot table and some of the data summarized by averaging or counting.

I had the data in the table in the following format.

 

PatientID

TestNo

TestName

TestVar

Result

ResultUnit

CollectedDate

1

1010

CBC2

LYABS

2.0

10^3 /uL

1/6/2008 12:00:00 AM

1

1010

CBC2

MCHC

33.4

g/dL

1/6/200812:00:00 AM

 

I then had to write a linq expression to produce the following results.

Patient

TestNo

CollectedDate

MCHC

LYABS

TotalTests

1

1010

9/30/2008 12:00:00 AM

32.8

1.9

19

1

1010

1/6/2008 12:00:00 AM

33.4

2.0

21

1

1010

3/3/2008 12:00:00 AM

null

null

1

1

1010

7/2/2008 12:00:00 AM

33.0

2.2

21

           

The linq was as follows.

var labResults = from lab in CoreLabResults
                 where lab.Patient == 8
                 group lab by new { lab.Patient, lab.TestNo, lab.CollectedDate }
                     into labtests
                     select new
                     {
                         labtests.Key.Patient,
                         labtests.Key.TestNo,
                         labtests.Key.CollectedDate,
                         MCHC = labtests.Where(lab => lab.TestVar == "MCHC").FirstOrDefault().Result,
                         LYABS = labtests.Where(lab => lab.TestVar == "LYABS").FirstOrDefault().Result,
                         TotalTests = labtests.Count()
                     }

The lambda expression for the above was be as follows

var labResults = CoreLabResults
    .Where(lab => lab.Patient == 8)
    .GroupBy(lab =>
            new
            {
                Patient = lab.Patient,
                AccessionNo = lab.AccessionNo,
                CollectedDate = lab.CollectedDate
            }
            )
    .Select(labtests =>
            new
            {
                Patient = labtests.Key.Patient,
                AccessionNo = labtests.Key.TestNo,
                CollectedDate = labtests.Key.CollectedDate,
                MCHC = labtests.Where(lab => (lab.TestVar == "MCHC")).FirstOrDefault().Result,
                LYABS = labtests.Where(lab => (lab.TestVar == "LYABS")).FirstOrDefault().Result,
                TotalTests = labtests.Count()
            }
);

I still need to find out how I can replace the column names like “MCHC” by computed ones. I may probably need to use the expression trees to dynamically create lambda expressions that have variable number of columns. E.g. in financial statements - I would not want to hardcode column names of each financial period year/quarter.

Apologies – syntax highlighting using Live writer is hell.


Thursday, February 05, 2009 #

I have been allocated an account in Windows Azure Developer Portal for CTP - and very excited. Now i'm looking into the corridors of Windows Azures and downloading the SDKs.

Am looking at these sites

Deploying a Servce on Windows Azure
http://msdn.microsoft.com/en-us/library/dd203057.aspx

Windows Azure SDK
http://www.microsoft.com/downloads/details.aspx?FamilyId=BB893FB0-AD04-4FE8-BB04-0C5E4278D3E9&displaylang=en

Microsoft SQL Data Services SDK
http://go.microsoft.com/fwlink/?LinkID=129449

Using Microsoft ADO.NET Data Services
http://msdn.microsoft.com/en-us/library/cc907912.aspx

Microsoft SQL Data Services SDK
http://go.microsoft.com/fwlink/?LinkID=129448


Thanks to the Azure Services Platform Team 

Wednesday, January 28, 2009 #

I was very delighted when i found the CopyAsHTML tool on http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/.
It just made me think about going through my entire list of blogs postings, to edit them so the code looks ok.

Thanks to Colin Coller the developer of this great tool. Very ideal for bloggers.

Cheers.


I have encountered a number of errors with visual studio 2008, which i'm now getting convinced are bugs. The first i will talk about is the Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)

The reason i think it is a bug in VS2008 is the inconsistencies around it. The code below is supposed to create Toolbox items based on some assemblies that i add into the Assembly cache.

     public void RunCreator(DTE dte)

        {

            if (DllFileList.Count < 1)

            {

                Console.WriteLine("Could not find DLLs!");

                return;

            }

 

            Console.WriteLine("Creating context for templates.");

 

            string tempProjPath = Path.Combine(Path.GetTempPath(), "tempProj");

 

            if (Directory.Exists(tempProjPath))

            {

                Directory.Delete(tempProjPath, true);

            }

 

            Console.WriteLine("1");

            string fileName = ((Solution2)dte.Solution).GetProjectTemplate("WindowsApplication.zip", "CSharp");

 

            Console.WriteLine("2");

            switch (fileName)

            {

                case null:

                case "":

                    fileName = ((Solution2)dte.Solution).GetProjectTemplate("WindowsApplication.zip", "VisualBasic");

                    break;

            }

            Console.WriteLine("3");

 

            if ((fileName == null) || (fileName == string.Empty))

            {

                fileName = ((Solution2)dte.Solution).GetProjectTemplate("WindowsApplication.zip", "csharp");

            }

 

            Console.WriteLine("Adding solution template.");

            Application.DoEvents();

            System.Threading.Thread.Sleep(10000);

 

            dte.Solution.AddFromTemplate(fileName, tempProjPath, "temp.cproj", false);

            dte.Solution.Close(false);

 

            Application.DoEvents();

 

            dte.ExecuteCommand("View.PropertiesWindow", "");

            Window window = dte.Windows.Item(EnvDTE.Constants.vsWindowKindToolbox);

 

 

            Application.DoEvents();

            window.Activate();

            ToolBox box = window.Object as ToolBox;

            ToolBoxTabs tabs = box.ToolBoxTabs;

            ToolBoxTab tab;

 

            try

            {

                tab = FindOrCreateTab(tabs, TABLABLE, false);

 

                if (_option == "install")

                {

                    if (tab != null)

                    {

                        tab.Delete();

                        Console.WriteLine("Deleting tab: [" + TABLABLE + "]");

                    }

 

                    Console.WriteLine("Locating tab.");

                    tab = FindOrCreateTab(tabs, TABLABLE, true);

                    dte.ExecuteCommand("View.PropertiesWindow", "");

                    tab.Activate();

 

                    Console.WriteLine("Acquiring Components.");

                    this.ComponentDataList = this.GetAllComponentsData();

 

                    if (ComponentDataList.Count < 1) return;

 

                    Console.WriteLine("\nRegistering...");

 

                    foreach (ComponentData componentData in this.ComponentDataList)

                    {

                        RegComponent(tab, componentData);

                        System.Threading.Thread.Sleep(500);

                    }

                }

                else

                {

                    if (tab != null)

                    {

                        tab.Delete();

                        Console.WriteLine("Deleting tab: [" + TABLABLE + "]");

                    }

                }

 

 

                Console.WriteLine("Done...");

            }

            catch

            {

                throw;

            }

        }


The moment i access
dte get the error.

bug

I have not found any solution on google and all attempts by others have not resulted in solutions. I spent the entire day trying to solve this, but as a developer i will still persist.

Monday, January 05, 2009 #

MethodValidatorsAndConverters

1. Introduction

This is a follow-up to the "An attribute based approach to business object validation" article in which i introduced the use attributes and reflection to validate business objects. I also promised to write another article on data converters and method based validators, and here it is.

2. Background

In this article i will use an example to show how you can validate your business object using a method based attribute and how you can convert data assigned to properties when it is saved and re-convert it when the business object is read. All this will be based on attributes.

Sometimes a need arises, to store data differently from the way it is viewed. For example if you have a field which would have comments you may want to compress them on storage. This will certainly be good for your bandwidth - especially for us here in Africa.

3. The Business object

In this example lets use a Candidate object. I'm assuming a company considering interviewees.

I would like you to look closely at the attributes that we have used to decorate the object below. The attributes will enable validation of the business object much easier than having to write some instructions on the presentation layer. Some attributes below will help in converting the data so that it is stored differently from the way it is viewed.

    public class Candidate : EntityObject
{

#region Constructors

public Candidate(DataContext dataContext)
{
}

public Candidate()
{
}

#endregion

#region Properties

///
/// Property that describes the FirstName of this
/// We ensure that this is unique by using a method based validator.
///

[Required]
[MethodRule("Unique")]
public int CandidateNo { get; set; }

///
/// Property that describes the Title of this
/// This property defaults to "Mr" if the user does not specify a value.
///


[DefaultValue("Mr")]
public string Title { get; set; }

///
/// Property that describes the Firstname of this
///

[Required]
public string Firstname { get; set; }
///

/// Property that describes the Lastname of this
///

[Required]
public string Lastname { get; set; }
///
/// Property that describes the Age of this
///

[InRange(18, 95)]
[DefaultValue(30)]
public int? Age { get; set; }
///
/// Property that describes the RegistrationDate of this
/// We use a method to validate this property. I have placed the Validators
/// in their region below.
///

[MethodRule("ValidRegistration")]
public DateTime RegistrationDate { get; set; }
///
/// Property that describes the CV of this
/// In this attribute we have a DataConverter which should compress the CV
/// text before it is stored.
///

  [DataConversion(typeof(StringZipper))]
public string CV { get; set; }

#endregion

#region Methods
public override string ToString()
{
return string.Format(
"ObjectID: {0} \nName: {1} \nSurname : {2} \nAge: {3} \nRegistration Date: {4} \nCV: {5}",
ObjectID, Firstname, Lastname, Age, RegistrationDate, CV); }
#endregion

#region Method Based Validators

public void ValidRegistration(object sender, ValidateEventArgs e)
{
e.Valid = true;
if (RegistrationDate.Date < DateTime.Parse("01/01/2008 8:00:00"))
{
e.Valid = true;
e.ErrorMessage = "Impossible! The client could not have registered before we started this business.";

//Set to the minimum date - if you want
e.Property.SetValue(this, DateTime.Parse("01/01/2008 8:00:00"), null);
}

}

public void Unique(object sender, ValidateEventArgs e)
{
if (this.IsNew)
{
// A bit of LINQ to find if we have this candidate
var query = from candidate in DataSettings.DataContext.EntityObjects.OfType()
where candidate.CandidateNo == this.CandidateNo
select candidate;

e.Valid = query.Count() <= 1;
e.ErrorMessage = "The Candidate number must be unique.";
}
}

#endregion

}

The collection of attributes included in this object are:

  • Required - To ensure that the property value is entered.
  • InRange - A range validator.
  • DefaultValue - Indicates that if value is omitted, the default will be assigned to property.
  • MethodRule - Executes a method specified on saving the object.
  • DataConversion - converts the data assigned to property using a conversion class specifed.

In this example, i'm saving all my objects in a cache-like object, which i call DataSettings.DataContext.EntityObjects, and i use the code below to find an object.

             Guid CandidateID = (Guid)dataGridView1[9, e.RowIndex].Value;

Candidate candidate = new Candidate(); //You may decide to use static methods for this
candidate.Load(CandidateID);
if (candidate != null)
{
candidateBindingSource.DataSource = candidate;
cVTextBox.Text = candidate.CV;
}

If we save the data we would have the following.

Compressed

4. The Business object Base

In this example i would like you to focus on the area where i create a delegate using Delegate.CreateDelegate(typeof(EventHandler)....

    public class EntityObject : IEntityObject
{

#region Internal Fields

internal Guid objectID;
public bool IsNew { get; set; }
public bool IsDirty { get; set; }

#endregion

#region Public Properties

///
/// The Errors collection to keep the errors. The validation method populates this.
///

public readonly List Errors = new List();

public DataContext dataContext;

public Guid ObjectID
{
get
{
return objectID;
}
set
{
objectID = value;
}
}

#endregion

#region Constructors

public EntityObject()
{
if (dataContext == null)
{
if (DataSettings.DataContext == null) DataSettings.DataContext = new DataContext();
this.dataContext = DataSettings.DataContext;
}

//Create unique object identifier
objectID = Guid.NewGuid();
IsNew = true;
IsDirty = false;
}

public EntityObject(DataContext dataContext)
{
if (dataContext == null)
{
if (DataSettings.DataContext == null) DataSettings.DataContext = new DataContext();
this.dataContext = DataSettings.DataContext;
}
else
this.dataContext = dataContext;

}

#endregion




.....



public virtual void Validate(object sender, ValidateEventArgs e)
{
//Initialise the error collection
Errors.Clear();

//Enable calling the OnValidate event before validation takes place
if (this.OnValidate != null) this.OnValidate(this, new ValidateEventArgs());
try
{
foreach (PropertyInfo prop in this.GetType().GetProperties())
{
/* Get property value assigned to property */
object data = prop.GetValue(this, null);

#region Default Value setting
...
#endregion

#region IsRequired Validation
...
#endregion

#region InRange Validation
...
#endregion

#region MethodBasedValidation
/* Check if property value is Method Based Validation */
foreach (object customAttribute in prop.GetCustomAttributes(typeof(MethodRuleAttribute), true))
{
//Create event handler dynamically
EventHandler eventHandler = Delegate.CreateDelegate(typeof(EventHandler), this,
(customAttribute as MethodRuleAttribute).ValidationMethod) as EventHandler;
ValidateEventArgs args = new ValidateEventArgs(prop, string.Format("Value assigned to {0} is invalid.", prop.Name));

eventHandler(this, args); // Execute event handler
if (!args.Valid)
{
Errors.Add(new Error(this, prop.Name, args.ErrorMessage));
}
}
#endregion

#region Data Converters

/* Check if property value is required */
foreach (object customAttribute in prop.GetCustomAttributes(typeof(DataConversionAttribute), true))
{
Type conversionType = (customAttribute as DataConversionAttribute).ConverterType;
prop.SetValue(this, Converter.Instance(conversionType).Change.ToPersistentType(data), null);
}

#endregion
}
}
catch (Exception ex)
{
//
throw new Exception("Could not validate Object!", ex);
}
finally
{
//Enable calling the OnValidated event after validation has taken place
if (this.OnValidated != null) this.OnValidated(this, new ValidateEventArgs());
}

}


5. Method Based validators.

A delegate is a pointer to a method or event handler.

The .NET framework has a very interesting feature which enables us to create delegates at runtime and bind them to event handlers.

The following is the algorithm for a method based validator

    (a) Go to next Property.
    (b) If not exists exit.
    (c) Find associated attributes
    (d) If the attribute is a MethodValidator proceed, else jump to (i).
    (e) Create delegate, bind it to the method indicated on MethodValidator.
    (f) Create new Event arguments of validation type.
    (g) Execute associated eventhandler, using the event arguments.
    (h) If result on the event arguments is not valid, add Error to this.Errors collection.
    (i) Go to (a).

The main thing behing the method based validator is the ability to bind the evant at runtime to a property, and thanks again to reflection. We can find attributes associated with a property and then we create a delegate which points to the event handler presented at the attribute. We then invoke the eventhandler using event arguments, which would then help us to obtain response from the method call. The response from the event handler is then used to add associated Error to the Errors collection of this object.

                       //Create delegate and map it to ValidationMethod of current property
EventHandler eventHandler = Delegate.CreateDelegate(typeof(EventHandler), this,
(customAttribute as MethodRuleAttribute).ValidationMethod) as EventHandler;

//Create event arguments which we will inject into handler, and then review the modifications on it
ValidateEventArgs args = new ValidateEventArgs(prop, string.Format("Value assigned to {0} is invalid.", prop.Name));

eventHandler(this, args); // Execute event handler through the delegate
if (!args.Valid) // Read argument after it has been affected by the event handler
{
Errors.Add(new Error(this, prop.Name, args.ErrorMessage));
}

For more information on dynamic delagate creation, consult the MDSN documentation (here).

6. The Data Converters

The data converters enable us to change the data before it is stored. They inherit from the DataConverter class below.

We made the methods virtual so that you can override them for any implementation of conversion. The ToPersistentType(object value) is to enable conversion to storage type while FromPersistentType(object value) converts data back to viewable type.

     public class DataConverter
{
public virtual object ToPersistentType(object value)
{
throw new NotImplementedException();
}

public virtual object FromPersistentType(object value)
{
throw new NotImplementedException();
}
}

The class below is one data converter, that may be used to compress string data. You may decide to create another one to compress other data types or encrypt certain data when its stored. That reminds me of a payroll application which i built and the clients did not want the column which stores salary to be readable to database admnistrators.

     class StringZipper: DataConverter
{
public override object ToPersistentType(object value)
{
return value == null ? null : (object)Zipper.Compress((string)value);
}

public override object FromPersistentType(object value)
{
return value == null ? null : (object)Zipper.Decompress((string)value);
}
}

Code below shows how you can load the and object within the base class.

         public void Load(Guid entityObjectID)
{
EntityObject entityObject = new EntityObject(DataSettings.DataContext);
entityObject = dataContext.Load(entityObjectID);
entityObject.IsNew = false;
entityObject.IsDirty = false;

if (entityObject != null)
{
foreach (PropertyInfo prop in entityObject.GetType().GetProperties())
{
/* Get property value assigned to property */
object data = prop.GetValue(entityObject, null);

#region Data Converters

if ((prop.Attributes == PropertyAttributes.None) && (data != null))
prop.SetValue(this, data, null);

/* Check if property value is required */
foreach (object customAttribute in prop.GetCustomAttributes(typeof(DataConversionAttribute), true))
{
Type conversionType = (customAttribute as DataConversionAttribute).ConverterType;
prop.SetValue(this, Converter.Instance(conversionType).Change.FromPersistentType(data), null);
}
#endregion
}
}
else
{
Console.WriteLine("Could not find object!");
}
}

7. Techniques used in this article

  • Reflection
  • Generics
  • Anonymous types
  • Delegates / Dynamic Delegates
  • LINQ

8. Challenges and Limitations

    (a) Using reflection .SetValue() on attributes without setters specified.
    (b) DataBinding for convertable properties is not supported, else the user will see some garbled text on some controls.
    (c) I don't know whether to call it a limitation - typesafety and generics are not available on attributes.

9. History

Published on 03/01/2009, follow-up of Attribute based validation

10. Source Code

All source code for this article is available for download from CodeProject

 

Technorati Tags: ,,

Monday, December 22, 2008 #



Attribute based validation for business objects

1. Introduction

This article shows how you can use attributes to validate your business objects. You may have had to write tones of code to validate your business objects without taking advantage of attributes. The value of attributes comes when you need to apply the same rules on a number of properties and the only thing you have to do is to decorate your property accordingly. For other articles on attribute based validation please check the Visual Studio Magazine and Ennis Ray Lynch, Jr.

2. Background

The .NET compiler enables us to richly embed metadata into an assembly, which can be accessed at a later time by reflection. Attributes are decorative clauses that can be placed on either classes, interfaces, methods, assemblies, modules or other items. They become part of the assembly metadata and can be used to assign rules to the compiler or enable developers to reuse code to perform various operations including validation, tracing and type conversion. Attributes are inherited from the System.Attribute class

Examples of attributes/p>

[Serializable, XmlRoot(Namespace = "www.idi.ac.ug")]
public class Person : EntityObject {
[XmlAttribute]
public string Firstname {get ; set;}

[XmlAttribute]
public string Lastname {get ; set;}
}
Some attributes that are commonly used include

[Obsolete]Tells compiler to issue a warning because the decorated method is obsolete.

[Serializable] Tells compiler that the object is can be serialized to some storage, as Xml, Text or Binary.

[Assembly: ] These are assembly level attributes that are applied on the entire assembly.

[DefaultValue] this is used to give a default value.

In our case we would like to see how we can build our own custom attributes which we can use to make business object validation easier. The usability of attributes would surely save you time, code and the stress of having to individually validate each property of a business object.

/// <summary>

/// Person class.

/// This class represents the person

/// Author: Malisa Ncube

/// </summary>

public class Person : EntityObject

{

    /// <summary>

    /// Property that describes the Title of this <see cref="Person">

    /// </see></summary>

    [DefaultValue("Mr")]

    public string Title { get; set; }

 

    /// <summary>

    /// Property that describes the FirstName of this <see cref="Person">

    /// </see></summary>

    [Required]

    public string FirstName { get; set; }

 

    /// <summary>

    /// Property that describes the LastName of this <see cref="Person">

    /// </see></summary>

    [Required(ErrorMessage = "LastName must have a value")]

    public string LastName { get; set; }

 

    /// <summary>

    /// Property that describes the Age of this <see cref="Person">

    /// </see></summary>

    [InRange(18, 95)]

    [DefaultValue(30)]

    public int? Age { get; set; }

 

    public override void Validate(object sender, ValidateEventArgs e)

    {

        base.Validate(sender, e);

 

        //Custom business rules

        if (this.Age == 25)

        { Errors.Add(new Error(this, "Person",

                     "A person cannot be 25 years of age!")); }

    }

}

3. The business object

Consider a business object called Person which we have decided inherits from the base class EntityObject. We have made the base class to implement the IEntityObject interface although it in not vital for this article. The attributes used here are [Required], [InRange] and [DefaultValue]

 

In the calling method, I would like to instantiate the Person object and ensure that on object.Save() method which is provided by the base class, I validate the object. There are other approaches you may use, e.g. immediate validation when a property have been changed. I have decided to defer validation to the end because the process involves reflection, which can be expensive.

          // Create new instance of the Person class

            Person person = new Person();

 

           //Assign values to properties

 

           person.Firstname = "John";

           person.Lastname = "Doe";

 

           person.Age = 15; //Should cause the object to be invalid and fail to save

 

           string errMsg = "Could not save Person!\n\n";

 

            if (!person.Save())

            {

                //Collect all error messages into one error string

                foreach(Error err in person.Errors)

                {

                    errMsg += err.Message + "\n";

                }

                MessageBox.Show(errMsg, "Error", MessageBoxButtons.OK,

                           MessageBoxIcon.Exclamation);

            }

            else

            {

                //We show the following message if the person object is valid

                MessageBox.Show("Person = Valid");

            }

4. The attributes

Let’s have a look at the attribute classes presented below. We will begin with the [Required] attribute and i should quickly ask you to note that the actual class name is RequiredAttribute and .NET lets you write it nicely as [Required] instead of [RequiredAttribute] although it will still work.

        [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]

        public class RequiredAttribute : System.Attribute

        {

            private bool required;

            private string errorMessage;

            public bool Required

            {

                get

                {

                    return required;

                }

                set

                {

                    required = value;

                }

            }

            public string ErrorMessage

            {

                get

                {

                    return errorMessage;

                }

                set

                {

                    errorMessage = value;

                }

            }

 

            public RequiredAttribute()

            {

                required = true;

            }

 

            public RequiredAttribute(bool required)

            {

                this.required = required;

            }

 

            public RequiredAttribute(string errorMessage)

            {

 

                this.errorMessage = errorMessage;

            }

 

        }

In the above Required attribute, the[AttributeUsage], is an attribute of an attribute - interesting isn't it? It enables you to determine or restrict where the attribute may be used, and in my example above we can only use the Required attribute on properties. The AttributeTarget is a enum in which you can choose the scope of where this attribute can be used. The default is All, however you can have Assembly, Class, Constructor, Delegate, Enum, Event, Field, Interface, method, Parameter, Property, ReturnValue and struct.

You can use AllowMultiple parameter of the AttributeUsage to determine whether the same attribute can be used more than once on the same target. In this case we can only have the [Required] attribute only once on a property.

Attributes can only have constants

5. The Business Object Base

I decided to place a public Errors collection which will keep errors encountered during validation. I also added event handlers that can be triggered when the validation method is executed. This enables hijacking the validation process if necessary and to inject validation rules after the object has been created. The Validate() method is virtual and therefore can be overridden to allow custom business rules. This is what you would use to ensure that a Male person would not have a boolean Pregnant property set to true./p>

    ///     

    /// EntityObject class.    

    /// This Entity base class     

    /// Author: Malisa Ncube    

    ///     public class EntityObject : IEntityObject    

    {                 

        #region Internal Fields        

    ///         

    /// The Errors collection to keep the errors. Tthe validation method populates this.        

        public List Errors = new List();

        #endregion

 

        #region Delegate and Events

        ///         

        // OnValidateEventHandler delegate to enable injection of custom validation routines        

        public delegate void OnValidateEventHandler(object sender, ValidateEventArgs e);      

        public delegate void OnValidatedEventHandler(object sender, ValidateEventArgs e);       

        public OnValidateEventHandler OnValidate;        

        public OnValidatedEventHandler OnValidated;        

 

        .....    

    } 


6. Reflection

We use reflection and the validate method loops through all the properties looking for associated custom attributes. We then test the property value against the attribute rule, and if it violates the rule we then add an error into the Errors collection.

The magic of knowing all the properties lies in the System.Reflection namespace. We first will then use PropertyInfo to keep all the properties of the object and GetType.GetProperties() method as follows.

PropertyInfo info = this.GetType().GetProperties();

We furthermore check the attributes on each property to see if it matches e.g. RequiredAttribute, if it does we then check the property value for violation of attribute rules. We also provide an appropriate error message if the message was not included in the attribute declaration.

The validate method of the base object EntityObject is as follows

 

        #region Internal Fields

        ///         

        /// The Errors collection to keep the errors. Tthe validation method populates this.        

        public List Errors = new List();

 

        #endregion

 

       .....

 

        ///         

        /// Validate method performs the validation process and allows overriding         

        public virtual void Validate(object sender, ValidateEventArgs e)        

        {            

        //Initialise the error collection            

            Errors.Clear();             

        //Enable calling the OnValidate event before validation takes place            

            if (this.OnValidate != null) this.OnValidate(this, new ValidateEventArgs());            

            try            

            {                 

                foreach (PropertyInfo info in this.GetType().GetProperties())                

                {                     /* Get property value assigned to property */                    

                    object data = info.GetValue(this, null);                     

                    /* Set Default value if value is empty */                    

                    foreach (object customAttribute in info.GetCustomAttributes(typeof(DefaultValueAttribute), true))                    

                    {                        

                        if (data == null)                        

                    {                            

                        info.SetValue(this, (customAttribute as DefaultValueAttribute).Default, null);                            

                            data = info.GetValue(this, null);                        

                        }                     }                      /* Check if property value is required */                    

                    foreach (object customAttribute in info.GetCustomAttributes(typeof(RequiredAttribute), true))                    

                    {                         if (string.IsNullOrEmpty((string)data))                         {                            

                        Errors.Add(new Error(this, info.Name,                               

                            string.IsNullOrEmpty((customAttribute as RequiredAttribute).ErrorMessage) ?                               

                            string.Format("{0} is required", info.Name) : (customAttribute as RequiredAttribute).ErrorMessage));                        

                    }                     }                      /* Evaluate whether the property value lies within range */                    

                    foreach (object customAttribute in info.GetCustomAttributes(typeof(InRangeAttribute), true))                    

                    {                         

                                if (!(((IComparable)data).CompareTo((customAttribute as InRangeAttribute).Min) > 0) ||   !(((IComparable)data).CompareTo((customAttribute as InRangeAttribute).Max) < 0))                        

                                {                            

                                Errors.Add(new Error(this, info.Name,                              string.IsNullOrEmpty((customAttribute as InRangeAttribute).ErrorMessage) ?                             

                                string.Format("{0} is out of range", info.Name) : (customAttribute as InRangeAttribute).ErrorMessage));                        

                            }                    

                            }                 }            

            }             catch (Exception ex)            

            {                 //                 throw new Exception("Could not validate Object!", ex);             }            

                        finally            

            {                

        //Enable calling the OnValidated event after validation has taken place                

                if (this.OnValidated != null) this.OnValidated(this, new ValidateEventArgs());            

            }        

        }

        } 

7. Overriding the Validate method

Below is some code which shows how you would override the Validate method of the business object and add custom validation rules.

   ....

 

 

        public override void Validate(object sender, ValidateEventArgs e)

        {

            base.Validate(sender, e);

 

            //Custom business rules

            if (this.Age == 25) { Errors.Add(new Error(this, "Person", "A person cannot be 25 years of age!")); }

        }

}

8. Other Considerations

a. You may decide to have the a Validate method in each attribute that takes the arguments from the reflected data and transform it into a appropriate manner. This would enable you not to worry about testing for validity of the property value on the business object but let the business object call the instance of the attribute and validate values in an identical manner.

b. When you need to databind the business object on to you winforms / WPF / WebForms you may have to take advantage of the nice interfaces that are provided in the .NET framework to enable ErrorProviders inform users on invalid entries.

IErrorInfo
INotifyPropertyChanged

c. You may also decide to cache the properties and their attributes on business object creation to enable better performance. This would be a best approach if you wish to do immediate validation, rather than wait until the user saves the object.

d. You may decide to have more complex attributes which call delegates on validation. This would be another way that would enable you to create more robust and flexible business rules.

e. Using AOP (Aspect oriented programming) frameworks like PostSharp you can add attributes to you object and perform validation, tracing and other interesting things.

Please check Validation Aspects and the Validation Framework

9. Conclusion

I hope this will help those of you who would like to understand what attribute based programming is. While writing this article i realised that there is type-safety is something as a programmer you need to look at closely. Ensure that for example you use [DefaultValue(30)] rather than a string [DefaultValue("30")].

10. History

* 12/22/2008: First posted.

 

Source code for this article is available on CodeProject http://www.codeproject.com/KB/cs/AttributeBaseValidation.aspx

 

Technorati Tags: ,,

Monday, October 27, 2008 #

I looked at the DataObjects.NET (http://www.x-tensive.com) website like i always do, and noticed that they have change their licensing model and their product is actually cheaper and very well broken down into internal, personal, small business and enterprise. After going through the presentations for v3.9, i should say i fell in love with the idea of not having to think about the database side of thing, but to rather concentrate of the business layer and the business rules and interface of the application.

Since the release of v4.0. I wanted comprehensive documentation, examples, benchmarks of DataObjects.NET or some ORM which would not require me to define any database specific details. So you will know that i was also impressed with XPO from Developer Express.

X-tensive.com has now released the new update which contains exactly what i wanted. I still want more examples though, but the example included of a WPF + DO4.0 application was quite interesting. It will be interesting to have as many examples as possible, for example, i would want to know if i can incorporate the WCF to make my application more scalable, how would i effectively do it. My quest is  to reduce development time and use a simple, lightning speed ORM which will shadow the develpment team from the databases, but retain the power of persisting (blindly), log, cache, search and utilise the new stuff in the .NET framework. I should say i was amazed with the benchmarks of DO4.0 performance. I think the future is bright for this ORM.

I realised that a wiki, is being updated regularly on the usage of DO4.0 and a blog (http://tips.x-tensive.com/) has been opened to give tips to developers on good practices in .NET development.

One of my main concerns is support and response to queries. I hope it will improve, maybe it has already since the last time i talked to them.

Tuesday, September 23, 2008 #

I'm having an interesting experience here in Cape Town and enjoying the VS2008, C# 2008, .NET3.5 ADO.NET 3.5 and WinForms. We are digging deep into the DataClasses and the Entity Framework.  I already have suggestions for Microsoft and will publish them as soon as i get some time.

I will also now begin to blog in the direction of C# more and more as that is my life now. I'm extremely excited about the way thing are going and can't wait to sit and combine all the concepts that we are doing and pushing them on the blog.

Wow! I think Cape Town is one of the most beautiful places in the world.