Showing posts with label .NET. Show all posts
Showing posts with label .NET. Show all posts

Monday, July 14, 2008

Form validation - part 2: the .NET validators

Since as we move forward, some of the team which does more of the front-end work, will start to delve more into contributing by doing some of the client-side validation, I thought it would be good to give an overview of the .NET validators. The first version of ASP.NET I worked with - 1.1 - had validator controls as part of the .NET framework. I bet they had them in the 1.0 version as well although I don't actually know for sure. Now the way these validators were supposed to work was like other .NET controls such as the datalist, or the repeater; you could just drag the control onto your page and hook it up. When the aspx made the round trip to the server and rendered in a browser, it would actually serve as client side validation of whatever field you hooked the validator control up to. The trouble was the 1.1 validators created javascript which only worked on IE. :P AND if javascript was disabled...no validation at all!

Furthermore, they were somewhat limited in what you could do with them. In the kind of forms we build, we frequently end up with skip patterns. "If you answered No to 12, go to 14" for example, meaning if you answered no to 12, 13 was not required; but if you answered yes to 12, then it WAS required. This was fairly simple javascript to create, but there existed no validator control that could handle it, except for the CustomValidator. And since we already had an extensive library of javascript functions covering most situations that cropped up, it was much simpler to just not use the validators and stick to the javascript.

In 2.0 however, Microsoft vastly improved the way these validator controls worked. Now they:

1. are cross browser compatible. WOW!
2. still work when javascript is disabled
3. have this neat option where you can tell them to ONLY turn on if the javascript is not enabled. COOL!

Our blog user known as tiger figured out how to work out #3 quite elegantly. Therefore #3 is the way we have been going, unless we don't have the hours to cover it. However as our code library goes, it is taking less and less time to even handle this task :)

Now I am going to show you a basic example. Let's say we have a form with one field with id 'greekGod' for "Favorite Greek god" and one field with id 'faveNumber' for "Favorite Number up to 20". Color is a textbox and Number is a dropdown starting with "Choose Number" for the zero index and going to 20.

Normally this might be your validation. Note that the following function actually calls 2 others, expecting them to return true or false individually. The following function is a handy way of checking numerous fields at once, and just returning false (in other words not letting the form submit) if ANY of them are invalid/have a problem:


function checkForm() <-- note it takes no arguments/parameters
{
if (
(isNotBlankAlpha(document.getElementById('greekGod'))) &&
(isSelected(document.getElementById('faveNumber'))
)
{
return true;
}

return false;
}


And this might be how you hook your form up to the javascript:


form onsubmit="return checkForm();"


If we were using the validator controls as well as the javascript, our javascript would be slightly altered:


function checkForm(source, clientside_arguments) <-- note it takes 2 arguments {
if (
(isNotBlankAlpha(document.getElementById('greekGod'))) &&
(isSelected(document.getElementById('faveNumber'))
)
{
clientside_arguments.IsValid = true;
}
clientside_arguments.IsValid = false;
}


And instead of hooking the form tag to the javascript, our aspx file would include a CustomValidator that might look like this:

asp:customvalidator id="CustomValidator1" runat="server" errormessage="" clientvalidationfunction="checkForm" onservervalidate="CustomValidator1_ServerValidate" display="Dynamic" setfocusonerror="True"


Note that it is the ClientValidationFunction property of the control that you use to tie it to the javascript. Note that you don't need the parens to call it either. However, notice that the javascript function itself now has 2 arguments. These are a mandatory signature; the source argument refers to the HTML tag that represents the validator control, and the clientside_arguments parameter refers to an object with 2 properties, IsValid and Value. The Value property is the value stored in the input control to be validated. The IsValid property must be set to false or true according to the result of the validation (Programming Microsoft ASP.NET 2.0 by Dino Esposito).

Note that the Value property is not relevant to the way we are using the CustomValidator - we are not using it to validate a single control, which is what the Value would be the value of - but instead we are validating a group of inputs all at the same time. Further, note that the javascript now takes 2 arguments - and it appears that these 2 arguments are in fact passed to the client function by the server, probably passed by the CustomValidator itself. Finally, the client function (javascript function), now that it is being tied to the CustomValidator, needs to set the IsValid property of the args parameters passed into it.

I hope this overview is somewhat helpful in explaining how to tie javascript to the ASP.NET CustomValidator control. Questions, comments, corrections are all welcome.

Friday, April 4, 2008

interacting with Active Directory


Recently I had to work on a web application that authenticated via Active Directory/IIS. In other words in IIS, anonymous access was turned off for my app, and Windows Authentication was turned on (image shows default setting for a web app; for this situation you need to uncheck anonymous access and check windows integration). When you do this, you then set at the folder level in Windows explorer who has access. It is at the folder level that it was indicated, in my case, to use Active Directory. For the current ePortfolio, on the other hand, users were added, account by account, to the folder via the Security tab.


Here are some articles on the topic:

Integrated Security with Web Application

Accessing the Active Directory from Microsoft .NET

Howto: (Almost) Everything In Active Directory via C#

Now I didn't really use the methods describes in these articles because they all demonstrate a way to interact with Active Directory that involves connecting directly to the Active Directory server. In my situation, I was not able to obtain the information needed to create the connection (LDAP server info). So below I will show you the code I did use.


string strADUser = Request.ServerVariables["LOGON_USER"].ToString().Substring(Request.ServerVariables["LOGON_USER"].ToString().LastIndexOf("\\") + 1);


Note that for ePortfolio Dave accessed the "HttpContext.Current.User.Identity.Name" property. I tried this first but it appears AD does not populate this property.

The last of my code:

Here I compare the SAMAccountName of the logged in user, to see if that person is a member of a specific AD group:


DirectorySearcher search = new DirectorySearcher();
search.Filter = String.Format("(SAMAccountName={0})", strADUser);
search.PropertiesToLoad.Add("memberOf");

SearchResult result = search.FindOne();
if (result != null)
{
int groupCount = result.Properties["memberOf"].Count;

for (int counter = 0; counter < groupCount; counter++)
{
string str = (string)result.Properties["memberOf"][counter];
if (str.Contains(editGroupName))
{
return true;
}
}
}


Any questions, suggestions for improvement? Post to the comments!

Tuesday, April 1, 2008

transaction

I am always looking through our code for a previous example of using transactions in .NET, so I am posting the code here. That's what here is for!



private void TRANSACTION EXAMPLE(int example)
{
using (SqlConnection conn = new SqlConnection(dsn))
{
conn.Open();
SqlCommand cmd = conn.CreateCommand();
SqlTransaction transaction;

transaction = conn.BeginTransaction("GiveItAName");
cmd.Connection = conn;
cmd.Transaction = transaction;
try
{
//1st query, initializes sql variable
string sql = "INSERT INTO someTable etcetc";
cmd.CommandText = sql;
//define needed parameters is any
cmd.ExecuteNonQuery();

//next query
sql = "UPDATE or WHATEVER QUERY etc etc";
cmd.CommandText = sql;

// define needed parameters
// NOTE you don't have to define any param if it was already defines and used for a previous query
cmd.Parameters.Add("@something", SqlDbType.VarChar, 50).Value = exaple;
cmd.ExecuteNonQuery();

//next query
sql = "UPDATE etc etc";
cmd.CommandText = sql;
cmd.ExecuteNonQuery();

// Attempt to commit the transaction.
transaction.Commit();
}
catch (Exception)
{
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
throw;
}
throw;
}
}
}

Thursday, February 14, 2008

sqlbulkcopy

I have just used sqlbulkcopy for the third or so time. This is a handy thing built into 2.0 that you should be familiar with. In the past I have used it in my code to copy a row into an "archive" table before deleting it from the initial table. Today I used it to copy data to a database housed at another school. I had a copy of their schema. I built a view of our data, such that the view held just what needed to go into their table. Here is my sample code:


string sql = "SELECT * FROM sourceView";
using (SqlConnection conn = new SqlConnection(dsn))//source dsn
using (SqlCommand cmd = new SqlCommand(sql, conn))
using (SqlBulkCopy copy = new SqlBulkCopy(dsn2))//destination & source dsn are the same
{
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
copy.DestinationTableName = "destinationTable";
//have to map the columns because the tables are not an exact match
copy.ColumnMappings.Add("fieldname", "samefieldButSlightlyDifferentName");
copy.ColumnMappings.Add("fieldname2", "samefieldButSlightlyDifferentName2");
copy.WriteToServer(rdr);
conn.Close();
}


A couple things to note. I copied code from the last time I did this. Reading my comment I'm not sure I actually needed to map out the columns, because from my view to the destination the schemas matched up. Last time I did this however, there were a few more columns in the destination table, hence my comment about the not matching up. Second, I got this error early on: SqlBulkCopy: the locale id '0' of the source column ' ' and the locale id '1033' of the destination column '' do not match. This was because the ID column in my table was an int and in theirs was varchar. I therefore modified my view for that and one other column so that the data types matched using CAST(field as varchar) AS field.