Archive

Archive for the ‘c#’ Category

EntityFramework force the update of an object

June 3rd, 2016 No comments

The Problem

You are trying to save some changes to an object.
Your code looks like the following:

But the changes are not being saved

The Solution

Entity framework is not aware that the job object has been changed so you need to tell it, by adding line 8.

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: c# Tags:

PayPalMerchantSDK, integrate paypal into your mvc 4 website

September 20th, 2013 2 comments

The following outlines some of the things that I have learned whilst trying to integrate a PayPal payment gateway into the MVC 4.0 website.

Installation

After much reading I determined that I easiest way was to use the PayPaylMerchantSDK.
This is implemented using the PayPal classic API which is the only API available in Australia @ the time of writing.
The easiest way to add this to your application is to use NuGet in Visual Studio (Tools ->> Library Package Manager ->> Manage NuGet Packages for Visual Studio) and search for PayPal.
Click on the PayPalMerchantSDK and choose Install. This will download the dependencies including log4net and the “PayPal Core SDK for .Net”

Code Samples

You should download and use the “Merchant” solution in the .net code samples that can be found here.
Classic API .Net code samples

App.Config

The code samples have an App.Config file that contains the base configuration for working with the API.
It contains the log4net and PayPal configuration section. You should copy these configuration sections into your web.config file.

Access the paypal settings section in the web.config

When coding I want to access some of the PayPal settings that I just copied into my web.config file, for example “paypalUrl” could be used when attempting to send a request for express checkout.

To access the paypal configuration settings that are in the web.config use the following code:

  string url = PayPal.Manager.ConfigManager.Instance.GetProperties()["paypalUrl"];

IPN Testing

IPN Simulator

I thought that it was dangerous opening port 80 through the firewall to get to my development server, so I was clever and used an unusual port number. This worked fine for most things but the IPN testing would not work with my special port number.

You have to use port 80 to allow the IPN test to work.

Core .SDK

The old way of implementing the IPN callback has been integrated into the PayPal .Core SDK.
IPNMessage.cs

This can now be implemented in 3 lines of code.

Put this inside your controller page

public ActionResult IPN()
{
	byte[] bytes = Request.BinaryRead(Request.ContentLength);

	PayPal.IPNMessage message = new PayPal.IPNMessage(bytes);
	if (message.Validate())
	{
		// message returned VERIFIED
	} 
	else 
	{
		// There was a problem
	}
	
	return null;
}

Note I then created a PayPalTransaction Class to simplify access to the PayPal.IPNMessage object.

It looked something like this.

