Monday, April 09, 2012

Entity Framework Code First Error-- The path is not valid. Check the directory for the database.


You may get the the path is not valid error if you are using SQL Server CE 4.0 and do not have the ASP.Net data folder (APP_Data).

Connection String

Make sure you create the APP_Data folder





Digg It! Add to del.icio.us Stumble This

Entity Framework Code First Error -- The provider did not return a ProviderManifestToken string

There are a couple of reasons for getting this error when using Code First approach in Entity Framework 

Reason 1: The name of the connection string does not match the name of the Entity Framework Context.
Example: The name of my EF context JobBoardContext does not match the name of the connection string which is JobBoard.





Reason 2: Entity Framework was unable to make a connection to the database. Take a look at the inner exception to get more details about the exception.

Check the inner details of the exception to know more details about the connection error















Digg It! Add to del.icio.us Stumble This

Saturday, March 24, 2012

Adding a Linked Server in SQL Server 2008

You can do the following in order to add a linked server to another SQL Server Instance

Step 1: Run the Add Linked Server Stored Procedure
EXEC sp_addlinkedserver
@server=N'Server1_Instance1', --Give a name to the linked server
@srvproduct=N'',
@provider=N'SQLNCLI', --Provider name for SQL Server
@datasrc=N'Server1\Instance1';

Step 2: Add credentials to access the linked server
If the linked server can be accessed via Windows Authentication this step is not required. But if you need to access is via SQL Server authentication you need to run the following procedure by replacing the text in bold appropriately.

EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname= N'Server1_Instance1',@useself=N'False',@locallogin=NULL,@rmtuser=N'user_id',@rmtpassword='password'
GO

Step 3: Access the linked server
You can access the linked server in the following format
linked_server.database_name.schema_name.table_name
Example: Select * from Server1_Instance1.Database1.dbo.Table1

Digg It! Add to del.icio.us Stumble This

Sunday, June 07, 2009

Creating a Windows Task Scheduler Service

Recently, one of my friends asked me for help in creating a windows service for scheduling some tasks. He said that he could not use the task scheduler that comes with Windows as the Task Scheduler expects each task to be a stand alone executable file. All his tasks were in a single class library and it would be a lot of work to separate each task into its own executable. Also he said that that the schedule for the tasks could change from time to time and it would be nice if he could configure the tasks in an XML file.

To Recap, the requirements for the windows service are

1) The tasks should be loaded from a class library

2) The schedule information for the tasks should be configurable in an XML file.

We googled (binged :)) for the solution and found an excellent article by Ajit Kumar Application Scheduler Service Using C#.Net And XML to base our solution upon. We took some good points like configuring the task information from the above mentioned article.

This is how we took a stab at our solution. We used a bit of reflection to get instances of tasks that need to run.

  • Configure the scheduled tasks in an XML file (Tasks.xml)
  • On Service Start, load the the tasks configuration from the Tasks.xml file into a DataSet.
  • Get a reference to the assembly that contains the tasks.
  • Use a Systems.Timer to run a method (RunTaks) periodically that checks the tasks that need to run and run the tasks.


This is how the Tasks.xml file looks like

name is the class name of the task to run, time is date and time (MM/dd/yyyy HH:mm format) when the task should run, and repeat is how often the task should run (H- hourly, W-Weekly, M-Monthly, D-Daily)

<appSchedule>
<task name="Task1" time="06/07/2009 12:00" repeat="H" />
<task name="Task2" time="06/15/2009 12:00" repeat="W" />
<task name="Task3" time="06/29/2009 12:00" repeat="D" />
<task name="Task5" time="06/10/2009 19:00" repeat="M" />
</appSchedule>




The path to the tasks.xml should be configured in the service's app.config file

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="tasksConfigPath" value="E:\Training\Demo\SchedulerService\SchedulerService\Tasks.xml"/>
</appSettings>
</configuration>


Next, to ease the process of running a task, we made sure that all the tasks implement an interface ITask which has one method called RunTask that returns nothing (void).

MailTasks



namespace MailTasks
{
public interface ITask
{
void RunTask();
}
}



