The Self-Taught Programmer

Recipes from real-world experience with asp.net.

Archive for the ‘How-To’ Category

Entity Framework: Repository or Not Repository?

leave a comment »

On the heels of the demise of the commercial version of EntitySpaces, I am delving into Microsoft’s Entity Framework. I need to implement it in a big project to replace a much more manual Data Access Layer.

So I’ve done a ton of reading on it (and played with it some, too), and the common wisdom is to use the Repository Pattern to implement it. The main reason seems to be to maintain a separation of concerns — so that the persistence layer isn’t directly used/referenced from the UI. In turn, this seems to be important so that you can change the underlying data structure without changing the UI very much.

At first, this seems to add a layer of complexity to an application that doesn’t seem to be necessary — I never used this pattern with EntitySpaces (although I could have), and indeed, the ES developers recommended against it. Entity Framework uses the dbcontext as its own repository/unit of work pattern, so why add a whole other level of abstraction to it? Adding complexity will surely slow down development, and speeding things up is my primary motivation for using an ORM framework.

There is a terrific case made for NOT using repositories by Oren Eini (aka Ayende Rahien), a contributor to the NHibernate open-source ORM framework. His post can be found here.

Will I ever actually change the database layer?

As I said, this doesn’t seem to be necessary – I don’t believe I will change the underlying database from SQL Server to Oracle or some other database. Even if I did, Entity Framework can work with many different databases.

I suppose there is a possibility that it could change to a web service implementation. I don’t know why the project would change like that, but it could if the need arose. In this case, repositories would insulate the UI from that change. I am implementing EF using POCOs, so those could be kept and reused even with web services.

But What If I Change the UI?

But after thinking about it for a while, the most important reason to maintain separation of concerns in my project is that I don’t know how the UI layer will change. I can already see the possibility of creating several UIs – Web, mobile, and public web services. Aha! If I use repositories, I can develop as many UIs as I want using the same data access layer. Or almost the same data access layer — I am certain I would have to add custom methods to my repositories for each UI.

But can I still create multiple UIs if I don’t use repositories — using EF directly? Yes, of course I can. On the downside, there will be some duplication of code. Conversely, the data access will be tailored directly to my UI needs.

So what’s the right answer?

Guess what, despite the legions of fanatics that will tell you there is only one “right” way to do it, either way will work.

For speed’s sake, I strongly considered going without repositories, but I have decided to use them based on the multiple examples of their effectiveness and my uncertainty about future UI development for my project.

Written by Jim McMullen

February 1, 2013 at 10:32 pm

Posted in How-To

Tagged with

How to Find the Index of an Object in a List

leave a comment »

The Situation

You are trying to find out if an object exists in a List object.

A Solution

Use Linq. The example below looks for a User object in a List<User> by searching for the UserName:

private int FindObjectInList(List<User> list, string uName)
{
    return list.FindIndex(s => s.UserName == uName);
}

Written by Jim McMullen

February 1, 2013 at 10:23 pm

Posted in How-To

Tagged with ,

How to Pass Extra Parameters When Using AJAX Toolkit CascadingDropDowns with a Database

leave a comment »

The Situation:

You have set up your CascadingDropDowns as shown on the ASP.NET AJAX Control Toolkit documentation here, using a web service to retrieve and populate your dropdowns. However, in addition to the value of the first dropdown, you need to pass other parameters to the web service to be used in your query.

A Solution:

The CascadingDropDown control has a mechanism for this — ContextKey. You can add it like at design time like this (note the last 2 lines):

<ajaxToolkit:CascadingDropDown
	ID="CascadingDropDown3"
	runat="server"
	TargetControlID="dd2"
	ParentControlID="dd1"
	PromptText="Please select blah blah blah"
	ServiceMethod="GetDDdata"
	ServicePath="WebSerivce.asmx"
	Category="ObjectName"
	UseContextKey="true"
	ContextKey="0" />

This works great for passing a single parameter. And you can set it dynamically in the code-behind like this (make sure to include a “using AjaxControlToolkit;” in the code-behind):

CascadingDropDown ccd = (CascadingDropDown)CascadingDropDown2;
ccd.ContextKey = avalues + ";";

But what if you want to pass more than a single value?

You can always set ContextKey to a delimited string, which will work just fine. But the AJAX toolkit’s CascadingDropDown control has a function called “ParseKnownCategoryValuesString(string)”, which accepts a string delimited in this way:

