Thursday, May 28, 2009

LINQ – A Technology Overview

What is LINQ?

LINQ is short for Language Integrated Query. It is a Microsoft technology that allows programmers to query just about anything using SQL like syntax.

LINQ can be extended to support just about anything. For example, you could write something to allow you to query Flickr using LINQ. Microsoft has done an excellent job of making virtually all objects in C# 3.0 (or VB 9) queryable using LINQ. This means you can query lists, collection, objects, etc.

Why use LINQ?

Performance

Performance is extremely good with LINQ. The reason is that queries are optimized to only bring back the data you need. This is key for roundtrips to databases. In-memory queries are equally as optimized.

Strongly-Typed data

Strongly-typed data makes programming much easier and less error-prone. This also means that Intellisense works for all your objects. This gives the compiler a chance to help find issues with code changes.

No SQL Injection

LINQ provides protection from SQL Injection just a stored procedures in databases do. This is one of the major reasons I used stored procedures, and now I don’t have to.

Leverage Skills

Imagine being able to query anything using the same syntax and the same skill. This means you learn it once and use it everywhere. This is extremely useful and efficient. It also means that programmers can actually get away with not being well versed in SQL. Though, I think SQL and database knowledge is still very important. :) In terms of having to learn new API’s for services such as Flickr, Facebook, Amazon, etc, the learning curve is greatly reduced when using LINQ.

Future Standard

The general hope is that in 5 to 10 years all programming languages will have support to do queries as concept that is built into the language just like a for loop. Java appears to be moving in this direction with JPA (Java Persistance API) also. So, you might consider getting used to the idea and sharpening your skills early.

Less Code

When dealing with a database, you can have your entities that map to tables in your database automatically created for you. The mapping is editing using a visual designer in Visual Studio 2005 or later. The entity code is hidden from you, but you use partial classes to change the classes to suit your needs. This means much less code to maintain in your Data Access Layer. I still recommend a Data Access Layer so that you are not duplicating LINQ code and you are not tightly coupled to LINQ, but that is technically up to you.

Stored Procedure Support

Stored Procedures can still be used. This means if you prefer to use stored procedures you can. This is important in environments where databases are locked down and only allow access via stored procedures. The advantage is that you still don’t have to create or do much to maintain your entities.

List of LINQ Providers - What does LINQ work with?

The list is growing every day, but here is a good snapshot of what LINQ currently works with. Well, at least what I thought was pretty mainstream and useful. :) Each extension of LINQ has the naming conventions like LINQ to <thing> or LINQ over <thing> or <thing>LINQ. Most except the first few are created by third parties.

If you want to try to write your own, you might try this toolkit, though I have not tried it. Keep in mind, many times you don’t need a full blown provider. LINQ to Objects makes it possible to query most anything that is some sort of collection, list, etc.

If you want to see an example of how to actually use LINQ, I recommend my entry titled: LINQ – A brief overview

Thursday, May 21, 2009

Opening a SQL file in Visual Studio 2008 at the click of a button

I love the SQL interface that is built into Visual Studio 2008. It has much of the key functionality that Microsoft SQL Server Management Studio has.

The problem is that it is not a docked window. It is a regular window just like any other window in Visual Studio 2008. This makes it difficult to find it in my often long list of Visual Studio windows that are open.

What I wanted was a way to see my SQL file at the click of the button. Then I thought, why not have it open for me if it is not one of the windows that is already open in Visual Studio 2008.

Macros (aka Visual Basic for Applications (VBA)) is a natural way to add this functionality. I explain where Macros are hiding in Visual Studio 2008 and how to record a macro in another one of my blog entries.

Here is the code snippet you can use to do what I describe above. Obviously, you will need to change your paths to suit your situation.

Sub GotoSQLWindow()
        Try
            DTE.Windows.Item("(local).MyDB - dev.sql").Activate()
        Catch ex As Exception
            Try
                DTE.ItemOperations.OpenFile("C:\\dev.sql")
                DTE.Windows.Item("dev.sql").Activate()
                DTE.ExecuteCommand("Data.Connect")
            Catch ex2 As Exception
                Throw ex2
            End Try
        End Try
    End Sub

I recommend if you have trouble getting the names correctly, you either look at the tabs and put that exact text as the name in the Item collection, or you record it using Control-Shift-R.

Extending Visual Studio using Macros (VBA)

Like most every Microsoft Office product, Visual Studio (all versions including 2008) have support for using Macros to end the IDE. I had trouble finding the docs for where to access Macros in Visual Studio 2008. It is there, but at least for me it was hidden. Here is where you can find it (once you are in Visual Studio 2008)

Macro Explorer - View menu | Other Windows | Macro Explorer

The Macro menu is no longer in the Tools menu. However, you can add it there or anywhere else (such as a new menu or toolbar) you like by going to Tools menu | Customize... From here you will see a list of categories. Choose Tools from the list. The list of Commands to the right has many commands in it. The ones that you may want to grab are the following:

  • Cancel Recording
  • Edit Macro
  • Load Macro Project
  • Macro Explorer (same as the one in the View menu)
  • Macros IDE…
  • New Macro Command
  • New Macro Project
  • Pause/Resume Recording
  • Record Temporary Macro
  • Run Macro
  • Run Temporary Macro
  • Save Temporary Macro
  • Set Recording Project
  • Start Recording Temporary Macro
  • Stop Recording Temporary Macro
  • Unload Macro Project

Simply drag and drop any of the above items to a toolbar or menu. It is really pretty cool. When the customize window is open you can right-click and modify just about any menu item or toolbar item.

If you don’t want to mess with all that you can just type Control-Shift-R to start recording a temporary macro. This will cause the Macro toolbar to show. You can then use it to cancel, pause, and stop recording.