We used ThreadPooling to ease the burden of managing the threads. Since each task has to run in its own thread and we dont know how many threads to create, we zeroed in on ThreadPooling to manage the threads.


We have System.Timer object that periodically calls the RunTasks method. We use the global boolean variable workInProgress to track if the RunTasks method is running or idling. If the workInProgress is true we just return to wait for the completion of the earlier call to RunTasks method. If the workInProgress is false, we proceed further to run the scheduled tasks.


We get the list of tasks to run by calling a method GetTasksToRun(). Inside the GetTasksToRun method, we go through the DataSet with the tasks schedule information, for each task scheduled, if the current time is greater than the scheduled time, using reflection we create the Task Object that needs to run and then add it to the list of tasks to run.


Once we get the list of tasks to run, we update a global variable numBusy with the count of tasks to run. This numBusy variable will be used to track the number of busy threads at any given time. we loop through the scheduled tasks list, and queue each task in the ThreadPool by passing reference to a method (DoTask) and the task object itself to the ThreadPool's QueueUserWorkItem method.


Inside the DoTask method, we call the RunTask() method on the task object passed in as an argument. We update the next run time for the task in the DataSet by calling the method UpdateNextRunTime and decrement the count of busy threads (numBusy) in the finally.

Back in the RunTasks method we wait for all the threads to complete by calling WaitOne() method on the ManualResetEvent object doneEvent.


After all the queued tasks are complete, we persist the tasks data in the DataSet back to the disk and set workInProgress to false to mark the completion of all the tasks queued.


