RAD for N-Tier web apps in .NET

December 4, 2009

Integrating FreeText Search in NHibernate Detached Criteria

Filed under: General — Eric P @ 5:01 am

Sql Server 2005/2008 has very nice FreeText searching capabilities. It is pretty easy to run FreeText query in Sql, but how would one do it in NHibernate.

To be able to use “contains” or “freetext” functions in HQL, the functions need to be registered in an override of MsSql2008Dialect, as is done here:
http://nhforge.org/blogs/nhibernate/archive/2009/03/13/registering-freetext-or-contains-functions-into-a-nhibernate-dialect.aspx

But how would one do it in Criteria or DetachedCriteria. The approach I chose was to implement a new class based on ICriterion, AbstractCriterion. To simplify the process I downloaded latest NHibernate 2.1 and based my class on one of the already existing ICriterion implementations.

You will need NHibernate 2.1 and FreeText functionality enabled on your Sql Server database. Also all the text columns that are searched using FreeText Search, must be indexed in a FreeText catalog.
Here is the code for ContainsExpression class which allows for using Criteria API for simple “CONTAINS” searches:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Engine;
using NHibernate.SqlCommand;
using NHibernate.Util;

namespace  ENTech.Common.Infrastructure
{
	public class ContainsExpression : AbstractCriterion
	{
		private readonly string propertyName;
		private readonly object value;
		private readonly IProjection projection;

		public ContainsExpression(IProjection projection, object value)
		{
			this.projection = projection;
			this.value = value;
		}

		public ContainsExpression(string propertyName, object value)
		{
			this.propertyName = propertyName;
			this.value = value;
		}

		public override SqlString ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery,
		                                      IDictionary<string, IFilter> enabledFilters)
		{
			//TODO: add default capacity
			SqlStringBuilder sqlBuilder = new SqlStringBuilder();
			SqlString[] columnNames =
				CriterionUtil.GetColumnNames(propertyName, projection, criteriaQuery, criteria, enabledFilters);

			SqlString columnName = columnNames[0];

			criteriaQuery.AddUsedTypedValues(GetTypedValues(criteria,criteriaQuery));
			sqlBuilder
				.Add("contains(")
				.Add(columnName)
				.Add(StringHelper.CommaSpace);

			sqlBuilder.AddParameter();
			sqlBuilder.Add(")");

			return sqlBuilder.ToSqlString();
		}

		public override TypedValue[] GetTypedValues(ICriteria criteria, ICriteriaQuery criteriaQuery)
		{
			return CriterionUtil.GetTypedValues(criteriaQuery, criteria, projection, propertyName, value.ToString().ToLower());
		}

		public override IProjection[] GetProjections()
		{
			if(projection != null)
			{
				return new IProjection[] { projection };
			}
			return null;
		}

		/// <summary></summary>
		public override string ToString()
		{
			return (projection ?? (object)propertyName) + " contains " + value;
		}
	}
}

The main method in this class is “ToSqlString” which generates Sql from Expression parameters.

Here is the unit test that checks this functionality.
It basically gets a count of all non-deleted Products with name or description that contain “pants” word.

using System;
using Castle.ActiveRecord;
using ENTech.Common.Infrastructure;
using NHibernate.Criterion;
using NUnit.Framework;
using Modules.ProductCatalog;

namespace ENTech.Common.Infrastructure.Tests
{
    [TestFixture]
    public class NHibernateTest
    {

		[Test]
		public void Search_For_Pants_Using_DatachedCriteria_With_ContainsExpressioin()
		{
			using (new SessionScope())
			{
				string keyword = "pants";

				var query = DetachedCriteria.For<Product>();

				query.Add(Restrictions.IsNull("DateDeleted"));

				ICriterion criterion = new ContainsExpression("Name", keyword);
				criterion = Restrictions.Or(criterion, new ContainsExpression("Description", keyword));

				query.Add(criterion);

				Console.WriteLine("Total products: " + ActiveRecordMediator.Count(typeof(Product), query));
			}
		}
	}
}

Using NHProf. I can see that the following sql query is generated:

SELECT count(* ) as y0_
FROM   Product this_
WHERE  this_.DateDeleted is null
       and (contains(this_.Name,'pants' /* @p0 */)
             or contains(this_.Description,'pants' /* @p1 */))