You can use the Macro Explorer to see the Temporary Macro under RecordingModule. You can Run, Edit, or Rename, or Delete the macro by right-clicking it. If you want to not have it rewritten the next time you record a temporary macro, you will need to rename it.

Once you have the Macro you like, you can add it to a toolbar (you can create a new one if you prefer) by going to Tools menu | Customize… | Macros category and then dragging your Macro from the Customize window to a toolbar.

Here is a sample macro that I recorded and then tweaked to go to the already open SQL file or open it first if it is not already open. See here for details.

References: How to Record Macros in Visual Studio 2005 or How to Record Macros in Visual Studio 2008

Wednesday, May 20, 2009

Monitoring Commit Charge in Windows

I noticed that when the Commit Charge gets to be near or above my Physical memory (RAM) that my system starts getting slower. I wanted a simple way to monitor this and be alerted when a specified threshold is exceeded.

If you want to see what Commit Charge is, just open up Windows Task Manager and look at the status bar at the bottom of the Window. This is the value we want.

As it turns out WMI makes it extremely easy to do this. Below is a snippet of C# code that brings up an alert when Commit Charge > threshold.

public static void CheckCommitCharge()
{
System.Diagnostics.PerformanceCounter perfCounter = new System.Diagnostics.PerformanceCounter("Memory", "Committed Bytes");
long commitChargeInMB = perfCounter.RawValue / 1024 / 1024;

long threshold = 1900;
if (commitChargeInMB > threshold)
{
MessageBox.Show("Your system may become sluggish if you don't close some applications. \r\nThe Commit Charge is " + commitChargeInMB + " MB, but it should be below " + threshold + ".");
}}
It is actually quite easy to use this snippet. Follow the high level steps below to use it.
  1. Create a Win Forms application in Visual Studio.
  2. Open Program.cs and comment out the Application.Run(new Form1()); line, and put CheckCommitCharge(); in its place.
  3. Build the executable and run from a Windows scheduled task. Schedule the task to your comfort level. Remember, it could get annoying if you don’t quit some apps and you are reminded every 10 seconds or something. :)

Friday, May 15, 2009

ASP.NET FormView won’t stay in Edit mode after an update (by default)

The ASP.NET FormView has default behavior such that after you make it update, it changes the mode to ReadOnly. I want to change this behavior so that it stays in edit mode after an update command has been issued.

I don’t consider it very intuitive, but I did find the answer. All you have to  is use the ItemUpdated of the FormView.

protected void formview1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
e.KeepInEditMode = true;
}
Likewise, there is the same scenario for Insert.
protected void formview1_ItemInserted(object sender, FormViewInsertedEventArgs e)
{
e.KeepInInsertMode = true;
}

Thursday, May 14, 2009

ADO.NET Entity Data Model Designer doesn’t open model when double-clicking

I understand that the ADO.NET Entity Data Model Designer is still in its infancy, but I have to wonder how this bug was missed. If you open a model in the ADO.NET Entity Data Model Designer by double-click on it or right-clicking and choosing Open the model opens fine. Close it and try the same thing again. The model will not open.

Luckily the workaround is not too much of a pain. Do the following to get the designer to open the model again.

  1. Close the model.
  2. Right-click on the Model.edmx (or similarly named file) in the Solution Explorer in Visual Studio 2008.
  3. Choose Open With…
  4. In the windows that comes up, choose XML Editor and click the OK button. Actually you can also use the Source Code (Text) Editor. You really just need an editor that can read xml, so most of them will work. The key is to open it in something other than the ADO.NET Entity Data Model Designer.
  5. Close Model.
  6. Double-click on the Model.edmx (or similarly named file) to open it as you usually would.

Unfortunately, you will need to do this every time you close the model and then want to re-open it. It is better than the other workaround of restarting Visual Studio 2008.

ADO.NET Entity Data Model Designer missing delete functionality

I understand that the ADO.NET Entity Data Model Designer is still in its infancy, but to not delete an object all the way is a BIG missing feature in my opinion.

If you delete an object as I describe below, you may get the following error:

Error    1    Error 3013: Problem in Mapping Fragment starting at line 241: Missing table mapping: Foreign key constraint 'FK_TestTable1_MyTable' from table TestTable1 (MyTableID) to table MyTable(ID): no mapping specified for the table TestTable1.
    C:\MyWebSite\DataModel\Model.edmx    242    15    DataModel

For simplicity in explanation, let’s assume you have two tables in your model. They are called TestTable1 and MyTable. There is a foreign key / association inTestTable1 that points to MyTable. There is also a reverse association from MyTable to TestTable1.

I decided that I didn’t want TestTable1 in the model anymore. I select the object and delete it. That only deletes it from the viewable portion of the model. It is still in the underlying xml.

You can try to update the model from the database, but it doesn’t show TestTable1 as a table that you can add again. This is because it is still defined in the underlying xml.

Luckily the workaround is pretty easy and painless, but not necessarily obvious at first glance. Do the following to remove the references to TestTable1 and fix the problem.

  1. Right-click on the Model.edmx (or similarly named file) in the Solution Explorer in Visual Studio 2008.
  2. Choose Open With…
  3. In the windows that comes up, choose XML Editor and click the OK button.
  4. If you are prompted to close the model because it is already open, click the Yes button.
  5. Now, search for your table name. In my case TestTable1. Delete all tags (and inner-Xml) that you find.
  6. Save your changes.
  7. Right-click on the Model.edmx (or similarly named file) in the Solution Explorer in Visual Studio 2008.
  8. Choose Open With…
  9. In the windows that comes up, choose ADO.NET Entity Data Model Designer and click the OK button.
  10. Rebuild your solution. That should take care of the error.

Another workaround is to actually delete the table from your database. This may or may not be what you want to do. If you do indeed want to delete it from the database also, then you are in luck. You can update the underlying xml by updating the model from the database (right-click on the designer surface).