using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using System.Timers;
using System.Xml;
using System.Configuration;
using MailTasks;
namespace SchedulerService
{
public partial class Scheduler : ServiceBase
{
private static ManualResetEvent doneEvent;
private static string configPath = string.Empty;
private static int numBusy;
private static DataSet dsTasks;
private const string TASKS_NAME_SPACE = "MailTasks."; //Period is needed
private const string DATE_FORMAT_STRING = "MM/dd/yyyy HH:mm";
private static Assembly tasksAssembly;
private static EventLog eventLog1;
readonly System.Timers.Timer _timer = new System.Timers.Timer();
private static bool workInProgress;

public Scheduler()
{
InitializeComponent();
if (!EventLog.SourceExists("MailScheduler"))
EventLog.CreateEventSource("MailScheduler", "Application" );

eventLog1 = new EventLog("Application", Environment.MachineName, "MailScheduler");
}

protected override void OnStart(string[] args)
{
try
{
eventLog1.WriteEntry("Mail Scheduler Service Started");
LoadTasksIntoDataSet();
LoadTasksAssembly();
_timer.Interval = 60000;
_timer.Elapsed += RunTasks;
_timer.Start();
}
catch (Exception ex)
{
eventLog1.WriteEntry("Error occurred OnStart "+ex.Message);
}
}

protected override void OnStop()
{
eventLog1.WriteEntry("MailScheduler service stopped");
try
{
UpdateTasksConfigonDisk();
}
catch(Exception ex)
{
eventLog1.WriteEntry("Error occurred onStop "+ex.Message);
}
}

private static void RunTasks(object sender, ElapsedEventArgs args)
{
//If the processing of RunTasks lasts longer than the Timer's interval, RunTasks could be called
//again before the previous call finished. To overcome this, using a bool variable workInProgress to track if this method is in progress
//If not, go ahead else return
if (workInProgress) return;

numBusy = 0;
// LoadTasksIntoDataSet();
doneEvent = new ManualResetEvent(false);

List tasksList = GetTasksToRun();
numBusy = tasksList.Count; //Number of threads to create is not constant, depends on the tasks ready to run at a given time
if (numBusy > 0)
{
workInProgress = true;
foreach (ITask task in tasksList)
{
ThreadPool.QueueUserWorkItem(DoTask, task);
}
doneEvent.WaitOne();
}
//All scheduled tasks completed, persist the tasks data to disk,iteration over
if (numBusy == 0 && tasksList.Count > 0)
{
workInProgress = false;
UpdateTasksConfigonDisk();
}
}

private static void DoTask(object o)
{
ITask task = o as ITask;
if (task == null) return;
string scheduleName = task.GetType().ToString();
try
{
//Event Log, starting task at this time.
task.RunTask();
//Task completed successfuly at this time

int lastIndexOfPeriod = scheduleName.LastIndexOf(".");
UpdateNextRunTime(scheduleName.Substring(lastIndexOfPeriod + 1));
}
catch (Exception ex)
{
eventLog1.WriteEntry("Error occurred while executing task: " + scheduleName);
eventLog1.WriteEntry("Stack Trace is: " + ex.Message);
}
finally
{
if (Interlocked.Decrement(ref numBusy) == 0)
{
doneEvent.Set();
}
}
}

private static void LoadTasksIntoDataSet()
{
try
{
eventLog1.WriteEntry("Trying to Load Tasks into DataSet");
configPath = ConfigurationManager.AppSettings["tasksConfigPath"];
XmlTextReader xmlTextReader = new XmlTextReader(configPath);
XmlDataDocument xdoc1 = new XmlDataDocument();
xdoc1.DataSet.ReadXml(xmlTextReader, XmlReadMode.InferSchema);
dsTasks = xdoc1.DataSet;
xmlTextReader.Close();
eventLog1.WriteEntry("Finished Loading Tasks into DataSet");
}
catch(Exception ex)
{
eventLog1.WriteEntry("Error occurred while loading tasks into DataSet " + ex.Message);
throw;
}
}

private static void UpdateTasksConfigonDisk()
{
try
{
eventLog1.WriteEntry("Attempting to save tasks information to disk ");
StreamWriter sWrite = new StreamWriter(configPath);
XmlTextWriter xWrite = new XmlTextWriter(sWrite);
dsTasks.WriteXml(xWrite, XmlWriteMode.WriteSchema);
xWrite.Close();
}
catch (Exception ex)
{
eventLog1.WriteEntry("Error occurred while savings tasks information to disk "+ex.Message);
throw;
}
}

//updating the dataset is not thread safe
private static void UpdateNextRunTime(string taskName)
{
if (dsTasks == null) return;
foreach (DataRow row in dsTasks.Tables[0].Rows)
{
if (taskName.ToLower() != row[0].ToString().ToLower()) continue;
DateTime scheduledTime = DateTime.Parse(row[1].ToString());
string repeat = row["repeat"].ToString().ToUpper();
switch (repeat)
{
case "H":
scheduledTime = scheduledTime.AddHours(1);
if (scheduledTime < DateTime.Now)
scheduledTime = DateTime.Now.AddHours(1);
break;
case "D":
while (scheduledTime < DateTime.Now)
{
scheduledTime = scheduledTime.AddDays(1);
}
break;
case "W":
while (scheduledTime < DateTime.Now)
{
scheduledTime = scheduledTime.AddDays(7);
}
break;
case "M":
while (scheduledTime < DateTime.Now)
{
scheduledTime = scheduledTime.AddMonths(1);
}
break;
}
row[1] = scheduledTime.ToString(DATE_FORMAT_STRING);
dsTasks.AcceptChanges();
}
}

private static List GetTasksToRun()
{
if (dsTasks == null) return null;
List tasks = new List();
foreach (DataRow row in dsTasks.Tables[0].Rows)
{
DateTime scheduledTime = DateTime.Parse(row[1].ToString());
if (DateTime.Now < scheduledTime) continue;
ITask task = CreateTaskInstance(row[0].ToString());
if (task != null)
tasks.Add(task);
}
return tasks;
}

private static ITask CreateTaskInstance(string taskName)
{
string taskFullName = TASKS_NAME_SPACE + taskName;
try
{
if(tasksAssembly==null)
throw new Exception("Tasks Assembly is null, cannot proceed further..");
//Create an instance of the task
ITask task = (ITask)tasksAssembly.CreateInstance(taskFullName, true);
return task;
}
catch (Exception ex)
{
eventLog1.WriteEntry("Error occurred while creating Task Instance " + ex.Message);
}
return null;
}

private static void LoadTasksAssembly()
{
try
{
if (tasksAssembly == null)
tasksAssembly = Assembly.GetAssembly(typeof(MailTasks.ITask));
}
catch(Exception ex)
{
eventLog1.WriteEntry("Error occurred while loading tasks Assembly " + ex.Message);
throw;
}
}
}
}