“key:value;key:value;key:value;”

And returns a StringDictionary containing the keys and their values, so you can easily use them in your code.

Here’s an example of how to use this in your web service code:

[WebMethod]
public CascadingDropDownNameValue[] GetDDdata(string knownCategoryValues, string category, string contextKey)
{
	// The Value of the parent DropDown is in knownCategoryValues
	StringDictionary kv = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
	int someId;
	if (!kv.ContainsKey("SomeItemName") ||
		!Int32.TryParse(kv["SomeItemName"], out someId))
	{
		return null;
	}

	// Other parameters are in contextKey. Must be formatted the same as knownCategoryValues
	//  (i.e., "key:value;key:value;key:value;")
	StringDictionary ck = CascadingDropDown.ParseKnownCategoryValuesString(contextKey);

	string SomeOtherThing = "";
	if (!ck.ContainsKey("SomeOtherThing") || ck["SomeOtherThing"] == null)
	{
		return null;
	}
	else
	{
		SomeOtherThing = ck["SomeOtherThing"];
	}
	
	// ... do some other processing here ...
	
	return result;
}

Written by Jim McMullen

November 1, 2012 at 12:14 pm

Posted in How-To

Tagged with ,

How to Force an ASP.NET Page into IE Compatibility Mode (Even when Using Themes)

leave a comment »

The Situation:

You need to to use a control that is not fully compatible with the latest version of Internet Explorer (or you find that your page just doesn’t function the same in the latest version).

A Solution:

The normal solution (and the one that nearly everyone can use UNLESS they are working with ASP.NET and themes) is detailed here (http://msdn.microsoft.com/en-us/library/cc288325(v=vs.85).aspx). Basically, that solution says to put a meta tag in the head section of your page like the one shown below which forces IE into IE8 compatibility mode.

<meta http-equiv="X-UA-Compatible" content="IE=8" >

Now here’s the MOST IMPORTANT PART — this meta tag MUST be the first thing after the opening HEAD tag.

And this is what causes the big problem for anyone creating an ASP.NET web site that uses themes — ASP.NET injects stylesheets for the theme into the page as the first elements after the opening HEAD tag, so IE ignores our awesome meta tag. And to complicate things even further, ASP.NET performs this injection very late in the page life cycle, so it is hard to override it.

I solved this problem recently by using the code-behind to inject the meta tag. To make sure the meta tag would be the last thing injected (after the theme stylesheets), I injected it at the very start of the RENDER event of the page life cycle like this:

protected override void Render(HtmlTextWriter writer)
{
	// This code injects an IE compatibility meta tag into the page "head" section to resolve
	//   version incompatibility issues IE.
	//   This tag has to be injected at the very last moment before rendering the html
	//   because ASP.NET injects theme stylesheets that interfere with this tag.
	string meta = @" <meta http-equiv='X-UA-Compatible' content='IE=8'> ";

	// add the meta tag to head BEFORE the page starts rendering
	Literal ctrl = new Literal();
	ctrl.Text = meta;
	Page.Header.Controls.AddAt(0, ctrl);

	// Now continue with the page Render event
	base.Render(writer);
}

Note that the code above is an override method — you can cut and paste this code directly in your page‘s (or master page’s) code behind. ASP.NET knows how to process it and passes the ‘HtmlTextWriter’ parameter into the method without our intervention.

What about injecting the meta tag in the PRE-RENDER stage?

You may find the solution at https://connect.microsoft.com/VisualStudio/feedback/details/581278/setting-x-ua-compatible-meta-tag-in-asp-net-4-0-site-doesn-t-work-yes-it-s-at-the-top, which involves finding and moving the meta tag in the pre-render event, works for you. It did not work for me, and I suspect it has to do with the page life cycle when a master page is used. When I tried it, the suggested code seems to have performed its job BEFORE the theme stylesheets were injected, so the meta tag still ended up below the stylesheets and was therefore ignored.

Written by Jim McMullen

October 22, 2012 at 4:57 pm

Posted in How-To

How to Catch Errors when Using ObjectDataSource

leave a comment »

The Situation:

You have an ASP.NET page that uses an ObjectDataSource to provide data access for a GridView, FormView or list control like a DropDownList or CheckboxList. When a data access error occurs, you would like to display a message on the page to help users understand what is happening.

A Solution:

You could try to handle the errors in the GridView or FormView by using the Updating, Selecting, or Deleting events, but perhaps you are in a position that the ObjectDataSource feeds data to more than one control on the page.

Instead of trying to handle the error in the consuming controls, implement the error handling in the ObjectDataSource itself using those same events. Here’s an example of what I mean:

<asp:ObjectDataSource ID="objCurrentRecord" runat="server" InsertMethod="InsertWidget"
	SelectMethod="GetWidgetByID" UpdateMethod="UpdateWidget" TypeName="AppWidgets"
	oninserted="objCurrentRecord_Inserted" onupdated="objCurrentRecord_Updated"
	ondeleted="objCurrentRecord_Deleted">
	...
</asp:ObjectDataSource>

 

And in the code-behind, implement the oninserted, onupdated, and ondeleted methods from above.

In the following example, my data access code throws a custom error when I try to insert a widget if the widget already exists, so I am checking for that error to provide feedback to the user. I have also enhanced the basic code to display a message upon successful insertion of the record.

protected void objCurrentRecord_Inserted(object sender, ObjectDataSourceStatusEventArgs e)
{
	if (e.Exception != null)
	{
		if (e.Exception.InnerException.Message == "Widget already exists.")
		{
			lblResults.Text = e.Exception.InnerException.Message;
		}
		else
		{
			lblResults.Text = e.Exception.Message;
		}

		// MUST SET EXCEPTIONHANDLED TO TRUE so that the exception
		//    is not sent on to the calling control.
		e.ExceptionHandled = true;
	}
	else
	{
		lblResults.Text = "Widget Added.";
	}
}

 

Of course, you could do this in a variety of ways, the most flexible of which would be to create a method that reads the standard or custom error message and translates it into something that would make sense to the user.

Or even returning the standard error message itself like this:

protected void objCurrentRecord_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
	if (e.Exception != null)
	{
		lblResults.Text = e.Exception.Message;

		// MUST SET EXCEPTIONHANDLED TO TRUE so that the exception
		//    is not sent on to the calling control.
		e.ExceptionHandled = true;
	}
	else
	{
		lblResults.Text = "Widget Updated.";
	}
}

 