public class PayPalTransaction
    {
        
        #region Constants

        private const string KEY_ADDRESSSTATE = "address_state";
        private const string KEY_TXNID = "txn_id";
        private const string KEY_LASTNAME = "last_name";
        private const string KEY_CURRENCY = "mc_currency";
        private const string KEY_PAYERSTATUS = "payer_status";
        private const string KEY_ADDRESSSTATUS = "address_status";
        private const string KEY_TAX = "tax";
        private const string KEY_INVOICE = "invoice";
        private const string KEY_PAYEREMAIL = "payer_email";
        private const string KEY_FIRSTNAME = "first_name";
        private const string KEY_BUSINESS = "business";
        private const string KEY_VERIFYSIGN = "verify_sign";
        private const string KEY_PAYERID = "payer_id";
        private const string KEY_PAYMENTDATE = "payment_date";
        private const string KEY_PAYMENTSTATUS = "payment_status";
        private const string KEY_RECEIVEREMAIL = "receiver_email";
        private const string KEY_PAYMENTTYPE = "payment_type";
        private const string KEY_ADDRESSNAME = "address_name";
        private const string KEY_ADDRESSSTREET = "address_street";
        private const string KEY_ADDRESSZIP = "address_zip";
        private const string KEY_ADDRESSCITY = "address_city";
        private const string KEY_ADDRESSCOUNTRY = "address_country";
        private const string KEY_ADDRESSCOUNTRYCODE = "address_country_code";
        private const string KEY_SHIPPING = "mc_shipping";
        private const string KEY_ITEMNUMBER1 = "item_number1";
        private const string KEY_SHIPPING1 = "mc_shipping1";
        private const string KEY_ITEMNAME1 = "item_name1";
        private const string KEY_HANDLING1 = "mc_handling1";
        private const string KEY_GROSS1 = "mc_gross1";
        private const string KEY_GROSS = "mc_gross";
        private const string KEY_FEE = "mc_fee";
        private const string KEY_RESIDENCECOUNTRY = "residence_country";
        private const string KEY_NOTIFYVERSION = "notify_version";
        private const string KEY_RECEIVERID = "receiver_id";
        private const string KEY_HANDLING = "mc_handling";
        private const string KEY_TXNTYPE = "txn_type";
        private const string KEY_CUSTOM = "custom";
        private const string KEY_TESTIPN = "test_ipn";

        private const string KEY_IPNTRACKID = "ipn_track_id";
        private const string KEY_PROTECTION_ELIGIBILITY = "protection_eligibility";

        private const string KEY_QUANTITY = "quantity";
        private const string KEY_RESEND = "resend";
        private const string KEY_NOTIFY_VERSION = "notify_version";
        
        
        #endregion 

        #region Constructor

        public PayPalTransaction()
        {
        }

        public PayPalTransaction(PayPal.IPNMessage message)
        {
            this.TransactionId = message.IpnValue(KEY_TXNID);
            this.TransactionType = message.IpnValue(KEY_TXNTYPE);
            this.PayerId = message.IpnValue(KEY_PAYERID);
            this.Currency = message.IpnValue(KEY_CURRENCY);
            this.Custom = message.IpnValue(KEY_CUSTOM);
            this.HandlingAmount = GetAsDecimal(message, KEY_HANDLING);
            this.FirstName = message.IpnValue(KEY_FIRSTNAME);
            this.IpnTrackId = message.IpnValue(KEY_IPNTRACKID);
            this.LastName = message.IpnValue(KEY_LASTNAME);
            this.PayerStatus = message.IpnValue(KEY_PAYERSTATUS);
            this.PaymentDate = GetAsDate(message, KEY_PAYMENTDATE);
            this.PaymentStatus = message.IpnValue(KEY_PAYMENTSTATUS);
            this.ProtectionEligibility = message.IpnValue(KEY_PROTECTION_ELIGIBILITY);
            this.ShippingAmount = GetAsDecimal(message, KEY_SHIPPING);
            this.GrossAmount = GetAsDecimal(message, KEY_GROSS);
            this.FeeAmount = GetAsDecimal(message, KEY_FEE);

            this.Quantity = GetAsInt(message, KEY_QUANTITY);
            this.Resend = GetAsBool(message, KEY_RESEND);
            this.PaymentType = message.IpnValue(KEY_PAYMENTTYPE);
            this.NotifyVersion = message.IpnValue(KEY_NOTIFY_VERSION);
            this.PayerEmail = message.IpnValue(KEY_PAYEREMAIL);
            this.TaxAmount = GetAsDecimal(message, KEY_TAX);
            this.ResidenceCountry = message.IpnValue(KEY_RESIDENCECOUNTRY);

            this.ItemNumber1 = message.IpnValue(KEY_ITEMNUMBER1);
            this.ItemName1 = message.IpnValue(KEY_ITEMNAME1);
            this.ShippingAmount1 = GetAsDecimal(message, KEY_SHIPPING1);
            this.HandlingAmount1 = GetAsDecimal(message, KEY_HANDLING1);
            this.GrossAmount1 = GetAsDecimal(message, KEY_GROSS1);

            this.AddressCity = message.IpnValue(KEY_ADDRESSCITY);
            this.AddressCountryCode = message.IpnValue(KEY_ADDRESSCOUNTRYCODE);
            this.AddressCountry = message.IpnValue(KEY_ADDRESSCOUNTRY);
            this.AddressName = message.IpnValue(KEY_ADDRESSNAME);
            this.AddressStreet = message.IpnValue(KEY_ADDRESSSTREET);
            this.AddressZip = message.IpnValue(KEY_ADDRESSZIP);

        }

        #endregion

        #region Private Helpers

        private int GetAsInt(PayPal.IPNMessage m, string key)
        {
            int value = Convert.ToInt32(m.IpnValue(key));
            return value;
        }

        private bool GetAsBool(PayPal.IPNMessage m, string key)
        {
            bool bln = false;
            string value = m.IpnValue(key);
            if (value != null)
            {
                bln = (value.ToLower().Trim() == "true");
            }
            return bln;
        }

        private decimal GetAsDecimal(PayPal.IPNMessage m, string key)
        {
            decimal value = Convert.ToDecimal(m.IpnValue(key));
            return value;
        }

        private DateTime GetAsDate(PayPal.IPNMessage m, string key)
        {
            string value = m.IpnValue(key);
            value = HttpUtility.HtmlDecode(value);

            int iPos = value.LastIndexOf(" ");
            string timezone = value.Substring(iPos+1);
            TimeZoneInfo tzSource = null;
            
            value = value.Substring(0, iPos);

            switch (timezone)
            {
                case "PST":
                case "PDT":
                    tzSource = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
                    break;
            }
            
            DateTime date = Convert.ToDateTime(value);
            date = TimeZoneInfo.ConvertTimeToUtc(date, tzSource);
            date = TimeZoneInfo.ConvertTimeFromUtc(date, TimeZoneInfo.Local);

            return date;
        }

        #endregion

        #region Properties

        public int Id { get; set; }

        /// <summary>
        /// The transaction Id of the PayPal Transaction
        /// </summary>
        public string TransactionId { get; private set; }

        public string TransactionType { get; private set; }

        public string PayerId { get; private set; }

        public string IpnTrackId { get; private set; }

        public string Currency { get; private set; }

        public string Custom { get; private set; }

        public string FirstName { get; private set; }

        public decimal HandlingAmount { get; private set; }

        public string LastName { get; private set; }

        public string ProtectionEligibility { get; private set; }

        public string PayerStatus { get; private set; }

        public DateTime PaymentDate { get; private set; }

        public string PaymentStatus { get; private set; }

        public int Quantity { get; private set; }

        public bool Resend { get; private set; }

        public string PaymentType { get; private set; }

        public string NotifyVersion { get; private set; }

        public string PayerEmail { get; private set; }

        public string ResidenceCountry { get; private set; }

        public string AddressName { get; private set; }
        
        public string AddressStreet { get; private set; }
        
        public string AddressZip { get; private set; }
        
        public string AddressCity { get; private set; }
        
        public string AddressCountry { get; private set; }

        public string AddressCountryCode { get; private set; }

        public decimal ShippingAmount { get; private set; }

        public decimal GrossAmount { get; private set; }

        public decimal FeeAmount { get; private set; }

        public decimal TaxAmount { get; private set; }

        public decimal ShippingAmount1 { get; private set; }

        public decimal GrossAmount1 { get; private set; }

        public decimal HandlingAmount1 { get; private set; }

        public string ItemNumber1 { get; private set; }

        public string ItemName1 { get; private set; }
    
        #endregion

        #region Virtual Properites

        public virtual bool IsComplete
        {
            get
            {
                bool value = (this.PaymentStatus.ToLower().Trim() == "completed");
                return value;
            }
        }
        #endregion

    }