Digg It! Add to del.icio.us Stumble This

Friday, May 22, 2009

Limit the characters in a multi row textbox

The maxlength property to limit the number of characters does not work on a multi row text box. We have to rely on custom JavaScript to limit the MaxLength functionality. Here is a snippet that does that.


function ValidateCharCount(txtBox, maxLength,controlName) {
var input;
var length;

input = txtBox.value;
length = input.length;

var allowedLength = new Number(maxLength);
if (length > allowedLength) {
txtBox.value = txtBox.value.substring(0, allowedLength);
alert(" Only " + maxLength + " characters are allowed in the field " + controlName);
txtBox.focus();
}
}


The function above takes three parameters. The first parameter is reference to the textbox control,second parameter is the max length of characters, third one is the textbox field description.
The function kicks in once the texbox loses focus, alerts the user if the character count exceeded the allowed length and then truncates the characters to the allowed length.

Usage:

<input type="text" rows="5" id="Description" onfocusout="ValidateCharCount(this,100,'Description')" />
<br/><br/>

Digg It! Add to del.icio.us Stumble This

IF by Rudyard Kipling

I came across this beautiful poem by Rudyard Kipling which totally blew my mind away. The poem is simple to read and sums up the way one has carry himself in just a few stanzas.
Here it is in its entirety.

[IF]

If you can keep your head when all about you
Are losing theirs and blaming it on you,
If you can trust yourself when all men doubt you
But make allowance for their doubting too,
If you can wait and not be tired by waiting,
Or being lied about, don't deal in lies,
Or being hated, don't give way to hating,
And yet don't look too good, nor talk too wise:

If you can dream--and not make dreams your master,
If you can think--and not make thoughts your aim;
If you can meet with Triumph and Disaster
And treat those two impostors just the same;
If you can bear to hear the truth you've spoken
Twisted by knaves to make a trap for fools,
Or watch the things you gave your life to, broken,
And stoop and build 'em up with worn-out tools:

If you can make one heap of all your winnings
And risk it all on one turn of pitch-and-toss,
And lose, and start again at your beginnings
And never breath a word about your loss;
If you can force your heart and nerve and sinew
To serve your turn long after they are gone,
And so hold on when there is nothing in you
Except the Will which says to them: "Hold on!"

If you can talk with crowds and keep your virtue,
Or walk with kings--nor lose the common touch,
If neither foes nor loving friends can hurt you;
If all men count with you, but none too much,
If you can fill the unforgiving minute
With sixty seconds' worth of distance run,
Yours is the Earth and everything that's in it,
And--which is more--you'll be a Man, my son!

--Rudyard Kipling

Digg It! Add to del.icio.us Stumble This

Monday, May 11, 2009

Debug classic ASP in Visual Studio 2008 with SP1

At work we still have some classic ASP applications. Visual Studio 2008 did not support classic ASP debugging until SP1. Once you installed VS 2008 SP1, follow these steps to debug classic ASP.
1) Enable ASP server side script debugging in IIS
a. To do this, launch IIS, right click on the virtual directory of your application, and click on properties from the context menu. Select the virtual directory tab from the properties windows. Click on the Configuration button in the application settings section.


b. In the application configuration window, select the debugging tab and make sure that enable ASP server-side script debugging is checked


2) From your visual studio 2008, launch the classic ASP application by pressing F5. Wait for the asp page to show up in the browser
3) Once the application is launched, go to Debug --> Attach to Process to launch the Attach to process window. In that window, click on the Show Processes for all users checkbox to show all processes.


















4) In the list of available processes, select the process dllhost.exe with user name COMPUTERName\IWAM_COMPUTERNAME and click Attach.
5) Set a break point in classic ASP code you want to browse, the execution should stop at the break point

