Pages

Search

Wednesday, April 4, 2012

Accessing UI Thread Controls from Task using UI Thread Scheduler


While dealing with “Tasks” creating multiple which run concurrently, it might be required to access or update the UI the progress state. If the Task which is running a different thread other than UI thread, tries to access the controls (textbox or label, etc…) of UI thread, UI thread throws cross thread error.
To avoid this, we can either use the UI thread Synchronization context as posted in Thread Synchronization Context  or the UI Thread Scheduler can be used to access the control or functions in the UI Thread.
Below example demonstrates the same.
Form.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Collections.Specialized;
using System.Reflection.Emit;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// function which takes time to generate.
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public static double SumRootN(int root)
        {
            double result = 0;
            for (int i = 1; i < 100000; i++)
            {
                result += Math.Exp(Math.Log(i) / root);
            }
            return result;
        }
        private void btnLogSum_Click(object sender, EventArgs e)
        {
            try
            {
                int totTasks = 10;
                SynchronizationContext ct = SynchronizationContext.Current;
                lblResult.Text += Environment.NewLine + "--------------";
                //creating 10 tasks, calculating the "SumRootN" for each value from 1 to 10
                //and displaying the same in the UI.
                for (int i = 1; i <= totTasks; i++)
                {
                    int j = i;

                    TaskScheduler objScheduler = TaskScheduler.FromCurrentSynchronizationContext();
                    Task<double> objTask = Task.Factory.StartNew(
                           () =>
                           {
                               return SumRootN(j);
                           }
                       );
                    //using the synchronisation context of the UI Thread.
                    //objTask.ContinueWith(
                    //    (tsk) =>
                    //    {
                    //        ct.Post((o) => { lblResult.Text += Environment.NewLine + "Result is : " + o; }, tsk.Result);
                    //    });

                    //or
                    //using the scheduler of the UI Thread.
                    objTask.ContinueWith(
                        (tsk) =>
                        {
                            lblResult.Text += Environment.NewLine + "Result is : " + tsk.Result;
                        }, objScheduler);
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("Error : " + exp.Message);
            }
        }
    }
}



Output





WCF Data Service


WCF Data Service, enables the capability of exposing the entity objects as service objects and also the service methods can be invoked directly from the request url. The method of invoking the service method(s) from url is same as like in MVC invoking through url or same as like REST API.
Beside the possible way of invoking from browser, the same url can be treated as WCF Service and can be used to generate a proxy class in the client application adding through the service reference.
Below example demonstrates both of these.

WCF Data Service .cs
using System;
using System.Data.Services;
using System.Data.Services.Common;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Web;
using CarModel;

public class WcfDataService : DataService<CarEntities>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {
        //setting access to read all the "AllCars" entity objects
        config.SetEntitySetAccessRule("AllCars", EntitySetRights.AllRead);

        //setting the access to invoke operations
        config.SetServiceOperationAccessRule("GetGermanCars",
                                      ServiceOperationRights.AllRead);
        config.SetServiceOperationAccessRule("GetJapaneseCars",
                                      ServiceOperationRights.All);
        config.SetServiceOperationAccessRule("AddJapaneseCar",
                                      ServiceOperationRights.All);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
    }

    //can invoke the "GetGermanCars" method requesting from browser as
    //http://localhost:[port]/[webapp]/WcfDataService.svc/GetGermanCars?strModel='[value]'
    //or
    //http://localhost:[port]/[webapp]/WcfDataService.svc/GetGermanCars
    [WebGet]
    public string GetGermanCars(string strModel)
    {
        //getting the list of "AllCars" of type "German" and serializing in JSON format and returning the same
        IEnumerable<AllCar> GCars = null;
        if (string.IsNullOrEmpty(strModel))
            GCars = from a in this.CurrentDataSource.AllCars.OfType<GermanCar>() select a;
        else
            GCars = from a in this.CurrentDataSource.AllCars.OfType<GermanCar>() where a.Model == strModel select a;
        System.Web.Script.Serialization.JavaScriptSerializer objJavaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        return objJavaScriptSerializer.Serialize(GCars);
    }

    [WebGet]
    public IQueryable<string> GetJapaneseCars(string strModel)
    {
        //getting the list of "AllCars" of type "Japan" returning the japan car models
        if (string.IsNullOrEmpty(strModel))
            return from a in this.CurrentDataSource.AllCars.OfType<JapaneseCar>() select a.Model;
        else
            return from a in this.CurrentDataSource.AllCars.OfType<JapaneseCar>() where a.Model == strModel select a.Model;
    }

    [WebGet]
    public IQueryable<string> AddJapaneseCar(string strModel, string strBrand)
    {
        //Adding "Japan" car to database with given model and brand
        JapaneseCar obj = new JapaneseCar() { Model = strModel, Brand = strBrand, JapaneseCarAdditionalNotes = "This is japenese car added via WCF Dataservice" };
        this.CurrentDataSource.AllCars.AddObject(obj);
        this.CurrentDataSource.SaveChanges();
        return from a in this.CurrentDataSource.AllCars.OfType<JapaneseCar>() where a.Model == strModel select a.Model;
    }
}

In Client Application
Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //require to add the "WCF Data Service" url as Service reference.
                //accessing the WCF data service
                EntityProxy.CarEntities objEntities = new EntityProxy.CarEntities(new Uri("http://localhost:44731/Misc/WcfDataService.svc/"));
                //getting the list of Entity objects (AllCars) and wtriting the model of the car to console
                IQueryable<EntityProxy.AllCar> cars = objEntities.AllCars;
                foreach (var item in cars)
                {
                    Console.WriteLine(item.Model);
                }
            }
            catch (Exception exp)
            {
                Console.WriteLine("Error : " + exp.Message);
            }

            Console.Read();
        }
    }
}

From Client (Console)


From Browser







 http://localhost:44731/Misc/WcfDataService.svc/GetJapaneseCars



Tuesday, April 3, 2012

Generic Handler


Generic Handler is straight forward in executing functionality which can also be achieved using server page (aspx). The advantage with generic handler, is that it does with out executing the page life cycle events. The response from generic handler is same as like a server page which means it can be rendered in a browser
Below example is to render an image using generic handler.
Generic Handler:
<%@ WebHandler Language="C#" Class="MyHandler" %>

using System;
using System.Web;

public class MyHandler : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "image/png";
        context.Response.WriteFile(context.Server.MapPath("Images/Google.png"));
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

}
ASPX Page :
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeFile="Default.aspx.cs" Inherits="_Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <img src="MyHandler.ashx" />
</asp:Content>


The generic handler page can also be requested directly from the browser beside from a server page.

Monday, April 2, 2012

Log4Net Logging


Log4Net is a third party assembly, using which the logging functionality in an application could be error logging or info logging, etc… can be handled.
Logging the message in a log file or Event logs or Database table or Can be displayed in a console.
Below sample explains the same

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using log4net.Config;
using log4net.Appender;
using log4net.Layout;
using log4net.Repository;
using log4net.Core;
namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //creating a log4net repository
                log4net.Repository.ILoggerRepository objRepository = log4net.LogManager.CreateRepository("MyRep");

                //applying setting to append logs in a file.
                //file name will be found in the application folder path with name same as repository name
                ConfigureFileAppender(objRepository);

                //applying setting to add logs to event viewer
                ConfigureEventLogAppender(objRepository);

                //applying setting to display the message in the console
                ConfigureConsoleAppender(objRepository);
                log4net.ILog objLog = log4net.LogManager.GetLogger("MyRep", typeof(MyInterface));

                //writing the debug message
                objLog.Debug("Test Message");
            }
            catch (Exception exp)
            {
                Console.WriteLine("Error : " + exp.Message);
            }
            Console.Read();
        }

        private static void ConfigureConsoleAppender(ILoggerRepository repository)
        {
            MyConsoleAppender objMyConsoleAppender = new MyConsoleAppender();
            objMyConsoleAppender.ActivateOptions();
            BasicConfigurator.Configure(repository, objMyConsoleAppender);

        }
        private static void ConfigureEventLogAppender(ILoggerRepository repository)
        {
            MyEventLogAppender objMyEventLogAppender = new MyEventLogAppender("MyApp", "MyLog");
            objMyEventLogAppender.Threshold = Level.All;
            BasicConfigurator.Configure(repository, objMyEventLogAppender);
        }

        private static void ConfigureFileAppender(ILoggerRepository repository)
        {
            MyRollingFileAppender objMyRollingFileAppender = new MyRollingFileAppender(repository.Name);
            objMyRollingFileAppender.Threshold = Level.All;
            objMyRollingFileAppender.ActivateOptions();
            BasicConfigurator.Configure(repository, objMyRollingFileAppender);
        }
    }

    sealed class MyRollingFileAppender : RollingFileAppender
    {
        public MyRollingFileAppender(string appName)
        {
            //setting the properties relevant to the log files
            this.Name = "MyLogFile";
            this.File = appName + ".log";
            this.AppendToFile = true;
            this.MaximumFileSize = "10000KB";
            this.MaxSizeRollBackups = 50;
            this.Layout = new PatternLayout(@"%date [%thread] %-5level %logger [%ndc] - %message%newline");
        }
    }
    sealed class MyEventLogAppender : EventLogAppender
    {
        public MyEventLogAppender(string appName, string logName)
        {
            //setting the properties relevant to event logs
            this.Name = "AfpEventLogAppender";
            this.ApplicationName = appName;
            this.MachineName = ".";
            this.LogName = logName;
            this.Layout = new PatternLayout(
                @"%date [%thread] %-5level %logger %method() [%ndc] - %message%newline");
        }
    }
    sealed class MyConsoleAppender : ConsoleAppender
    {
        public MyConsoleAppender()
        {
            //setting the properties relevant to the console
            Name = @"ConsoleAppender";
            Layout = new PatternLayout(
                @"%date [%thread] %-5level %logger %method() [%ndc] - %message%newline");

        }
    }

    public interface MyInterface
    {
        void Test();
    }
    public class Cls : MyInterface
    {
        public void Test()
        {
            throw new NotImplementedException();
        }
    }
}