July 10, 2009

ASP.NET MVC Grid – Part 5 – Persisting Grid State

Filed under: ASP.NET MVC, General — Eric P @ 8:14 am

In this part I will implement the following items:

  • persist the state of the grid
  • update UI with row striping and row click actions
  • handle a “last customer on page” edge case
  • add status/notifications line when customer is added/deleted/modified
  • validate edit form
Click on the image to view the whole screenshot.

Click on the image to view the whole screenshot.

You can see demo here:
http://samples.entechsolutions.com/MvcGridSample/Part5

Code is available here:
http://samples.entechsolutions.com/Downloads/MvcGridSample/MvcGridSample_Part5.zip

Implementation

1. Persist state of the grid
A common use case for any grid control – is remembering the state of the grid when user comes back to the list page. So if grid is currently on page 2 and sorted by phone and user clicks on Edit – then saves customer and goes back to grid – the grid will still be on page 2 and sorted by phone.

To implement this functionality I created a new service class GridStateService with methods
Save(key, Grid)
Grid Load(key)
bool Exists(key);

This service uses session to save/load grid’s state, so while the person is using the site and his/her session hasn’t expired, the grid state (current page, page size, etc…) persists. If needed, it should be simple to change the persistence mechanism to store grid state in the cookie or the database. In those cases, whenever user would come back to the site, his/her last preferences (like sorting by “Phone” or page-size=50) would remain.

Here is how GridStateService is used in CustomerController.List:


[AcceptVerbs(HttpVerbs.Get)]
public ActionResult List()
{
	string key = GetGridKey();   //This function returns current url used to uniquely identify the grid
	if (_gridStateService.Exists(key))
	{
		_grid = _gridStateService.Load(key);
	}
	else
	{
		_grid = new Grid<Customer>
					(
						new Pager {CurrentPage = 1, PageSize = 5},
						new Sorter("ID", SortDirection.Asc)
					);
	}

	UpdateGridData();

	return View(_grid);
}

Here is the code the saves last state of the grid.


[AcceptVerbs(HttpVerbs.Post)]
public ActionResult List(Grid<Customer> grid)
{
	_grid = grid;

	_grid.ProcessAction();

	UpdateGridData();

        //Check if there is data in the grid
	if (_grid.Data.Count() > 0)
		SaveGridState();

	return View(_grid);
}

private void SaveGridState()
{
	string key = GetGridKey();
	_gridStateService.Save(key, _grid);
}

2.Row striping and row click

The row striping and highlighting (which allows for better readability) was implemented using approach described in the following article:
http://webdevdotnet.blogspot.com/2009/06/aspnet-mvc-jquery-part-2-zebra-striping.html

As for row-clicking – I added onlcick handler to data row’s tr tag:

<tr onclick="onRowClick(<%= item.ID %>)">
     <td>

When user clicks on the row they will be taken to customer edit screen. This is handled by JS function:

function onRowClick(id) {
	document.location = "/Customer/Edit/" + id;
}

3. Edge case for deleting last customer on the page

If I am using 5 rows per page with 6 customers and I deleted sixth customer on the second page, the grid should automatically switch to page 1 instead of showing page 2 with no customers.

This condition was handled in pager using the following code:

//handle a case when user deleted all rows on last page
if (_currentPage > _totalPages)
    _currentPage = _totalPages;

4. Status line when customer is added/deleted/modified

This functionality is related to operations triggered in the grid page.
Previously, if I deleted a customer there would be no indication that customer was deleted. The grid would get refreshed, but there would be no explicit notification that customer was actually deleted.

I used TempData functionality on the MasterPage to display such messages/notifications. All operations, add/edit/delete, that are successfully performed on the customer are now followed by a status line message (yellow box) on the grid page.
This message fades out after 5 seconds (ain’t JQuery great).

5. Validate add/edit form
This functionality is not really grid-related, but I thought it would be important to have it for sample completeness. It is not very realistic to add a customer with all fields empty. It will also help when I implement inline editing.

I used Data Annonations approach described here:
http://www.asp.net/learn/mvc/tutorial-39-cs.aspx

I update UI a little to display error message right next to the input fields which are invalid.