Digg It! Add to del.icio.us Stumble This

Saturday, May 09, 2009

Adding custom client side validation to controls inside GridView

Some times we find it necessary to provide custom validation to controls inside a GridView.
Recently I had to have a DropDownList for selecting a country inside a gridview, the country dropdownlist contains several items including an item with the text "Other". When "Other" is selected, a textbox should show up where in the user can provide the rationale for selecting "Other". Also, I had to make sure that the textbox is not left blank when "Other" is selected.

To Recap
Requirements:
Make sure that a value is selected from the country dropdownlist.
When "Other" is selected for country, show a textbox to show enter what the "Other" country is
Make sure that a value is entered in the textbox when "Other" is selected

First the GridView, for simplicity I'm just showing the country dropdownlist and the TextBox. Both those controls are included in a templatefield in the GridView
Leaving out the databinding details.


I also add an eventhandler for OnRowDataBound event.


<asp:GridView ID="grdSales" runat="server" OnRowDataBound="grdSales_RowDataBound"
ShowFooter="true">
<Columns>
<asp:TemplateField HeaderText="Country">
<ItemTemplate>
<asp:RequiredFieldValidator runat="server" ID="reqValCountry" ControlToValidate="ddlCountry"
Display="Dynamic" ErrorMessage="Country is required<br>" ValidationGroup="Country"></asp:RequiredFieldValidator>
<asp:DropDownList ID="ddlCountry" runat="server">
<asp:ListItem Value=""> --Select Country--</asp:ListItem>
<asp:ListItem Value="1">USA</asp:ListItem>
<asp:ListItem Value="2">Canada</asp:ListItem>
<asp:ListItem Value="3">Mexico</asp:ListItem>
<asp:ListItem Value="4">Other</asp:ListItem>
</asp:DropDownList>
<asp:Panel ID="pnlOther" Style="display: none" runat="server">
Other:&nbsp;<asp:TextBox runat="server" ID="txtOther"></asp:TextBox>
<asp:CustomValidator ID="custValCountry" runat="server" ValidationGroup="Country"
ControlToValidate="ddlCountry" ClientValidationFunction="ValidateCountry"
ErrorMessage="Other is required"></asp:CustomValidator>
</asp:Panel>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

To meet the first requirement add a required field validator to ddlCountry drowdownlist.
For the second requirement, I need to add an onchange Event to the dropdownlist. In the event handler, I want to inspect the value selected and show the panel that contains the text box.
For the third requirement, I added a custom validator which calls a javascript function ValidateCountry.

Inside the grdCountry_RowDataBound event handler, pass the appropriate ids of the controls to the javascript functions.


protected void grdSales_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType != DataControlRowType.DataRow) return;

DropDownList ddl = e.Row.FindControl("ddlCountry") as DropDownList;
Panel pnl = e.Row.FindControl("pnlOther") as Panel;
TextBox txt = e.Row.FindControl("txtOther") as TextBox;
CustomValidator val = e.Row.FindControl("custValCountry") as CustomValidator;

if (ddl == null || pnl == null || txt == null || val == null) return;

//Add an onchange event to the ddlCountry drop down list which is in each data row of the grid view
//The javascript function will take as parameters Ids of the drop down list and the div that should be shows/hid

string jsToggle = "TogleCountry('" + ddl.ClientID + "','" + pnl.ClientID + "')";
ddl.Attributes.Add("onchange", jsToggle);

//The custom validation control will validate the drop down list. If option Other is selcted, it checks
//if the txtOther has some text
//Here we are adding ids of the ddlCountry and txtOther as client attributes of the validation control
//These attributes can be accessed from the client side function to help in doing the custom validation
val.Attributes.Add("ddl", ddl.ClientID);
val.Attributes.Add("txt", txt.ClientID);
}

Finally the javascript functions
I'm using ASP.net AJAX client side library here. If you are not using ASP.net AJAX just replace $get with document.getElementById

function TogleCountry(ddl, div) {
var selectedIndex = $get(ddl).selectedIndex;
$get(div).style.display = 'none';
if (selectedIndex == 5)
$get(div).style.display = 'block';
}