Links

Categories: c#, Code Tags:

Type ‘YourWPfApp.App’ already defines a member called ‘Main’ with the same parameter types

January 23rd, 2013 No comments

Create your own Custom Splash Page

In WPF you can really easily create a basic splash page which is just an image, but it is a bit simple for most people.

In my application I want to display a marquee style progress bar, application version numbers that are generated off the Assembly version number, and perform any application start up, like validate a license.

So create yourself a WPF window SplashPage.xaml and implement your layout and any custom code that should run inside the splash page.
Implement a timer so the window will remain open for a period of time. When the timer ticks, if all the work is done, close and dispose of the timer and then close the splash window.

Place this code in your App.xaml.

public partial class App : Application
{
	/// <summary>
	/// Application Entry Point.
	/// </summary>
	[System.STAThreadAttribute()]
	[System.Diagnostics.DebuggerNonUserCodeAttribute()]
	[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
	public static void Main()
	{
		ShowSplashScreen();
		YourWpfApp.App app = new YourWpfApp.App();
		app.InitializeComponent();
		app.Run();
	}

	protected static void ShowSplashScreen()
	{
		SplashPage splash = new SplashPage();
		splash.ShowDialog();
		splash.Close();
	}
}

The Problem

When you build the program you will get an error like:

When you click on the error it will take you to a page App.g.cs, which contains a duplicate static Main() application entry point, that is generated by VisualStudio.

The Solution