Observations

  • Data annotations has a property DataType which takes DataType enumeation consisting of members like “Currency”, “Email Address”, etc… I originally thought it would validate my fields according to the type I pass, but that doesn’t work. But it doesn’t work this way. In one of the comments to article http://www.aspworkshops.com/blog/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx it states:

    The DataType attribute are not validators.
    So DataType(DataType.Email) will not validate a string for being an email address. These are ui type hint sttributes.

    GRRRRRRReat… I guess I will need a reg expression validator or custom validator for Email/Phone validations.

  • When messing with Data Annotations, I discovered another error. Even though it worked ok when binding simple models, I started getting NullReferenceException when binding to complex models (with sub objects). I discovered the fix here:

    http://stackoverflow.com/questions/820468/how-does-dataannotationsmodelbinder-work-with-custom-viewmodels

  • One more thing related to validation. Looks like there is no way to provide custom error messages for data type conversions during binding. So if user enters some text into “Orders Placed” field – the error message is always:

    The value ‘SOME VALUE’ is not valid for the Orders Placed field.

    This is confirmed by Scott Gu in one of the comments to the following article:
    http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx

    # re: ASP.NET MVC Preview 5 and Form Posting Scenarios
    Wednesday, September 03, 2008 11:39 PM by ScottGu

    Hi Florian,

    >>>>>>> I very much like the approach, cannot wait until this stuff is baked. One question – is there a way to intercept input validation and specify custom ErrorMessages?

    >>>>>>> E.g. in your example where the user enters a random string instead of the decimal number required by the model field you’re auto-generating an error message (‘.. invalid value ..’).

    >>>>>>> Is there a way to customize (and localize) this message, without inspecting and changing ModelState explicitely?

    Unfortunately with Preview 5 you need to manipulate the ModelState dictionary directly to enable this (either that or override the ModelBinder behavior to customize your own message).

    Hope this helps,

    Scott

    NOTE TO SELF: Add custom binder with a way to override type conversion error message.

Coming up…

There are a couple of items left before I will make the grid generic and re-usable. For the next part I will work on:

  • Making grid work if Javascript is disabled. This was brought to my attention by a comment in Part 3. Thank you, sironfoot.
  • Advandced search form – which will include fields “First Name”, “Last Name”, “Email”, date range for “Date of Last Order”, etc…
  • AJAX implementation so the whole page doesn’t refresh. Will work similar to how GridView worked in ASP.NET web forms if you put UpdatePanel around it.

July 6, 2009

ASP.NET MVC Grid – Part 4 – View Models, Unit Testing, Keyword Search

Filed under: ASP.NET MVC, General — Eric P @ 12:00 am

In this part, I decided to take a step back and do some refactoring. At the same time I would like to keep moving forward, by adding new Keyword Search functionality as well as an Auto Complete control to quickly find/edit a customer.

Click image to view full screenshot

Click image to view full screenshot

You can see demo here:
http://samples.entechsolutions.com/MvcGridSample/Part4

Code is available here:
http://samples.entechsolutions.com/Downloads/MvcGridSample/MvcGridSample_Part4.zip

Implementation

1. Use View Model (instead of ViewData) to organize Grid variables

As I mentioned in last couple of posts, ViewData was being used for too many variables. Such variables in ViewData are not strongly typed and result in verbose code which could be error-prone (for example null errors).

Instead I introduce the following class structure for Grid View Model:

class Grid
{
	Pager
		CurrentPage
		TotalPages
		RowStats
		...

	Sorter
		SortField
		SortDirection

	SearchCriteria	- new functionality for keyword search
		Keyword

	GridAction - enumeration with items like "Sort", "GoToPage", ...
}

Using this class feels a lot cleaner then ViewData. In the code below Model is of class Grid.