function ValidateCountry(sender, args) {
var ddl = sender.attributes["ddl"].value; //These attributes are added to the validator in the rowdatabound event of the GridView
var txt = sender.attributes["txt"].value;

var selectedIndex = $get(ddl).selectedIndex;
var txtOther = $get(txt).value;

if (selectedIndex == 4 && txtOther.length < isvalid =" false;" isvalid =" true;">

Digg It! Add to del.icio.us Stumble This

Find the cause of poor performance in Sql Server

I found the following two part article by Gail Shaw on Simple-Talk really helpful in trouble shooting poorly performing queries in Sql Server.
The articles talks about spotting poorly performing queries with the help of the Profiler, understand Sql Server Query plans and fine tune the peformance using proper indexes.

Part 1: http://tinyurl.com/ccl6gj
Part 2:http://tinyurl.com/okcuqg

Digg It! Add to del.icio.us Stumble This

Validating checkbox list in asp.net using JQuery

ASP.net does not provide in built support to validate a checkbox list. We have to rely on custom javascript to verify if at least one checkbox in the list is checked.

Since we cannot add a validator to the checkbox list, I usually put a dummy textbox on the form and add a custom validator to that textbox.


<asp:CheckBoxList ID="cblLostDescription" runat="server" RepeatDirection="Horizontal" TabIndex="14">
<asp:ListItem Value="L">Lost/Stolen</asp:ListItem>
<asp:ListItem Value="D">Destroyed</asp:ListItem>
<asp:ListItem Value="M">Damaged</asp:ListItem>
<asp:ListItem Value="O">Other</asp:ListItem>
</asp:CheckBoxList>
<asp:CustomValidator ID="custValDescrption" runat="server" ErrorMessage="Property Description is required"
Display="Dynamic" ControlToValidate="txtPropertyDummy"
ClientValidationFunction="ValidatePropertyDescription" ValidateEmptyText="true"></asp:CustomValidator>
<asp:TextBox runat="server" ID="txtPropertyDummy" Width="1px" Style="display: none"></asp:TextBox>


Following is the client side validation function that we added to the custom validator. If we take a look at the rendered html, all The checkboxes in our list do not have the same id or name attribute.But all the ids start with the id we gave to the checkbox list. We can use JQeury to select the group of checkboxes whose ids start with a given text , then loop through the set and verify if at least one checkbox is checked. Make sure you have a reference to the JQuery script file on your asp.net page.


//Using JQuery, select all the checkboxes whose id begins with the
//asp.net checkbox list server control's ID.
function ValidatePropertyDescription(sender, args) {
var chkGroup = $("input[id^=<%=cblLostDescription.ClientID%>]");
//Loop through the set returned by JQuery
for (i = 0; i < chkGroup.length; i++) {
if (chkGroup[i].checked) {
args.IsValid = true;
return;
}
}
args.IsValid = false;
}

Digg It! Add to del.icio.us Stumble This

Sunday, April 26, 2009

Clear Validation Errors and Validation Summary messages

ASP.net built in validation does not provide us a straight forward to clear all the validation errors. This would be really helpful while resetting a form. The reset html button would simply reset the form values but will not clear the validation errors.

The following javascript code snippet can be used to clear the validation error messages.
Have a reset button on your form and call the following js function onclick.

<input type="reset" onclick="HideValidationErrors();" />


     function HideValidationErrors() {
        //Hide all validation errors
        if (window.Page_Validators)
            for (var vI = 0; vI < Page_Validators.length; vI++) {
            var vValidator = Page_Validators[vI];
            vValidator.isvalid = true;
            ValidatorUpdateDisplay(vValidator);
        }
        //Hide all validaiton summaries
        if (typeof (Page_ValidationSummaries) != "undefined") { //hide the validation summaries
            for (sums = 0; sums < Page_ValidationSummaries.length; sums++) {
                summary = Page_ValidationSummaries[sums];
                summary.style.display = "none";
            }
        }
    }

Digg It! Add to del.icio.us Stumble This