  1. Go to the properties of the App.xaml page.
  2. Check the Build Action. It will be “Application Definition”
  3. Change the build action to “Page”
  4. Build the application

The application will now build. The App.g.cs file will be updated and when you look it will no longer contain the Main method.

Cheers

VN:F [1.9.22_1171]
Rating: 10.0/10 (9 votes cast)

Categories: c#, WPF Tags:

How to make a Modal Dialog in WPF

October 23rd, 2012 No comments

The Scenario

I have created a c# WPF application. The app has an “About” page that I want to show as a modal dialog.

The Problem

The issue is when the “About” dialog is open and the user Alt+Tab’s the “About” dialog is visible as a separate tile in the Alt+Tab list. The about dialog also has a url link. Clicking this opens a browser window. When the user Alt+Tab’s back to the application the about dialog is visible whilst the calling app is now behind the web browser.

Solution

Firstly, in WPF create your dialog as a “Window” control.

In windows forms you would do:

  frmAbout about = new frmAbout();
  about.ShowDialog(this);

In WPF its the same concept but syntactically slightly different:

  About aboutWindow = new About();
  aboutWindow.Owner = this;
  aboutWindow.ShowDialog();

This will open the window as a modal dialog.

One other setting to play with on the About window is WindowsStartUpLocation
Options:

  • CenterScreen
  • CenterOwner
  • Manual

Setting it as “CenterOwner” will open in the centre of the calling Window. I set mine to centre screen as it looked a bit funny starting centre of the owner.

VN:F [1.9.22_1171]
Rating: 4.2/10 (6 votes cast)

Categories: c#, WPF Tags:

What is the best unique computer identifier?

June 28th, 2012 2 comments

I have been trying to find out what is the best way to uniquely identify a computer for licensing purposes.

Here are some suggestions.

Computer SID

In a previous post “Read computer sid on 64-bit machine” I show how to get the Product ID of the operating system as the unique identifier.

Cons
This seemed great and I even used it for a licensing tool until I installed the software on multiple virtual machines!
The VM’s were cloned from the same base image and guess what? They all had the same Product ID, which meant that every computer passed in the same ID.

Network Card Mac address and other hardware items

These do provide a level of uniqueness but there are a few problems:

Cons