If you want to see a read-only visual representation of the underlying xml look for a Model Browser tab while you are in the ADO.NET Entity Data Model Designer. Mine is right next to my Solution Explorer on the right side of my screen. If it isn’t there check the different dockable areas. The Model Browser only shows when you are in the ADO.NET Entity Data Model Designer, so be sure that is your active window. It would be nice if you could edit through the Model Browser. Oh well, your choices for now are direct xml editing or modifying your database and synching it.

Wednesday, May 13, 2009

Making Linq to Entities do a case-insensitive string comparison.

Linq to Entities by default is case-sensitive for string comparisons. Even for SQL Server, which is case insensitive string comparisons are case-sensitive.

Below are two examples. The first example shows a case-insensitive example, and the second one, shows a case-sensitive example.

// This is NOT case-sensitive
using (MyModel ctx = new MyModel())
{
Reviewer reviewer = ctx.Reviewer.First<Reviewer>(r => r.FirstName.Equals("Brent", StringComparison.CurrentCultureIgnoreCase));
}

// This IS case-sensitive
using (MyModel ctx = new MyModel())
{
Reviewer reviewer = ctx.Reviewer.First<Reviewer>(r => r.FirstName == "Brent");
}

BTW, don’t use the .toLower() method as this translates into a similar call in SQL which then will usually cause your indexes to not be used.

Impersonating a user in ASP.NET when using Windows Authentication

I frequently have the need to test, debug, troubleshoot, etc my ASP.NET intranet applications that use Windows Authentication as another user. For example, user1 says, the application is behaving strangely when I do xyz. It is very helpful to be able to see exactly what that user is seeing. Of course I could ask the user for their password, but that is a bad solution, and a bad idea to ever give passwords out, even to IT.

What I needed was a way to just specify the username I wanted, and not worry about the password. I want this functionality to be restricted to user in a certain role. For example, maybe an Admin role. I didn’t want any real performance impact either.

With that in mind, I set out to write a class to encapsulate all this logic. Below is the solution I came up with. It requires very little changes to your existing application. It hooks into the application at the Application_AuthenticationRequest event in your Global.asax. There are a couple of items that can be configured in the constructor or the web.config or use my defaults. You can control the impersonation through the url query string, or you can come up with your own user interface that calls my methods. See the example comment in the class for more details. The code is heavily commented and I also have examples of how to use the code as well. There really isn’t that much code once you take away the example code and comments.

Anyway, here is the code. Just paste the code into .cs file, and you should be ready to go.

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Xml.Linq;
using System.Web.Routing;
using System.Web.DynamicData;
using DataModel;
using ICAWebSite3;
using System.Security.Principal;

namespace MyWebSite
{
   /// <summary>
   /// Impersonates an specified user. Impersonation is started by adding
   /// ImpersonatedUser=domain\username to the url or calling Impersonate() 
   /// and UnImpersonate methods from code. 
   /// </summary>
   /// <example>
   ///     In Global.asax and using url to control impersonation
   ///     void Application_AuthenticateRequest(object sender, EventArgs e)
   ///     {
   ///        Impersonation i = new Impersonation();
   ///        i.ImpersonateBasedOnQueryStrings("ImpersonatedUser", "UnImpersonate");
   ///        i.HandleAuthenticationRequest();
   ///     }
   ///    
   /// or 
   /// 
   ///     In Global.asax and using web interface to control impersonation
   ///     void Application_AuthenticateRequest(object sender, EventArgs e)
   ///     {
   ///        Impersonation i = new Impersonation();
   ///        i.HandleAuthenticationRequest();
   ///     }
   ///     
   ///     This example assumes you call the Impersonate() and UnImpersonate()
   ///     methods in your code appropriately. Be sure to use the same parameters to the 
   ///     constructor as you do in Global.asax. The web.config option shown below is recommended.
   ///     
   ///     Impersonation i = new Impersonation();
   ///     i.Impersonate(@"domain\someoneelse")
   ///     
   ///     You will also need to call this somewhere else also.
   ///     Impersonation i = new Impersonation();
   ///     i.UnImpersonate();
   ///     
   ///     NOTE: You may need to redirect after you call i.Impersonate(...) because the 
   ///     button event handlers, etc are after the Application_AuthenticateRequest event.
   ///     
   ///     Below is an example of how you may want to show/hide a textbox and two buttons
   ///     on a page or master page. The textbox is for the domain\username you want
   ///     to impersonate and the two buttons are Impersonate and UnImpersonate.
   ///     
   ///     protected void Page_Load(object sender, EventArgs e)
   ///     {
   ///         Impersonation i = new Impersonation();
   ///         btnUnImpersonate.Visible = i.IsImpersonating;
   ///         if (i.CanImpersonate)
   ///         {
   ///             btnImpersonate.Visible = true;
   ///             txtImpersonatedUser.Visible = true; 
   ///         }
   ///         else
   ///         {
   ///             btnImpersonate.Visible = false;
   ///             txtImpersonatedUser.Visible = false;
   ///         }
   ///         if (!IsPostBack)
   ///         {
   ///             txtImpersonatedUser.Text = i.ImpersonatedUsername;
   ///         }
   ///     }
   ///
   ///     protected void btnImpersonate_Click(object sender, EventArgs e)
   ///     {
   ///         Impersonation i = new Impersonation();
   ///         i.Impersonate(txtImpersonatedUser.Text);
   ///         Response.Redirect(Request.Url.OriginalString);
   ///     }
   ///
   ///     protected void btnUnImpersonate_Click(object sender, EventArgs e)
   ///     {
   ///         Impersonation i = new Impersonation();
   ///         i.UnImpersonate();
   ///         txtImpersonatedUser.Text = "";
   ///     }
   /// </example>
   /// <remarks>
   ///     The impersonation data is being persisted to the Application object currently.
   ///     This is very fast and has no real impact on performance of the application. 
   ///     It is important that performance be very quick because the Application_AuthenticateRequest
   ///     event fires for every request. This includes image and javascript files, etc. 
   ///     
   ///     The Application object works very well for single web server environments.
   ///     However in web farms where a user may be redirected to multiple servers, 
   ///     the impersonation would not work anymore unless the user happens to hit the same
   ///     server each time. 
   ///     
   ///     In environments like this, I recommend using a database or something
   ///     of the like since it can be accessed from all application instances. There will of course
   ///     be a performance hit for this, so use sparingly. You may want to experiment with
   ///     caching credentials, or limiting the impersonation to .aspx pages only. These
   ///     approaches have their own issues that you will need to content with as well.
   ///     
   ///     If you choose to change the place where impersonation state is kept you just need to change
   ///     the ImpersonationDateStore object. This is by design.
   ///     
   /// </remarks>
   public class Impersonation
   {
       /// <summary>
       /// The role that the user must be in in order for impersonation to work.
       /// For example, Admin.
       /// </summary>
       private string requiredRoleName;