<div id="gridHeader">
			<table>
				<tr>
					<td id="rowStats">
						<%= Model.Pager.RowStats %>
					</td>

					<td id="pagerNav">
						<% if (Model.Pager.IsNavVisible) { %>
							<table>
								<tr>
									<td>
										<% if (Model.Pager.IsFirstPage) { %>
											<span class="disabled">&lt;&lt;</span>
										<% } else { %>
											<a href="#" onclick="goToPage(<%= Model.Pager.FirstPage %>)">&lt;&lt;</a>
										<% } %>
									</td>
...

2. Write unit tests for pager. When creating a Pager class, I decided to follow TDD methodology and wrote unit tests first. Through this approach I discovered a lot of errors before actually integrating Pager into UI.

Here is a couple of unit tests… That check cases when Pager navigation should be visible and not visible.

namespace MvcGridSample.Tests.ViewModels.Shared
{
	[TestClass]
	public class PagerTest
	{

	       [TestMethod]
		public void Nav_Not_Visible_If_One_Page()
		{
			var pager = CreateAndInitPager(1, 10, 5);
			Assert.IsFalse(pager.IsNavVisible);
		}

		[TestMethod]
		public void Nav_Visible_If_More_Then_One_Page()
		{
			var pager = CreateAndInitPager(1, 10, 15);
			Assert.IsTrue(pager.IsNavVisible);

		}
               ...

3. Add Keyword search. This functionality allows user to search data by keyword which is matched against First Name, Last Name, Email or Phone. It can be expanded to search through other fields too (for ex. Customer Address – if one would exist).

The keyword search criteria was added to LINQ query using the following code:

private IQueryable<Customer> AddQuerySearchCriteria(IQueryable<Customer> query, SearchCriteria searchCriteria)
{
	if (!String.IsNullOrEmpty(searchCriteria.Keyword))
	{
		string keyword = searchCriteria.Keyword.ToLower();
		query = query.Where(customer => customer.FirstName.ToLower().Contains(keyword)
		                                || customer.LastName.ToLower().Contains(keyword)
						|| (customer.Email != null && customer.Email.Contains(keyword))
						|| (customer.Phone != null &&  customer.Phone.Contains(keyword)));
	}

	return query;
}

Note that I had to ensure that Email and Phone were not null, before checking them against keyword. Otherwise there were some “object reference is null” exceptions.

4. Add Auto Complete on Keyword. This is for a common use case when you would like to quickly find a record and perform certain operation on it. To try it out – start typing name of existing customer in keyword box. After first couple of letters you will see auto-complete drop down with a list of matching customers (by first name and last name).

If you select the customer in the list and click enter or click on it with a mouse – you will be taken to Edit Customer page.

To implement this functionality I used a JQuery auto-complete plugin from here:
http://docs.jquery.com/Plugins/Autocomplete

For this to work I created a method on controller that would return list of customer names in JSON format:

public ActionResult GetKeywordAutoCompleteData(string q, int limit)
{
	string keyword = q;

	IQueryable<Customer> query = _customerService.GetQueryable();
	query = query.Where(customer => customer.FirstName.ToLower().StartsWith(keyword)
	                                || customer.LastName.ToLower().StartsWith(keyword))
					.Take(10)
					.OrderBy(customer => customer.FirstName)
					.ThenBy(customer => customer.LastName);

	var list = query.Select(customer => new {
												customer.ID,
												Name = customer.FirstName + " " + customer.LastName});

	return Json(list);
}

Then call this method from client side using JQuery auto-complete plugin functionality:

jQuery(document).ready(function() {

	$("#SearchCriteria_Keyword").autocomplete(
		"/Customer/GetKeywordAutoCompleteData",
        {
        	dataType: 'json',
        	parse: function(data) {
        		var rows = new Array();

        		for (var i = 0; i < data.length; i++) {
        			rows[i] = { data: data[i], value: data[i].Name, result: data[i].Name };
        		}
        		return rows;
        	},
        	formatItem: function(row, i, n) {
        		return row.Name;
        	},
        	width: 260,
        	selectFirst: false
        }
    );
    ....

Observations

  • When HtmlHelper functions generate HTML controls – control IDs replace character “.” with “_”. For ex, if I want to use the following control in Jquery:

         <%= Html.Hidden("Sorter.SortDirection")%>
    

    I need to call it like this:

         $("#Sorter_SortDirection").val(someVal);
    
  • Coming from Web Forms world, it seemed natural to me to create /App_Code directory to put all the utility classes.
    It has been working fine till now, but all of a sudden I started receiving compilation error:

    Compiler Error Message: CS0433: The type ‘MvcGridSample.StringFormatter’ exists in both ‘c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\752a9979\7f1c3889\assembly\dl3\da179bc1\70581e2e_8ff9c901\MvcGridSample.DLL’ and ‘c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\752a9979\7f1c3889\App_Code.nzexrw9-.dll’

    The reason for this issue is explained on the following page:
    http://weblogs.asp.net/meligy/archive/2008/08/03/converting-vs-2008-website-to-web-application.aspx

    During the conversion, VS 2008 renames your “App_Code” to “Old_App_Code”. This new name sounds ugly, but DO NOT RENAME IT BACK. In the “web application” model, all code will be in one assembly. In runtime, the web server does not know what web project type you are using. It does take all code in “App_Code” folder and create a new assembly for it. This way, if you have code in folder named “App_Code”, you’ll end up with RUNTIME compilation errors that the same types exist in two assemblies, the one created by VS, and the one created by IIS / ASP.NET Development Server.

    So to fix the issue I renamed the folder to “App_Code_”

Coming up for part 5…

In part 5, I will implement grid persistance. Currently, if I am on page 3 of the grid sorted by phone number and I click on “Edit” next to one of the customers – when I save the customer and go back to grid – the grid returns to its default state of Page 1 and sorted by ID.

Grid persistence – will remember the state of the grid when user goes to another page, so that when user will come back to the page with the grid, it will be in the state that it was left in.

I will also add more JQuery functionality to format alternative rows, change row color on hover and provide “status line” notifications of user actions like created new customer, deleted customer, etc….

May 14, 2009

Web Sites vs Web Applications – build times

Filed under: General — Eric P @ 10:01 am

The enterprise-level application I am currently working on has several web projects built using “ASP.NET Web Site” model.
For a while now, I have been thinking of converting one of them to “Web Application” and see if it improves my compilation/build times. I took one of the smaller projects consisting of about 500 web pages and converted it to Web Application.

Results are as follows (using VS2008, cleaned solution before each build)

Web Site: 1 minute 20 seconds
Web Application: 2 secs

That’s a 60 times improvement. AMAZING.

There are definitely some drawbacks to switching to Web Application:
1. Not sure how to deal with namespaces. By default when you convert to “Web Application” – the code behind classes and classes in App_Code stay the same as before – no namespaces are added. But any new classes will have the namespaces. This type of inconsistency can be a bit confusing.
2. Namespaces introduce some naming conflicts, since some of my directory names match class name. For ex.
\UserControls\Order\UCViewOrder.ascx
The namespace will be “…UserControls.Order.UCViewOrder” which will conflict with my domain’s “Order” class.
Need to come up with some convention for this one.

These issues seem pretty small compared to SIXTY TIMES speed up in compilation time.

January 29, 2009

OOP vs Structured/Procedural programming (or DDD vs ADM)

Filed under: DDD, General — Eric P @ 12:26 pm

I think I may have succeeded in creating a headline with most Jargon evar… It is almost as bad as the name of my blog.

I have been having this dilemma. I have an enterprise level application written using OOP and ActiveRecord. It utilizes many OOP concepts such as Inheritance and Polymorphism. Recently I have been looking into making the system more friendly for Unit Testing. I talked about it to some developers working on the system and I had an interesting revelation: most of the developers prefer Structured/Procedural programming over OOP.

Here is the general overview of Procedural programming and OOP.
From http://www.umsl.edu/~subramaniana/concepts1.html

Procedural/Structured programming

  • Procedural Approach Data Structures can be represented as a network of associated structures, referring to one another.
  • Procedures can be represented as a network of routines which call one another, i.e., “call tree”

OOP

  • Object Oriented Approach Collection of discrete objects that incorporate data structures and behavior.
  • Each data structure has, combined with it, the procedures which apply to that data structure.
  • Contrasts with conventional programming in which data structures and behavior are only loosely connected
  • These entities, called objects, can be associated to one another in one network, rather than two.

DDD is based on OOP, while Anemic Domain Model is all about procedural programming.

Back to Dilemma. As much as Anemic Domain Model is an anti-pattern – it has one really nice thing going for it – it is simple and it makes sense to most developers and non-developers. How can Order ship itself, how can cake bake itself? Instead order and cake are just data and you have some OrderService and CakeService operating on them. With SOA even many business people can easily think in procedural terms.

My current system is written using OOP – objects have both data and actions. So I was hoping that switching to DDD would make OOP even more central to future development. The problem is that separating Active Record Entities into Repositories, Domain Entities and Application Services layer is confusing other developers on my team. Many of them come from procedural programming (aka Java EJB), so as soon as they see Entities and Services – then all logic goes into Services while Entities are just data structures.

Here is an example of what I have in my system:


Order
{
     OrderStatus _status;  (InProgress, Paid, Shipped)
     List _orderLines;

     Customer _customer;
     OrderBilling _billing; //Includes ShippingAddress and payment info
     OrderShipping _shipping;  

     DateCreated _dateCreated;

     AddOrderLine(product, quantity)
     RemoveOrderLine(orderLine)

     CalculateTotal();

     TakePayment();
     Ship();
}

Here is the same code written using ADM/Procedural:


Order
{
     OrderStatus _status;

     List _orderLines;
     Customer _customer;
     OrderBilling _billing;
     OrderShipping _shipping;  //Includes ShippingAddress and payment info
     DateCreated _dateCreated;
}

OrderService
{
     AddOrderLine(order, product, quantity)
     RemoveOrderLine(order, orderLine)

     CalculateTotal(order);

     TakePayment(order);
     Ship(order);
}

So what are the benefits of OOP vs ADM:
1. In OOP all the methods that operate on Entity are part of entity (or delegated to appropriate command/domain services by entity), while in Procedural – the code can be dispersed through many different services.

2. OOP is better for code re-use. Will need some examples for this one.

3. When refactoring, can use OOP Design Patterns like State Pattern for switching Order status.

But saying all this is not enough. There is only one way to make a convincing argument in Software Development:

SHOW ME THE CODE

In the upcoming blog entries, I will try to provide a real world coding example for each one of the points above…

January 28, 2009

One class per file or multiple classes in one file

Filed under: ASP.NET, General — Eric P @ 9:10 am

I browsed the internet for an answer to this question and the best I could find was:

“The primary benefit of a class per file is for source control and concurrent access: Why deal with the inevitable human error of code merges if each developer is really dealing with the separate classes.”

In my case there are several really small classes that seem like they can go into one file or be separated. As a personal style it seems best to put each class into separate file. This way I can easily see/find all the classes through solution explorer.

On the other hand it seems more like a stylistic argument, since I couldn’t find any evidence that it actually improves performance. I could also use VS diagramming tool to create a nice diagram for all the classes in a module.

So some possible guidelines for development team that can come out of this are:

  1. Allow including several classes in file only if each class is max of 10 lines of code.
  2. Classes in the same file must belong to the same Module (DDD Module)
  3. Enums should go in .cs file that uses them. If there are several classes that use enum – pick one.

Found another good reason for keeping classes in separate files:

“Also you’ll find many errors/diagnostics are reported via file name (“Error in Myclass.cpp, line 22″) and it helps if there’s a one-to-one correspondence between files and classes. “
From: http://stackoverflow.com/questions/28160/multiple-classes-in-a-header-file-vs-a-single-header-file-per-class

August 9, 2006

ENTechSolutions.com launched, XLib is available for download

Filed under: .NET, General, XLib Framework — Eric P @ 1:51 pm

I finally launched a web site for my consulting company:

http://www.entechsolutions.com

At the same time I packaged BETA versions of XLib and XWebSiteTemplate.  You can find these products as well as AutoSuggestBox control in the ‘Developer Corner’ section of the web site.

May 17, 2006

.NET Web Service For Email Labs

Filed under: .NET, General — Eric P @ 10:53 am

Recently, one of the clients of my consulting business asked for an integrated email solution. I did some research and found that EmailLabs would fit client needs. They are one of the few companies that provide an API to their email marketing solution. The only issue is that their API is very basic — it works by sending and receiving XML messages through HTTP.

To simplify things I wrote .NET web service that wraps around HTTP messages. It allows .NET developers to quickly tap in into list management, message building and sending functionality provided by EmailLabs. The web service is available at the following location:

http://samples.entechsolutions.com/EmailLabsWebService/Service.asmx

This web service encapsulates about 80% of functionality of EmailLabs API. For this release I concentrated on most useful functions. I didn’t implement functions used for managing Filters and adding/editing Demographics.

To demo the API you can contact EmailLabs to setup a demo account. For testing they let you send e-mails with up to 50 recipients. Or you can send me an e-mail I will give you access to my demo account.

Security Considerations

For API integration EmailLabs provides IP security. For every mail list you can specify a list of IP Addresses that can contact EmailLabs API. To test .NET web service you can provide security the following 2 ways:

A. Disable IP Security for the list

  1. Edit mailing list in EmailLabs admin
  2. Uncheck the ‘Limit API access to these addresses:’ check box
  3. Save

For this approach you may want to create a new test list, so security for other lists stays the same.
B. Add IP Address ‘66.129.79.80′ to secure IP addresses list

  1. Edit mailing list in EmailLabs admin
  2. Check the ‘Limit API access to these addresses:’ check box.
    Add IP addresses ‘66.129.79.80′ to comma delimited list of allowed IP addresses.
  3. Save

This IP address is dynamic, so it is possible that it may change. Let me know if you get authentication error using this approach.

FYI: If you create a new mailing list using Web Service API – it will automatically add appropriate IP address to the list of allowed IP addresses.

April 28, 2006

Editable GridView with Add New/Insert button

Filed under: .NET, General — Eric P @ 6:05 pm

April 21, 2006

Life cycle of Custom Control within ASP.NET page

Filed under: General — Eric P @ 11:18 am

There are a lot of articles about life-cycle of the page and custom controls. The problem is they are all looked at separately. I had to do a bit of research on the order of control events within the events of the web page and I would like to share my findings.

To see what happens I created two items:

  • TestCustomControl.cs — custom composite control that gets included on Default.aspx. Adds a line to the log for the following events/methods:

OnInit

LoadViewState

OnLoad

CreateChildControls

SaveViewState

OnPreRender

Render

  • Default.aspx page — contains TestCustomControl and submit button. Adds line to the log for the following events:

Page_Init

Page_Load

Page_PreRender

Then I opened Default.aspx page. Here is the log of events:

Control: OnInit
Page: Page_Init
Page: Page_Load
Control: OnLoad
Page: Page_PreRender
Control: CreateChildControls
Control: PreRender
Control: SaveViewState
Control: Render

Looks pretty much what you expected. Here is what happens when I clicked on submit button:

Control: OnInit
Page: Page_Init
Control: LoadViewState
Control: CreateChildControls
Control: LoadPostData
Page: Page_Load
Control: OnLoad
Page: Page_PreRender
Control: PreRender
Control: SaveViewState
Control: Render

There are a couple of differences. LoadViewState and LoadPostData are now included in the cycle. The biggest difference (that has caused many headaches) is that 'CreateChildControls' now gets called before "Page_Load/OnLoad". Let's say you have a simple composite control that contains a text box and some other controls:

public class TextBoxPlus : CompositeControl
{

private TextBox _txt;

#region Class Properties

public string TextBoxValue
{

get {return (_txt.Text);}
set {_txt.Text=value;}

}

#endregion

protected override void CreateChildControls()
{

_txt = new TextBox();
_txt.ID = "txtTemp ";
_txt.CssClass = this.CssClass;
_txt.Columns = 3;
_txt.MaxLength = 3;

Controls.Add(_txt);

}

protected override void Render(HtmlTextWriter output)
{

}

}

The issue with CreateChildControls is that if you set 'TextBoxValue' property of the control in 'Page_Load' event of the page – you will get an exception that '_txt' doesn't exist. It does work without any problems if you set that value after the first form submit.

So:

protected void Page_Load(object sender, EventArgs e)

{

if (!this.isPostBack)

textBoxPlus.Value="SomeValue"; //Throws exception

else

textBoxPlus.Value="SomeValue"; //Works without any problems

}

To fix this problem modify TextBoxValue property as follows

public string Value
{

get
{

EnsureChildControls();

return _txt.Text;

}

set
{

EnsureChildControls();

_txt.Text=value;

}

}

EnsureChildControls() checks if CreateChildControls() has been called and if it hasn't — calls it.

Blog at WordPress.com.