  1. Most computers these days have many network cards, USB wi-fi etc, so which card do you choose to get the MAC address from?
  2. Unless it’s in a laptop or onboard, NIC’s can easily be replaced.

What is the best unique computer identifier

Thinking logically you want to use an identifier that is likely to change the least so CPU’s, NIC’s, Hard Drives etc etc should all be rejected.

The one component that generally doesn’t change very often is the motherboard. The motherboard has a UUID (Univerally Unique Identifier), that can be read from the bios.

This value is unique in both physical computers and Virtual Machines.

The following code sample is a c# example of how to read the UUID from the motherboard BIOS using the System.Management.ManagementClass to read the Win32_ComputerSystemProduct

using System.Management;
...
...
public static string UUID
{
    get
    {
        string uuid = string.Empty;

        ManagementClass mc = new ManagementClass("Win32_ComputerSystemProduct");
        ManagementObjectCollection moc = mc.GetInstances();

        foreach (ManagementObject mo in moc)
        {
            uuid = mo.Properties["UUID"].Value.ToString();
            break;
        }

        return uuid;
    }
}

Note for a full reference of all the Win32 Management classes go here:
MSDN Win32 Classes

VN:F [1.9.22_1171]
Rating: 9.8/10 (10 votes cast)

Categories: c#, Code Tags:

How to read computer SID on 64-bit machine

June 26th, 2012 1 comment

To get a unique identifier for a computer the recommended key is “ProductId” that is accessible from the following registry key – HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion.

This is the value you see as “Product ID” on the properties screen of your computer.

To access this key can be a little problematic if you are attempting to read the “ProductId” sub-key on a 64-bit machine from a 32-bit application.

This is due to Registry Reflection or WOW64 as it is sometimes called.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384235%28v=vs.85%29.aspx

In short the 64-bit OS shows 2 logical views of certain portions of the registry on WOW64, one for 32-bit and one for 64-bit. In the case of the “ProductId” key it explicitly exists in the 64-bit view so the following code will fail to find the key, as when running inside the 32-bit application it accesses the 32-bit view of the registry and fails to find the key.

The following code will return an empty string

public static string ComputerSID
{
    get
    {
        string sid = string.Empty;

        string SIDKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
        RegistryKey key = Registry.LocalMachine.OpenSubKey(SIDKey);

        object keyValue = key.GetValue("ProductId");

        if (keyValue != null)
        {
            sid = keyValue.ToString();
        }

        key = null;
        keyValue = null;

        return sid;
    }
}

To make this work you have to explicitly request c# to open the 64-bit view of the registry.
The following code sample will work.

public static string ComputerSID
{
    get
    {
        string sid = string.Empty;
        string SIDKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";

        RegistryKey baseKey = null;
        if (Environment.Is64BitOperatingSystem)
        {
            baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
        }
        else
        {
            baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default);
        }

        RegistryKey key = baseKey.OpenSubKey(SIDKey);

        object keyValue = key.GetValue("ProductId");

        if (keyValue != null)
        {
            sid = keyValue.ToString();
        }

        key = null;
        keyValue = null;

        return sid;
    }
}

Cheers
John

VN:F [1.9.22_1171]
Rating: 10.0/10 (1 vote cast)

Categories: c#, Code Tags:

Linq for Entity Join Query – Entity Framework

May 22nd, 2012 No comments

The Problem

You have a parent/child table structure in your database that looks like the table structure outlined below.

You have used the EntityFramework or Linq to SQL to build your data access layer.

Now you want to write a method using linq as follows:

public List<Child> GetChildren(string parentName)

The database

Create Table Parent (
  Id int identity(1,1) NOT NULL,
  ParentName varchar(50) NOT NULL,
  CONSTRAINT PK_Parent Primary Key Clustered (
    Id Asc
  )
);

Create Table Child (
   Id int identity(1,1) NOT NULL,
   ParentId int NOT NULL,
   ChildName varchar(50) NOT NULL,
   CONSTRAINT PK_Child Primary Key Clustered (
    Id Asc
  )
)

Obviously I would have foreign keys between parent and child.. you get the idea, this is your basic run of the mill one to many, parent child relationship.

In SQL

To get a list of all the children in sql your query would look something like:

select c.*
from Parent p 
    inner join Child c on c.ParentId = p.Id
Where p.ParentName = @searchString -- where @searchString is a variable 

The Method

I looked around the web to find a simple example of how to do this and I could not find one, that explained how to do an easy join syntax between two tables.

After much trial and error I came up with this:


public List<Child> GetChildren(string parentName)
{
    List<Child> children = null;

    using (FrameworkDb dbContext = new FrameworkDb())
    {
        children = (from c in dbContext.Children
                    join p in dbContext.Parent.Where(p => p.ParentName == parentName)
                    on new { PID = c.ParentId } equals new { PID = p.Id }
                    select c).ToList();
    }

    return children;
}

VN:F [1.9.22_1171]
Rating: 8.2/10 (6 votes cast)

Categories: c#, Code Tags:

Windows Installer – Uninstall custom actions – WARNING!!!

April 18th, 2012 No comments

With uninstall custom actions you have to be really careful.

#1 Rule

When working with uninstall custom actions, always encapsulate the custom action in a try / catch block!

Why?

If you are testing your uninstall process and it throws an exception you will be left with a half uninstalled application.
As the application has not un-installed you cannot install a new version and as the uninstaller is compiled you cannot rebuild the installer to fix the error that is causing the exception.

How do I know this?