       /// <summary>
       /// The time before an impersonation expires. This is a sliding expiration.
       /// This means that the time is relative to the last time the impersonation
       /// actually is set.
       /// This is Not the absoute time since impersonation was started, UNLESS
       /// the impersonation is only set once such as through the UI.
       /// </summary>
       private TimeSpan expirationDuration;

       /// <summary>
       /// Class responsible for persisting the impersonation state
       /// </summary>
       private ImpersonationDataStore ds;


       // Assuming there is some traffic on the site, any expired impersonations 
       // will be removed
       public Impersonation()
       {
           // if the web.config does not have a default value specified,
           // then use our own default.
           string configFileExpirationDuration = ConfigurationManager.AppSettings["Impersonation-ExpirationDuration"];
           if (!string.IsNullOrEmpty(configFileExpirationDuration))
           {

               // string in web.config should have the format:
               // [ws][-]{ d | d.hh:mm[:ss[.ff]] | hh:mm[:ss[.ff]] }[ws]
               // for more info see http://msdn.microsoft.com/en-us/library/system.timespan.tryparse.aspx 
               TimeSpan.TryParse(configFileExpirationDuration, out this.expirationDuration);
           }
           else
           {
               // default time to 20 minutes if one is not specified
               this.expirationDuration = new TimeSpan(0, 20, 0);
           }


           // if the web.config does not have a default value specified,
           // then use our own default.
           string configFileRequiredRoleName = ConfigurationManager.AppSettings["Impersonation-RequiredRoleName"];
           if (!string.IsNullOrEmpty(configFileRequiredRoleName))
           {

               this.requiredRoleName = configFileRequiredRoleName;
           }
           else
           {
               // default role to Admin
               this.requiredRoleName = "Admin";
           }

           Init();
       }

       // Assuming there is some traffic on the site, any expired impersonations 
       // will be removed
       public Impersonation(string requiredRoleName, TimeSpan expirationDuration)
       {
           this.expirationDuration = expirationDuration;
           this.requiredRoleName = requiredRoleName;

           Init();
       }

       /// <summary>
       /// Stuff that is done for all the constructors
       /// </summary>
       private void Init()
       {
           ds = new ImpersonationDataStore();

           ds.ClearExpiredImpersonations();
       }

     
  
       public void Impersonate(string impersonatedUsername)
       {
           if (Roles.IsUserInRole(requiredRoleName))
           {
               ImpersonatedUsernameInternal = impersonatedUsername;
           }
       }

      

       /// <summary>
       /// This must be called from the Application_AuthenticateRequest event in Global.asax Parameters are taken from the
       /// query string of the url. The url does NOT have to have either of the parameters
       /// present after the initial request with one of them in the url. Even if the 
       /// query parameters are not in the url the impersonation will exist until it expires 
       /// or is explicitly UnImpersonated by adding UnImpersonate=Y to the url or the UnImpersonate() method.
       /// </summary>
       /// <param name="impersonateParamName">The name of the query string key name. 
       /// In the url, the value associated with the key should be of the format
       /// domain\username</param>
       /// <param name="unimpersonateParamName">The name of the query string key name.
       /// In the url, the value associated with this can be anything. All that matters
       /// is that a value is specified so that the key will be passed from browser to the
       /// ASP.NET QueryString Dictionary.
       /// </param>
       public void ImpersonateBasedOnQueryStrings(string impersonateParamName, string unimpersonateParamName)
       {
           if (Roles.IsUserInRole(requiredRoleName))
           {
              
               string impersonateUserVal = HttpContext.Current.Request.QueryString[impersonateParamName] as string;
               bool unImpersonateUser = HttpContext.Current.Request.QueryString.AllKeys.Contains<string>(unimpersonateParamName);
              
               // if we need to unimpersonate then do so before we call Impersonate()
               if (unImpersonateUser)
               {
                   UnImpersonate(AuthenticatedUsername);
               }

               // if there is a new or same impersonation value, 
               // then set it to the current impersonation value
               if (!string.IsNullOrEmpty(impersonateUserVal))
               {
                   ImpersonatedUsernameInternal = impersonateUserVal;
               }
           }
       }

       /// <summary>
       /// This must be called from the Application_AuthenticateRequest event in Global.asax
       /// </summary>
       /// <param name="impersonateUserVal">The domain and username of the user that 
       /// is to be impersonated. The value should be of the format domain\username
       /// </param>
       public void HandleAuthenticationRequest()
       {
           if (Roles.IsUserInRole(requiredRoleName))
           {
               ImpersonationInfo info = ds.Retrieve(AuthenticatedUsername);
               if (info != null)
               {
                   string impersonatedUsername = info.ImpersonatedUser;
                   if (!string.IsNullOrEmpty(impersonatedUsername))
                   {
                       GenericIdentity id = new GenericIdentity(impersonatedUsername);
                       GenericPrincipal p = new GenericPrincipal(id, Roles.GetRolesForUser(impersonatedUsername));
                       HttpContext.Current.User = p;
                   }
               }
              
              
           }
      
       }