Returning the standard error message in a label is not very helpful to the end-user, but it may be better than sending them to an error page. Another way to handle this would be to simply display a “An error occurred when we tried to save your changes.” message.

Written by Jim McMullen

October 19, 2012 at 12:15 pm

Posted in How-To

Tagged with ,

How To Edit a Record in an ASPxGridView Bound to EntitySpaces’ esDataSource that Uses a Join

leave a comment »

The Situation:

You get an error when you try to save edits to a record in a DevExpress ASPxGridView. The grid view is bound to an EntitySpaces esDataSource, which is populated using EntitySpaces dynamic query functionality. The ES dynamic query syntax allows you to use joins between entities like this (actual code from one of my recent projects):

private SubscriptionScopeCollection GetScopes(int catid)
{
	SubscriptionScopeQuery ssq = new SubscriptionScopeQuery("q1");
	VwClientSubscriptionsQuery csq = new VwClientSubscriptionsQuery("q2");

	ssq.Select(ssq, csq.ClientName );
	ssq.InnerJoin(csq).On(csq.SubscriptionId == ssq.SubscriptionId);
	ssq.OrderBy(csq.ClientName.Ascending, csq.SObjectName.Ascending);

	SubscriptionScopeCollection coll = new SubscriptionScopeCollection();
	coll.Load(ssq);
	
	return coll;
}

What this gives you is an entity collection full of entities that have extra columns (the fields from the joined tables – in this case, the “ClientName” field). EntitySpaces stores these extra columns in a Dictionary object.

Why This Causes an Error…

esDataSource has functionality built in to update/delete an entity. This is one of its biggest advantages. When it tries to save the changes, it creates a new version of the entity to work with. As the EntitySpaces documentation states, you have to put the following code into your code-behind to allow it to do this:

protected void esDataSrc_esCreateEntity(object sender, esDataSourceCreateEntityEventArgs e)
{
    SubscriptionScope entity = new SubscriptionScope();
    if (e.PrimaryKeys != null)
        entity.LoadByPrimaryKey((int)e.PrimaryKeys[0]);
 
    // Assign the Entity
    e.Entity = entity;
}

However, the ASPxGridView sends ALL field values back to be saved, including the extra fields. The entity created using the code above does not contain those extra fields, so it will throw an error when it tries to process them.

The Solution:

To make the entity recognize those extra fields, you have to add them into the entity’s ExtraColumns object. Doing so will NOT allow the entity to save their data, but it will keep it from throwing an error. (If you want to save the data from those extra fields, you have to override the esDataSource’s save functionality with your own.)

Add the extra columns to the entity by adding the indicated lines to the code above

protected void esDataSrc_esCreateEntity(object sender, esDataSourceCreateEntityEventArgs e)
{
    SubscriptionScope entity = new SubscriptionScope();
    if (e.PrimaryKeys != null)
        entity.LoadByPrimaryKey((int)e.PrimaryKeys[0]);

    // THESE LINES ADDED TO MAKE THE ENTITY RECOGNIZE THE EXTRA FIELD
    Dictionary<string,object> xtra = new Dictionary<string,object>();
    xtra.Add("ClientName", null);
    entity.SetExtraColumns(xtra);
 
    // Assign the Entity
    e.Entity = entity;
}

Written by Jim McMullen

August 12, 2012 at 12:18 am

Posted in How-To

Tagged with , ,

How to get the value from a GridView column on RowDeleting

leave a comment »

The Situation:

You need to get the value of one of the columns in the row you are deleting in a GridView to do some processing before the row is deleted.

A Solution:

When using the RowDeleting method, you cannot use e.Row.Cells[1].Controls[1] to get the control in that cell like you can in the RowCreated method. Instead, you can simply access the text of the desired cell and convert it to whatever datatype you need, like this:

protected void gridview1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
	// You get the Key (identity value) of the row like this...
	string userName = gridview1.DataKeys[e.RowIndex].Value.ToString();
	
	// And you can get the value of any other cell in the row like this... 
	// (this would get the value in the 4th cell)
	string cellval = gridview1.Rows[e.RowIndex].Cells[3].Text;
	
	// DO MORE PROCESSING HERE
}

Written by Jim McMullen

June 19, 2012 at 3:51 pm

Posted in How-To

Tagged with

How to Get the Name of the Current Folder from the URL

leave a comment »

The Situation:

You need to get the name of the current page request folder from the URL.

A Solution:

Use this method:

public string GetCurrentFolderName()
{
	//get current file path 
	string folder = System.Web.HttpContext.Current.Request.FilePath;

	//find the position of the last '/' 
	int intPos = folder.LastIndexOf("/");

	// if it is not the root folder...
	if ( intPos > 0 )
	{
		//cut off all characters after the last '/' 
		folder = folder.Substring(1, intPos - 1);

		//find the position of the last '/' in the new string 
		int intPos2 = folder.LastIndexOf("/");

		//get the length of the characters between the two '/' 
		int intLength = ( intPos - 1 ) - ( intPos2 + 1 );

		//get the characters between the 2 '/' 
		folder = folder.Substring(intPos2 + 1, intLength);
	}
	else
	{
		folder = "";
	}

	return folder;
}

Written by Jim McMullen

December 2, 2011 at 11:06 am

Posted in How-To

Tagged with ,

How to Find the Index of a String in a List

leave a comment »

The Situation

You are trying to find out if a string exists in a List object.

A Solution

You could use a Find with a predicate, or try the simple method below.

private int FindStringInList(List<string> list, string str)
{
    return list.FindIndex(s => s == str);
}

Written by Jim McMullen

October 17, 2011 at 3:54 pm

Posted in How-To

Tagged with ,

How to Troubleshoot Problems Upgrading an Existing App from EntitySpaces 2009

leave a comment »

I am a big fan of EntitySpaces, a .NET ORM framework that I find very easy to use. When the developers upgraded the framework in 2010, they “broke” some things I used in my code. Specifically, they changed the way instantiate a new ORM entity. Entities used to have a method that looked like this…

esEntity.AddNew()

According to the release notes:
This method has been removed. It was optional in 2009 and has finally been removed. If you have overloaded or overridden this virtual method in a Custom class, you should move that logic to the constructor in your Custom classes (see above) and remove the custom AddNew method.

In fact, there were a bunch of “breaking” changes in ES2010. Keep a copy of the release notes when you upgrade past 2010 so that you will have a way to troubleshoot old apps when you upgrade them. (This is a good idea for all developer tools and languages, in fact.)

Written by Jim McMullen

October 17, 2011 at 2:37 pm

Posted in How-To

Tagged with ,