I did this exact thing. I was creating an installer that had custom actions for both install and uninstall.
I caused an exception in the uninstall process and I was left with a product that could not be uninstalled!

Can you recover from this?

I found that in this scneario the Microsoft Fix-it tool is your saviour.

Download the fix it program from here, Microsoft Fix-it Uninstaller

Run the program and follow the prompts.

I did a custom uninstall, chose my product that had failed to uninstall and it cleans up all the relevant registry keys so that it has been completely removed from the computer.

Cheers

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)

Categories: c# Tags:

Background Worker Thread Code Sample with event handlers and cross thread invoke

March 30th, 2012 2 comments

The Problem

So you want to create a windows form application. It needs to do the following:

  1. Do some work where its processing multiple items
  2. Have a progress bar that updates the progress of the application as it runs.

Seems easy.. well here are the complexities:

  1. You want the main process to run on a background thread, so that the UI thread is available to update and render
  2. The background thread will need to raise events back to the main application to notify the application of the background thread progress.
  3. The progress bar will need to be updated to reflect the progress.
  4. The call to update the UI will crash because it is actually doing a cross-thread call, (the background thread, is trying to update the UI thread and this will crash.

This will walk you through the pattern for implementing this solution.

Background Worker Thread

This as the name suggests is a thread of execution that runs in the background in the application leaving the UI thread to do what it is good at, UI rendering.

If you were to try and have a method that processed 1000 records @ 2 seconds per record, and tried to update the progress bar on processing each record the UI would not render the progress bar as the UI thread is caught up in the process of doing the work.
Doing the work on the background thread leave the UI thread to update the UI whilst the background thread gets on with doing the work.

What is the background thread and how do I create one?

First of all lets create a visual studio solution. I am going to create an empty windows form application called WorkerUtility. I will place the following controls on it.

  1. A button btnGo – labeled Go
  2. A progress bar; and
  3. A label called lblProgress – to show the % complete

The background worker thread can be found in the toolbox under the “Components” section.
Drag the background worker thread onto you form and you will end up with a control called backGroundWorker1.

It should look like this:

Set the following properties on the background worker control

  • WorkerReportsProgress true

Double click on the “Go” Button and setup the following Code

private void btnGo_Click(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}

Performing the Work and Raising Events

I am going to simulate processing a 126 items @ Random number of seconds between 0 and 2 seconds, by looping and pausing the background thread. You could be doing anything in the processing block, for example, read data from the database loop through the rows and perform file system tasks based upon the data?

I will be performing work but each time an item is processed I want to tell the UI thread so that it can update the progress bar. To do this I will need to use events and I am going to create some custom event args.

Create a class called ProgressUpdatedEventArgs like the following:

using System;

namespace WorkerUtility
{
    public class ProgressUpdatedEventArgs : EventArgs
    {
        public ProgressUpdatedEventArgs(int total, int progress)
        {
            this.Total = total;
            this.Processed = progress;
        }

        public int Processed { get; private set; }

        public int Total { get; private set; }
    }
}

I will encapsulate the logic in a class called Processor.cs. Create a class as follows:
This will house the event handler and raise events back to the UI thread

The main point of Interest in the following code is the creation of the event handler on the page with a delegate method declaration and an event handler. The RaiseEvent method is called on each file that is processed.

using System;

namespace WorkerUtility
{
    public static class Processor
    {
        public delegate void ProgressUpdatedEvent(ProgressUpdatedEventArgs progressUpdated);

        public static event ProgressUpdatedEvent ProgressUpdated;

        /// <summary>
        /// Main execution method that does the work
        /// </summary>
        public static void Execute()
        {
            int total = 126 // this creates funny percentages
            Random randomGenerator = new Random();
            for (int i = 0; i < total; i++)
            {
                // Do some processing here

                double delay = (double)randomGenerator.Next(2) + randomGenerator.NextDouble();

                int sleep = (int)delay * 1000;

                System.Threading.Thread.Sleep(sleep);

                RaiseEvent(total, i + 1);
            }
        }

        private static void RaiseEvent(int total, int current)
        {
            if (ProgressUpdated != null)
            {
                ProgressUpdatedEventArgs args = new ProgressUpdatedEventArgs(total, current);
                ProgressUpdated(args);
            }
        }
    }
}

Cross-Thread method invoke, handling the callback

Now we need to set up an event handler in the main form so we can update the UI when the event is raised.

On the Background Worker Switch to the “Events” properties and double-click on “DoWork” to create the “DoWork” event handler.

Go to the _DoWork event handler and change as follows:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Processor.ProgressUpdated += new Processor.ProgressUpdatedEvent(Processor_ProgressUpdated);
    Processor.Execute();
}