       /// <summary>
       /// Returns true if someone is being impersonated, else, false.
       /// </summary>
       public bool IsImpersonating
       {
           get { return (ImpersonatedUsername != null) && (ImpersonatedUsername != AuthenticatedUsername); }
       }

       /// <summary>
       /// True if the Authenticated user is in the role required to impersonate other users,
       /// else false
       /// </summary>
       public bool CanImpersonate
       {
           get { return Roles.IsUserInRole(AuthenticatedUsername, requiredRoleName); }
       }



       /// <summary>
       /// Call this method to unimpersonate
       /// </summary>
       public void UnImpersonate()
       {
           UnImpersonate(AuthenticatedUsername);
       }

       /// <summary>
       /// Call this method to unimpersonate
       /// </summary>
       /// <param name="authenticatedUsername"></param>
       private void UnImpersonate(string authenticatedUsername)
       {
           ds.Delete(authenticatedUsername);
       }

       public string AuthenticatedUsername
       {
           get
           {
               return HttpContext.Current.Request.ServerVariables["LOGON_USER"];
           }
       }

       public string ImpersonatedUsername
       {
           get
           {
               return ImpersonatedUsernameInternal;
           }
       }

       private class ImpersonationDataStore
       {
           public void Store(string key, ImpersonationInfo value)
           {
               HttpContext.Current.Application[GetImpersonationKey(key)] = value;
           }

           public ImpersonationInfo Retrieve(string key)
           {
               return HttpContext.Current.Application[GetImpersonationKey(key)] as ImpersonationInfo;
           }

           public void Delete(string key)
           {
               HttpContext.Current.Application.Remove(GetImpersonationKey(key));
           }

           private string GetImpersonationKey(string key)
           {
               return "Impersonation-" + key;
           }

           /// <summary>
           /// Removes any impersonations that have expired.
           /// </summary>
           public void ClearExpiredImpersonations()
           {
               string[] keys = HttpContext.Current.Application.AllKeys;
               string keyPrefix = GetImpersonationKey("");
               for (int i = 0; i < keys.Count(); i++)
               {
                   if (keys[i].StartsWith(keyPrefix))
                   {
                       object infoObj = HttpContext.Current.Application[keys[i]];

                       if (infoObj is ImpersonationInfo)
                       {
                           ImpersonationInfo info = infoObj as ImpersonationInfo;
                           DateTime expires = info.Expires;
                           if (expires < DateTime.Now)
                           {
                               Delete(info.AuthenticatedUsername);
                           }
                       }
                   }
               }
           }

       }

       private string ImpersonatedUsernameInternal
       {
           get
           {

               ImpersonationInfo info = ds.Retrieve(AuthenticatedUsername);
               if (info == null) return null;
               else return info.ImpersonatedUser;
           }

           set
           {
               ImpersonationInfo info = new ImpersonationInfo();
               info.AuthenticatedUsername = AuthenticatedUsername;
               info.ImpersonatedUser = value;

               // slide the expiration window out from the current date time.
               info.Expires = DateTime.Now.Add(expirationDuration);

               ds.Store(AuthenticatedUsername, info);
              
           }
       }

      

       /// <summary>
       /// Stores information about the impersonation.
       /// This could be persisted to a database or file.
       /// </summary>
       public class ImpersonationInfo
       {
           public string AuthenticatedUsername { get; set; }
           public DateTime Expires { get; set; }
           public string ImpersonatedUser { get; set; }
       }

      
      
   }
}

Tuesday, May 12, 2009

How to get FireFox to wrap PRE tag text

For some reason FireFox does not wrap code or other text (with no good word-breaking point) that is in a PRE tag. I noticed the problem when I was trying to stop a Blogger template from truncating (cutting of) the end of lines (that were too long). I want FireFox to wrap the long lines just like Internet Explorer. As it turns out, it is quite easy to fix. Just add the following to your CSS styles.

pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}

Monday, May 11, 2009

Using SQL Server to view all alerts in SharePoint

Here is a query to see all the alerts that are in SharePoint. This query works well in Microsoft Office SharePoint Server 2007 (MOSS 2007) and Windows SharePoint Services (WSS 3.0). If you use it for Windows SharePoint Services 2.0 or SharePoint Portal Server 2003, you will need to remove the alertTitle column since it does not exist in the older database schema.

select * from 
(-- Immediate
select 'Instant' as NotifyFreq, alertTitle, listTitle, tp_email, tp_login, s.siteurl + weburl + '/_layouts/sitesubs.aspx' as url from dbo.ImmedSubscriptions s join userinfo u on (s.userid = u.tp_id)
union
-- Scheduled 
select case NotifyFreq when 1 then 'Daily' when 2 then 'Weekly' else 'Other' end , alertTitle, listTitle, tp_email, tp_login, s.siteurl + weburl + '/_layouts/sitesubs.aspx' as url from dbo.SchedSubscriptions s join userinfo u on (s.userid = u.tp_id) 
) t

The first column indicates the type of alert. The second column is what you see on the admin page for alerts. The url and tp_login columns are very useful if you want to delete a particular alert.

The url is actual the url to the alert edit page in SharePoint. You can remove ‘/_layouts/sitesubs.aspx’ from the url if you want to see the url to the list that the alert is associated with. Once you are one the alerts admin page, you can will need the tp_login to find the alert is the drop down list.

Friday, May 8, 2009

Using Wireshark to sniff Active Directory (LDAP) traffic

Wireshark is a very power network sniffing tool. It used to be known as Ethereal (long story). It supports a ton of protocols, including LDAP. Active Directory is the Microsoft implementation of LDAP. You can download Wireshark here for most any platform. WinPcap is included in the install now, which is nice and saves some effort during installation. Once you have it installed, open it up and do the following to sniff your ldap traffic.

  1. This step is optional, but I recommend it if you are interested in the actual dns names of the devices instead of just the ip addresses. Go to Edit menu | Preferences… | Name Resolution  and then make sure the Enable network name resolution is checked. This will slow the output, but it provides much easier to read data.
  2. Go to Capture menu | Interfaces… Click the Start button on the network interface(s) you want to sniff.
  3. Immediately you should start see traffic in the window. This is all traffic.
  4. Filter the output to ldap only by typing in the Filter textbox (in the toolbar), and then hitting enter or clicking the Apply button. Unless you have some ldap traffic, you will see the window go blank. Do something that you know will perform a ldap query, then you should see the traffic in the window.

    NOTE: The ldap filter is just that a filter, all network traffic is still logged, you just don’t see it. You can use the Clear button to remove the filter.
  5. Go to the Capture menu | Stop to stop capturing traffic.

Example

This example uses the Active Directory Users and Computers application as a source for generating ldap traffic. We will use the tool to query for a user and then find the query and results in the traffic.

  1. Follow the above instructions to get Wireshark sniffing ldap traffic.
  2. Go to Start menu | Administrative Tool | Active Directory Users and Computers
  3. Select your domain in the left panel.
  4. Choose Action menu | Find…
  5. Type in something that has more than one result, but not too many. We are looking for something that has a small bit of sample data to look at. Maybe 3 or so results. You can type in particular names or usernames.
  6. Go back to Wireshark and stop the traffic.
  7. Now we can do some analysis of the data. Go to Edit menu | Find Packet…
  8. Click the String radio button.
  9. Click the Packet bytes radio button (you can also try the others, they typically work well also).
  10. Type in the same text you typed into the Active Directory Users and Computers application.
  11. Click the Find button. If you don’t find any results it may be because you have some bad reassembled packet or bad checksum. If that happens it is beyond me. Also, sometimes, the Find does work for me. I can’t explain it either. Sorry, I am programmer, not a network guy, so I am not of much help explaining what might be wrong, etc.

    However, I have had pretty good luck just looking at the traffic and figuring out what is going on. See the section below.

Understanding LDAP traffic

When you look at the Info column, you will see several different types data.

bindRequest()

This to be authentication request from us to the ldap server

bindResponse()

This is the response from the ldap server (domain controller) in response to our bindRequest().

SASL GSS-API Integrity: searchRequest()

This is a request to the ldap server. If you look in the middle pane of Wireshark, you will see a collapsed section called Lightweight-Directory-Access-Protocol | SASL Buffer | GSS-API payload | LDAPMessage searchRequest(). Here you will see specifics of what you are requesting from the ldap server. In particular, you can drill down to protocolOp: searchRequest(). Here you can see the Filter (this is the actual ldap query sent) that was used. You can also see the attributes that were requested. You can also see the baseObject here.

In terms of SQL, this is basically like the select statement.
Filter = SQL where clause
attributes = SQL list of columns to select
baseObject = roughly translates to a SQL table, but more just specifying at what level in the ldap tree the search should take place.

SASL GSS-API Integrity: searchResEntry()

This is the response to the searchRequest() request from the ldap server. If you look in the middle pane of Wireshark, you will see a collapsed section (unless you already opened it :) called Lightweight-Directory-Access-Protocol | SASL Buffer | GSS-API payload | LDAPMessage searchResEntry(). Each LDAPMessage searchResEntry() is a result from the ldap server. This is essentially a row or record. So, if the query found 7 matches, then you should have 7 LDAPMessage searchResEntry() here. You can expand each LDAPMessage searchResEntry() to see the exact objectName, and the values.

Here you will see specifics of what the response from the ldap server. In particular, you can drill down to protocolOp: searchRequest(). Here you can see the Filter (this is the actual ldap query sent) that was used. You can also see the attributes that were requested. You can also see the baseObject here.

SASL GSS-API Integrity: unbindRequest()

This is the disconnect from the ldap server. The clean up. The thing that tells the ldap server that we are done with it.

Thursday, May 7, 2009

Using ASP.NET Membership Roles (SQL table based roles) with Windows Authentication (Integrated Security)

I found this great blog posting by Scott Guthrie regarding how to use the ASP.NET Membership Roles with Windows Authentication. In this scenario users don’t have to login because Windows Authentication (I knew it as Integrated Security, but I don’t know where I got that name :). This works well with Internet Explorer and Intranet environments. The scenario also dictates that the roles are defined in SQL Server tables instead of Active Directory. Beyond that, Scott’s blog posting (noted above) explains the scenario very well.

Scott’s example goes into more functionality than I will here. However, I do cover how to create the supporting SQL tables and stored procedures and he doesn’t, but he does link to a page that does. He covers how to due Security Trimming of menus, admin interface, automatically creating the roles, etc. If you need details on those things please check ScottGu’s Blog: Implementing Role Based Security with ASP.NET using Windows Authentication and SQL Server. I have to give nearly all credit for the source of all my knowledge to Scott’s blog posting. NOTE: Scott also includes a link to download a working website project.

I want to give the basic steps only. I assume you have a website that just has a default web.config and thus default security.

Step 1: Enable Windows Authentication

Add the authentication and authorization tags to your web.config under the system.web tag. This will require all users to be authenticated. Be sure to enable Windows Integrated Authentication in IIS.

<system.web>
<authentication mode="Windows"/> <authorization> <deny users="?"/> </authorization>

Step 2: Configure for SQL Server

By default Visual Studio wants to use SQL Express instead of SQL Server. Really, the only thing that Visual Studio needs is to know where the ASP.NET Membership provider database is located. To tell Visual Studio where to connect to SQL Server, add the following (modify for your database location, etc) to your web.config.

<connectionStrings>
    <remove name="LocalSqlServer"/>
    <add name="LocalSqlServer" connectionString="Data Source=(local);Initial Catalog=MyDB;Integrated Security=True" providerName="System.Data.SqlClient"/>
    

Please note, the name LocalSqlServer is required, do not change this.

Step 3: Create supporting tables in SQL Server database

The roles membership table and stored procedures requires tables to be created in a database. This can be in your application database, or it can be in a separate database. There may be an easier way to create these tables and stored procedures, but this works well, but it is a bit of an obscure location for the .exe.

If you are using .NET 2.0 or greater (yes v3.0 and v3.5 and v3.5.1 also) the .exe you need to execute is located in a path similar to the following:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regsql.exe
Unless you want to generate support for all the other Application Services (not necessary for this scenario) I recommend using the following command line arguments. Change the server and database name to suit your environment.
aspnet_regsql.exe -E -S myServer -d MyDB -A m -A r
You can also run the aspnet_regsql.exe without parameters and a wizard will run. The wizard does NOT allow you to only install certain features, it installs all the features. Use the command line with args or use the less flexible, but prettier wizard. Your choice.

Step 4: Enable SQL based Role Management

Replace this line in your web.config

<roleManager enabled="true" />
with these lines.

<roleManager enabled="true" defaultProvider="SqlRoleManager">
<providers>
<clear/>
<add name="SqlRoleManager" type="System.Web.Security.SqlRoleProvider" connectionStringName="LocalSqlServer"/>
</providers>
</roleManager>
NOTE: In this example the membership database and my application database are the same. They don’t have to be though.
NOTE: If you are using a virtual directory for development, I would recommend adding the applicationName attribute to the SqlRoleManger tag. It doesn’t appear to be a problem with file based Web Site projects in Visual Studio 2008, but it appears that it is with IIS 5 and using virtual directories. To better understand the issue, see here.
 
Step 5: Add the roles
You can do this via code or using the configuration ui.
To use the UI, run the Project | ASP.NET Configuration tool. Click the Security tab, and click the Create or Manage roles link. Add any number of roles you want.
To create the roles (maybe on application start in the Global.asax.cs file)
void Application_Start(object sender, EventArgs e)
{
if (!Roles.RoleExists("Admin"))
{
Roles.CreateRole("Admin");

}
}
Step 6: Add users to the roles
The ASP.NET Configuration tool doesn’t work for adding users when using Windows Integrated Authentication mode. So, you have to do this via code. The following block of code checks to see if the user is in a role. If the user is not in the role, the user is then added to that role.
if (!Roles.IsUserInRole(userName, roleName))
{
Roles.AddUserToRole(userName, roleName);
}
You may want to create a custom user interface to add users to roles. Scott Gutherie provides an example of it in VB.NET in his downloadable working demo. Below is a virtual copy of the admin page that is in the project, but mine is in C#, not VB.NET.
Here is the code-behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Security;

namespace MyApp
{
public partial class UserAdmin : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void PopulateRoleList(string username)
{
RoleList.Items.Clear();
string[] roleNames = Roles.GetAllRoles();

foreach (string roleName in roleNames)
{
ListItem roleListItem = new ListItem();
roleListItem.Text = roleName;
roleListItem.Selected = Roles.IsUserInRole(username, roleName);
RoleList.Items.Add(roleListItem);
}
}

protected void UpdateRolesFromList()
{
foreach (ListItem roleListItem in RoleList.Items)
{
string roleName = roleListItem.Value;
string userName = TxtUserName.Text;
bool enableRole = roleListItem.Selected;

if (enableRole && !Roles.IsUserInRole(userName, roleName))
{
Roles.AddUserToRole(userName, roleName);
}
else if (!enableRole && Roles.IsUserInRole(userName, roleName))
{
Roles.RemoveUserFromRole(userName, roleName);
}
}
}

protected void LookupBtn_Click(object sender, EventArgs e)
{
PopulateRoleList(TxtUserName.Text);
UpdateBtn.Visible = true;
}

protected void UpdateBtn_Click(object sender, EventArgs e)
{
UpdateRolesFromList();
PopulateRoleList(TxtUserName.Text);
}

}
}

Here is the .aspx content.
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeBehind="UserAdmin.aspx.cs" Inherits="MyApp.UserAdmin" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">


<h3>Role Manager</h3>

<div>
Enter UserName:
<asp:TextBox ID="TxtUserName" runat="server"></asp:TextBox>

<asp:Button ID="LookupBtn" runat="server" Text="Search"
onclick="LookupBtn_Click" />

</div>

<div class="roleList">
<asp:CheckBoxList ID="RoleList" runat="server">
</asp:CheckBoxList>
</div>

<div>
<asp:button ID="UpdateBtn" text="Update" Visible="false" runat="server"
onclick="UpdateBtn_Click" />
</div>

</asp:Content>
Step 7: Authorizing Access based on Roles
The most basic way to implement security is at the page level. This can be done via code using the membership api and Server.Transfer() calls, but it can also be done via the web.config and is the recommended way.
Let’s make sure users in our Admin role have access to everything by default. To do this modify the web.config authorization tag so that it it allows the Admin role, and blocks everyone else. This example assume your admin group is called Admin.
<authorization>
<deny users="?"/>
<allow roles="Admin"/>
</authorization>

To control which pages (or directories) different roles have access to you will need to explicitly grant rights. You need to add a location tag for each page you want to grant rights to. The location tag needs to be added in  the configuration tag. You can specify the path to be a file or a directory. You can specify multiple roles using a commas, but do NOT use semi-colons. Semi-colons sort of work such that the first item in the list is read. You can also create different web.config in directories to control the rights for that directory only. This can help reduce web.config clutter. Here is an example entry for your web.config.
<configuration>
<location path="Review.aspx">
<system.web>
<authorization>
<allow roles="Reviewer,Process Manager"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Tips: In the web.config, when you are specifying users or roles, * and ? have special meaning.
* means all users
? means anonymous
It is also important to understand that the allow and deny tags are applied in the order they show in the parent tag. Order matters.

Wednesday, May 6, 2009

Binding a control property to a code-behind property or method.

In ASP.NET often it is easier to bind a code-behind property with a control property instead of using FindControl() method to find the control on the page and set it through code. It is also less bound to where the control you are trying to work with is on the page, which means less bugs and less maintenance issues.

Let’s assume you have a button on the page that you want to show or hide based on some logic you have on your code-behind class or maybe in a static class or singleton class you can access in one line of code. You can bind to it from the .aspx or .ascx page.

Here are some examples of how we could bind the code-behind property Test to the Visible property of the button. The Visible property will only be updated when the container of this button is databound. This is usually done with the DataBind() method.

<asp:LinkButton ID="LinkButton1"runat="server"Text="Submit"Visible="<%# Test %>"/>

<asp:LinkButton ID="LinkButton1" runat="server" Text="Submit" Visible=’<%# Test %>’/>

Do NOT do the following (use equal sign instead of number sign), because it will NOT work.

<asp:LinkButton ID="LinkButton1" runat="server" Text="Submit" Visible=’<%= Test %>’/>

<asp:LinkButton ID="LinkButton1" runat="server" Text="Submit" Visible="<%= Test %>"/>

Let’s assume in the code-behind you have a property called Test. It would look something like one of these, but with more logic.

protected bool Test { get { return true; } }
protected Boolean Test { get { return true; } }

The property can be protected or public, but cannot be private because private properties in the code-behind are not visible to the .aspx or .ascx page. It is important that the return type match what the control property is expecting

You can also do the same thing (same variations apply), but use a method instead of a property in the code-behind.

<asp:LinkButton ID="LinkButton1"runat="server"Text="Submit" Visible="<%# Test2() %>"/>

<asp:LinkButton ID="LinkButton1" runat="server" Text="Submit" Visible=’<%# Test2() %>’/>
public bool Test2() { return true; }

If you don’t want to create a bunch of properties, you can also use standard code also.

<asp:LinkButton ID="LinkButton1" runat="server" Text="Submit" Visible=’<%# MyMode != “ReadOnly” && OtherConditional %>’/>

 

Monday, May 4, 2009

ManualWorkflowScheduler causes DelayActivity to not execute

If you are using ASP.NET and Windows Workflow Foundation (WF) you should be using the ManualWorkflowScheduler instead of the default scheduler. Otherwise you will use 2 threads for ever web request instead of 1. This of course assumes you call WF on each request.

If you have a DelayActity in your workflow you may have noticed that it does not automatically execute like it does when you use the default scheduler. I see the behavior when I use a State Machine Workflow. I can't comment on the Sequential Workflow, other than it appears from what I have read that it is affected also. Any comments from anyone?

The reason I believe the behavior is different is because like the name of the ManualWorkflowScheduler implies, it is manual. This translates to the fact that the execution thread that ASP.NET is using is temporarily used for the workflow execute, and then is given back when it is done with it.

In order to restore the behavior to be like the default scheduler where Timers fire as expected, all you need to do is tell the ManualWorkflowScheduler service use active timers. The easiest way is to add the attribute to the line in the web.config where you include the ManualWorkflowScheduler to begin with.

The line in my web.config looks like the following after the change.

<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UseActiveTimers="True"/>

The thing to take note of is the UseActiveTimers=”True”;

One important thing to note about setting UseActiveTimers=”True” is that yes time will run, but it is AFTER ASP.NET response is finished. The stuff after the timer is basically asynchronously executed instead of synchronously like the rest of the workflow. Remember, we are using the ManualWorkflowScheduler to change this behavior.  We have now, change the behavior back to what the default scheduler would have provided, but only for DelayActivities.

This means that anything that happens after the timer will require another server postback to get any changes that happened asynchronously. This may not be what you had in mind.

References

Using FindControl to find your control that is inside a FormView control.

The FindControl method is overloaded. So, be sure you are using the one you want to. The page object has one, but that will only find controls that are at the page level. This means that nothing in a container, or data control such as a FormView will be found by this method, unless you give it the path to the control.

Here is the simple example of a TextBox at the Page level.

<asp:TextBox ID="TextBox1"runat="server" />

To find the control from the C# code behind, you would do something like this.

TextBox tb = Page.FindControl("TextBox1") as TextBox;

Now for an example where the same TextBox is in the FormView control.

<asp:FormView ID="FormView1" runat="server">
    <ItemTemplate>
        <asp:TextBox ID="TextBox1" runat="server" />
    </ItemTemplate>
</asp:FormView>

Here is how you would find that same control if it was in a FormView control.

TextBox tb = Page.FindControl("FormView1$TextBox1") as TextBox;

The above line of code only works if the FormView1 control is directly on the page and NOT in some other container. Also, TextBox1 can’t be in a container that is inside of FormView1. If you have additional containers, delimit them with ‘$’.

I recommend saving yourself a bit of headache and use the overloaded FindControl that FormView provides. This way, the path is relative to FormView1 at least. Less dependency on where the controls are placed on the page is good in my book.

TextBox tb = FormView1.FindControl("TextBox1") as TextBox;

The FindControl is nice, but it is NOT recursive. However, this gives you the code to search recursively. There is a bit of a performance penalty (theoretically), so I can’t recommend it. Though, there are likely some cases where you don’t know where the control is on the page or if it is nested in a container, etc, so this kind of strategy might be necessary. Use with care. :)

Reference