void Processor_ProgressUpdated(ProgressUpdatedEventArgs progressUpdated)
{

}

So this is where the tricky bit comes in. We need to implement the _ProgressUpdated callback.
If we were to go directly to update the UI, we would get a cross thread exception and the program would crash.

Cross-thread operation not valid

Cross-thread operation not valid: Control 'progressBar1' accessed from a 
thread other than the thread it was created on.

If you check the InvokeRequired method it will return true in this scenario so we need to perform a cross thread call to get from the background worker thread, that the “ProgressUpdated” event is raised on, to the UI thread to make the changes in the UI.

This is done as follows, firstly you need a delegate for the method invoke method callback and an Update Method to handle performing the UI Update.

public delegate void ProgressUpdatedCallaback(ProgressUpdatedEventArgs progress);

private void Processor_ProgressUpdated(ProgressUpdatedEventArgs progressUpdated)
{
	if (InvokeRequired)
	{
		Invoke(new ProgressUpdatedCallaback(this.UpdateProgress), new object[] { progressUpdated });
	}
	else
	{
		UpdateProgress(progressUpdated);
	}
}

private void UpdateProgress(ProgressUpdatedEventArgs args)
{
	if (progressBar1.Maximum != args.Total)
	{
		// initial setup
		progressBar1.Minimum = 0;
		progressBar1.Maximum = args.Total;
		progressBar1.Style = ProgressBarStyle.Continuous;
	}

	progressBar1.Value = args.Processed;

	if (args.Total > 0)
	{
		double progress = args.Processed / (args.Total * 1.0);
		lblProgress.Text = progress.ToString("P2");
	}

	Application.DoEvents();

}

Here is what it looks like running

Here is the full working code sample in a VS2010 solution.
WorkerUtility

VN:F [1.9.22_1171]
Rating: 10.0/10 (1 vote cast)

Categories: c#, Windows Forms Tags:

Distributed Transaction Failure – Race Condition

November 18th, 2011 No comments

I have a process that is using MSDTC (Microsoft Distributed Transaction Co-ordinator) to manage the transaction.

The transaction is controlled inside a c# .Net windows form executable that manages the transaction by the TransactionScope() object.

The transaction is maintained across 2 SQL server and an Oracle server and it all works… most of the time.

All transactions are opened inside a using statement, as follows

using (TransactionScope scope = new TransactionScope())

Or similar.

The overview of the .exe is it monitors a queue of jobs to process. It processes the work inside the transaction successfully commits, and then after that updates the status on the job queue to success or failure.

When there is an application error, the program throws an exception which means it ends the main transaction and in an outer transaction updates the status to failure.

Intermittently when the core logic was throwing errors on many jobs the entire process would fail with the following error:

But it was intermittent. I had the same 12 jobs throwing errors on every run and sometimes it would crash with the above error after 2 jobs, 8 jobs, 4 jobs or it would actually not crash at all. On my development box I could not replicate it at all?

So I figured I had a race condition.

The fix


After the main transaction block of code and before opening the next TransactionScope object to update the status, I put a 100ms sleep.

    // Stop race condition
    System.Threading.Thread.Sleep(100);

    item.Success = false; // <-- this is my object for updating the success or failure
    using (TransactionScope scope = new TransactionScope())
    {
        context.SubmitChanges();
        scope.Complete();
    }

The problem has disappeared. I think the re-opening of a new TransactionScope() object did not allow enough time for MSDTC to close the previous transaction and it was crashing.

Cheers
John

VN:F [1.9.22_1171]
Rating: 1.0/10 (1 vote cast)

Categories: c# Tags: