Friday, May 8, 2015

Using AngularJS to bind JSON data from an HTTP service request to an HTML SELECT list

Here's an example of how to bind JSON data from a $http service using AngularJS. In this example we'll use Zodiac data from MySafeInfo.



This example relies on AngularJS and uses mysafeinfo.com to retrieve data in JSON format. MySafeInfo is a free service we offer to software developers to provide test data in a simple, flexible manner, in a variety of formats, including XML, JSON, JSONP, CSV, and more.

Thursday, May 7, 2015

Using JavaScript, jQuery, XML, and XSL to bind a grid and automatically calculate column/row totals

This example uses JavaScript and a simple XSL transformation to bind XML data to a grid. The XSL transformation does the initial calculations for the rows and columns. There is also jQuery code to handle the blur() event on the textboxes to update the totals automatically.

Since we're using a client-side XSL transformation there is code to handle the XSL transform for browsers that support XSLTProcessor (e.g. Mozilla browsers), and there is an else statement to handle other browsers (e.g. Internet Explorer). It's also worth noting that since this example is in JSFiddle, if you're using Internet Explorer you won't be able to actually see the results since JSFiddle does not allow an ActiveXObject to be created. With that said, if you copy the example to your local testing environment it is cross-browser compatible.





Click here to view the demo and test the functionality.

Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey

Tuesday, May 5, 2015

Using JavaScript and XSL to retrieve and display a list of NFL Teams in XML format

Here's an example of how to retrieve XML data using simple JavaScript and to display it using a client-side XSL transformation.

In this example we'll use NFL Teams data from a free data service called MySafeInfo. You can view the configurable options for this particular dataset by clicking here. Or you can view the raw data for this example by clicking here.

Since we're using a client-side XSL transformation there is code to handle the XSL transform for browsers that support XSLTProcessor (e.g. Mozilla browsers), and there is an else statement to handle other browsers (e.g. Internet Explorer). It's also worth noting that since this example is in JSFiddle, if you're using Internet Explorer you won't be able to actually see the results since JSFiddle does not allow an ActiveXObject to be created. With that said, if you copy the example to your local testing environment it is cross-browser compatible.



This example relies on JavaScript and uses mysafeinfo.com to retrieve NFL Teams data in XML format. MySafeInfo is a free service we offer to software developers to provide test data in a simple, flexible manner, in a variety of formats, including XML, JSON, JSONP, CSV, and more.

Here are some additional resources I came across while preparing this example.

Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey

Using pure good ol' fashioned JavaScript to access The Beatles' albums in JSON format

Here's an example of how to retrieve JSON data using pure good ol' fashioned JavaScript. I'm a big fan of jQuery, and don't have anything against other JavaScript frameworks, but sometimes going back to the basics, and knowing how to do it with just JavaScript is good for you.

In this example we'll use The Beatles' albums data from a free data service called MySafeInfo. You can view the configurable options for this particular dataset by clicking here. Or you can view the raw data for this example by clicking here.



This example relies on JavaScript and uses mysafeinfo.com to retrieve The Beatles' albums data in JSON format. MySafeInfo is a free service we offer to software developers to provide test data in a simple, flexible manner, in a variety of formats, including XML, JSON, JSONP, CSV, and more.

Here are some additional resources I came across while preparing this example.

Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey

Monday, May 4, 2015

Using Knockout and jQuery to retrieve list of U.S. Presidents in JSON format using MySafeInfo API

Here's an example of how to retrieve JSON data using jQuery and binding it to a simple table using Knockout. In this example we'll use U.S. Presidents data from a free data service called MySafeInfo. You can view the configurable options for this particular dataset by clicking here. Or you can view the raw data for this example by clicking here.



This example relies on Knockout and uses mysafeinfo.com to retrieve U.S. Presidents data in JSON format. MySafeInfo is a free service we offer to software developers to provide test data in a simple, flexible manner, in a variety of formats, including XML, JSON, JSONP, CSV, and more.

Using AngularJS to retrieve list of U.S. Presidents in JSON format using MySafeInfo API

Here's an example of how to bind JSON data from a $http service using AngularJS. In this example we'll use U.S. Presidents data from a free data service called MySafeInfo. You can view the configurable options for this particular dataset by clicking here. Or you can view the raw data for this example by clicking here.



This example relies on AngularJS and uses mysafeinfo.com to retrieve U.S. Presidents data in JSON format. MySafeInfo is a free service we offer to software developers to provide test data in a simple, flexible manner, in a variety of formats, including XML, JSON, JSONP, CSV, and more.

Sunday, May 3, 2015

ASP.Net C# SQL Building Your Own Url Shortner

Several years ago I had to build a custom URL shortner for a website. I recently had a task where I needed to do something similar, so I converted it to C# and thought I'd share how easy this can be.

First off you'll need a database table for the url data. In this example I'm using SQL Server 2008.
 SET ANSI_NULLS ON  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 SET ANSI_PADDING ON  
 GO  
 CREATE TABLE [dbo].[Urls](  
      [PK] [int] IDENTITY(1,1) NOT NULL,  
      [Key] [varchar](10) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL CONSTRAINT [DF_Urls_Key] DEFAULT (''),  
      [Url] [varchar](2000) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL CONSTRAINT [DF_Urls_Url] DEFAULT (''),  
      [CreatedBy] [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,  
      [CreatedDate] [smalldatetime] NOT NULL CONSTRAINT [DF_Urls_CreatedDate] DEFAULT (getdate()),  
  CONSTRAINT [PK_Urls] PRIMARY KEY CLUSTERED   
 (  
      [PK] ASC  
 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],  
  CONSTRAINT [IX_Urls] UNIQUE NONCLUSTERED   
 (  
      [Key] ASC  
 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
 ) ON [PRIMARY]  
   
 GO  
 SET ANSI_PADDING OFF  
 GO  

PK column to act as the primary key
- Key column is a unique nonclustered column, which is the unique key for the shortened url
- Url column which is the fully qualified url that that the shortened url should redirect to
- CreatedBy is a simple varchar field to provide optional auditing
CreatedDate is a simple smalldatetime field to provide optional auditing

It's worth noting that I'm using SQL_Latin1_General_CP1_CS_AS for the collation on the Key column. This isn't absolutely necessary; however, it means that the Key column is case-sensitive and makes the universe much larger for the number of unique values that be be stored in that column.

The next step is to create the view and scalar-valued functions.

GetUniqueIdentifierView
 SET ANSI_NULLS ON  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 CREATE VIEW [dbo].[GetUniqueIdentifierView]  
 AS  
 SELECT 'ID' = NEWID()  
 GO  

GetUniqueIdentifier
 SET ANSI_NULLS ON  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 CREATE FUNCTION [dbo].[GetUniqueIdentifier]()  
 RETURNS UNIQUEIDENTIFIER  
 AS   
 BEGIN  
      RETURN (SELECT ID FROM GetUniqueIdentifierView)  
 END  
 GO  

GetRandomString
 SET ANSI_NULLS OFF  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 CREATE FUNCTION [dbo].[GetRandomString]  
 (  
      @Length     INT = 5  
 )  
 RETURNS VARCHAR(MAX)  
 AS  
 BEGIN  
      -- variables  
      DECLARE @Characters     VARCHAR(62)  
      DECLARE @Output         VARCHAR(MAX)  
      DECLARE @i              INT  
        
      -- values  
      SET @Characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'  
      SET @Output = ''  
      SET @i = 0  
        
      -- loop  
      WHILE @i < @Length  
      BEGIN  
           -- variables  
           DECLARE @Position INT  
             
           -- random position in character map  
           SET @Position = ABS(CAST(CAST(dbo.GetUniqueIdentifier() AS VARBINARY) AS INT)) % LEN(@Characters)  
             
           -- concatenate the random character map value to our string  
           SET @Output = @Output + SUBSTRING(@Characters, @Position + 1, 1)  
   
           -- increment loop counter       
           SET @i = @i + 1  
      END  
        
      -- return value  
      RETURN ISNULL(@Output, '')  
 END  
 GO  

At this point we have a simple function we can call to get a random string of a specified length, for example:
 SELECT dbo.GetRandomString(5)  

Now we'll create the stored procedures to finish off the SQL side of things.

The first stored procedure is Urls_List. This stored procedure has optional parameters to either return all urls, return a single url for the specified PK, or return a single url for the specified Key.
 SET ANSI_NULLS OFF  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 CREATE PROCEDURE [dbo].[Urls_List]  
 (  
      @PK      INT             = 0,  
      @Key     VARCHAR(10)     = ''  
 )  
 AS  
 BEGIN  
      -- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.  
      SET NOCOUNT ON;  
   
      -- get data  
      SELECT        PK,  
                    [Key],  
                    Url,  
                    'UrlShort' = '{BaseUrl}url/?' + [Key],  
                    CreatedBy,  
                    CreatedDate  
      FROM          Urls  
      WHERE         PK =   
                          CASE  
                               WHEN @PK > 0 THEN @PK  
                               ELSE PK  
                          END  
      AND               [Key] =   
                          CASE  
                               WHEN LEN(@Key) > 0 THEN @Key  
                               ELSE [Key]  
                          END  
      ORDER BY      PK  
 END  
 GO  

Now you have a simple way to get the data from the Urls table, for example:
 -- get all urls  
 EXEC dbo.Urls_List  
   
 -- get url for specified PK  
 EXEC dbo.Urls_List @PK = 1  
   
 -- get url for specified Key  
 EXEC dbo.Urls_List @Key = 'gyzAq'  

You'll want to pay special attention to the {BaseUrl} reference in the Urls_List stored procedure. You could do one of two things here. You could plug your fully qualified base url directly in there (e.g. http://pavey.me/) OR you can keep that placeholder in there, and replace it generically in either the data layer, model object, or the web project, depending on your requirements.

The next stored procedure is Urls_Save. This stored procedure creates the record in the Urls table with a unique key and returns the record. If a record with the specified Url already exists it will return that record instead of creating a new one.
 SET ANSI_NULLS OFF  
 GO  
 SET QUOTED_IDENTIFIER ON  
 GO  
 CREATE PROCEDURE [dbo].[Urls_Save]  
 (  
      @Url          VARCHAR(2000),  
      @CreatedBy    VARCHAR(100)  
 )  
 AS  
 BEGIN  
      -- declare variables  
      DECLARE @PK     INT  
        
      -- see if url already exists  
      SELECT   @PK = PK  
      FROM     Urls  
      WHERE    Url = @Url  
        
      -- handle nulls  
      SET @PK = ISNULL(@PK, 0)  
        
      -- create url if necessary  
      IF @PK < 1  
      BEGIN  
           -- variables  
           DECLARE @Key            VARCHAR(5) = ''  
           DECLARE @KeyLength      INT        = 5  
           DECLARE @KeyIsUnique    BIT        = 0  
             
           -- get random key  
           SET @Key = dbo.GetRandomString(@KeyLength)  
   
           -- check to see if key already exists  
           IF NOT EXISTS(SELECT PK FROM Urls WHERE [Key] = @Key)  
           BEGIN  
                SET @KeyIsUnique = 1  
           END  
   
           -- if key is not unique keep looking  
           IF @KeyIsUnique = 0  
           BEGIN  
                WHILE @KeyIsUnique = 0  
                BEGIN  
                     -- get random key  
                     SET @Key = dbo.GetRandomString(@KeyLength)  
   
                     -- check to see if key already exists  
                     IF NOT EXISTS(SELECT PK FROM Urls WHERE [Key] = @Key)  
                     BEGIN  
                          SET @KeyIsUnique = 1  
                     END  
                END  
           END  
   
           -- insert record  
           INSERT INTO Urls  
                ([Key], Url, CreatedBy)  
           VALUES  
                (@Key, @Url, @CreatedBy)  
                  
           -- get identity  
           SET @PK = SCOPE_IDENTITY()  
      END  
   
      -- return url  
      EXEC Urls_List @PK = @PK  
 END  
 GO  

You'll notice in the Urls_Save stored procedure I'm using @KeyLength = 5. The GetRandomString function we created can handle creating a random string of any size, so you'll just want to set this based on your needs. In my case, a unique key length of 5 was sufficient.

This takes care of the SQL side of things. Now we are going to create a model class to represent our url data. I keep my model classes in a Model project, and I use a BaseModel class, which for purposes of this example is empty, but gives you a place to provide base support for your model classes.

BaseModel.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
   
 namespace Sandbox.Model  
 {  
   public class BaseModel : IDisposable  
   {  
   
     #region "IDisposable Support"  
     // To detect redundant calls  
     private bool disposedValue;  
   
     // IDisposable  
     protected virtual void Dispose(bool disposing)  
     {  
       if (!this.disposedValue)  
       {  
         if (disposing)  
         {  
           // TODO: dispose managed state (managed objects).  
         }  
   
         // TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.  
         // TODO: set large fields to null.  
       }  
       this.disposedValue = true;  
     }  
   
     // TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.  
     //Protected Overrides Sub Finalize()  
     //  ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.  
     //  Dispose(False)  
     //  MyBase.Finalize()  
     //End Sub  
   
     // This code added by Visual Basic to correctly implement the disposable pattern.  
     public void Dispose()  
     {  
       // Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.  
       Dispose(true);  
       GC.SuppressFinalize(this);  
     }  
     #endregion  
   }  
 }  

UrlShort.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
   
 namespace Sandbox.Model  
 {  
   public class UrlShortner : BaseModel  
   {  
     // constructor  
     public UrlShortner()  
     {  
       PK = 0;  
       Key = string.Empty;  
       Url = string.Empty;  
       UrlShort = string.Empty;  
       CreatedBy = string.Empty;  
       CreatedDate = DateTime.MinValue;  
     }  
   
     // public properties  
     public int PK { get; set; }  
     public string Key { get; set; }  
     public string Url { get; set; }  
     public string UrlShort { get; set; }  
     public string CreatedBy { get; set; }  
     public DateTime CreatedDate { get; set; }  
   }  
 }  

Now we'll create the data layer class. I keep the data layer classes in a Data project, and I use a BaseDB class, which for purposes of this example is empty, but gives you a place to provide base support for your data layer classes. In this example we're using Microsoft Enterprise Library 5.0 – May 2011 to provide the data access.

BaseDB.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
   
 namespace Sandbox.Data  
 {  
   public class BaseDB : IDisposable  
   {  
   
     #region "IDisposable Support"  
     // To detect redundant calls  
     private bool disposedValue;  
   
     // IDisposable  
     protected virtual void Dispose(bool disposing)  
     {  
       if (!this.disposedValue)  
       {  
         if (disposing)  
         {  
           // TODO: dispose managed state (managed objects).  
         }  
   
         // TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.  
         // TODO: set large fields to null.  
       }  
       this.disposedValue = true;  
     }  
   
     // TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.  
     //Protected Overrides Sub Finalize()  
     //  ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.  
     //  Dispose(False)  
     //  MyBase.Finalize()  
     //End Sub  
   
     // This code added by Visual Basic to correctly implement the disposable pattern.  
     public void Dispose()  
     {  
       // Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.  
       Dispose(true);  
       GC.SuppressFinalize(this);  
     }  
     #endregion  
   }  
 }  
   

Urls.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Data;  
 using System.Data.Common;  
 using Microsoft.Practices.EnterpriseLibrary.Data;  
 using Sandbox.Utilities;  
   
 namespace Sandbox.Data  
 {  
   public class Urls : BaseDB  
   {  
     public Model.UrlShortner Get(int PK)  
     {  
       // validate  
       if (PK < 1)  
       {  
         return new Model.UrlShortner();  
       }  
   
       // lookup  
       List<Model.UrlShortner> x = List(PK: PK);  
   
       // return  
       if (x.Count > 0)  
       {  
         return x.First();  
       }  
       else  
       {  
         return new Model.UrlShortner();  
       }  
     }  
   
     public Model.UrlShortner Get(string Key)  
     {  
       // validate  
       if (Key.IsBlank())  
       {  
         return new Model.UrlShortner();  
       }  
   
       // lookup  
       List<Model.UrlShortner> x = List(Key: Key);  
   
       // return  
       if (x.Count > 0)  
       {  
         return x.First();  
       }  
       else  
       {  
         return new Model.UrlShortner();  
       }  
     }  
   
     public List<Model.UrlShortner> List(Int32 PK = 0, string Key = "")  
     {  
       // variables  
       Database DB = DatabaseFactory.CreateDatabase();  
       List<Model.UrlShortner> Urls = new List<Model.UrlShortner>();  
   
       // command  
       using (DbCommand cmd = DB.GetStoredProcCommand("dbo.Urls_List"))  
       {  
         // parameters  
         DB.AddInParameter(cmd, "@PK", DbType.Int32, PK);  
         DB.AddInParameter(cmd, "@Key", DbType.String, Key);  
   
         // execute query and get results  
         using (DataTable DT = new DataTable())  
         {  
           using (IDataReader IDR = DB.ExecuteReader(cmd))  
           {  
             if (IDR != null)  
             {  
               DT.Load(IDR);  
             }  
           }  
   
           // convert to business object  
           Urls = DT.ToList<Model.UrlShortner>().ToList();  
         }  
       }  
   
       // return list  
       return Urls;  
     }  
   
     public Model.UrlShortner Save(string Url, string CreatedBy = "")  
     {  
       // variables  
       Database DB = DatabaseFactory.CreateDatabase();  
       Model.UrlShortner UrlShort = new Model.UrlShortner();  
   
       // command  
       using (DbCommand cmd = DB.GetStoredProcCommand("dbo.Urls_Save"))  
       {  
         // parameters  
         DB.AddInParameter(cmd, "@Url", DbType.String, Url);  
         DB.AddInParameter(cmd, "@CreatedBy", DbType.String, CreatedBy);  
   
         // execute query and get results  
         using (DataTable DT = new DataTable())  
         {  
           using (IDataReader IDR = DB.ExecuteReader(cmd))  
           {  
             if (IDR != null)  
             {  
               DT.Load(IDR);  
             }  
           }  
   
           // convert to business object  
           if (DT.Rows.Count > 0)  
           {  
             UrlShort = DT.Rows[0].ToObject<Model.UrlShortner>();  
           }            
         }  
       }  
   
       // return value  
       return UrlShort;  
     }  
   }  
 }  

It might seem like overkill for this example, but I'm using some extension methods to make the data layer much more generic. For example, instead of having to do manual column mapping to map the data from the DataTable to the model object I am able to use ToObject and ToList to do that generically. You wouldn't believe how much time generic extension methods like this will save you over the course of your project.

Extensions.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Reflection;  
 using System.Data;  
 using System.ComponentModel;
   
 namespace Sandbox.Utilities  
 {  
   public static class Extensions  
   {  
     private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();  
   
     public static IList<PropertyInfo> GetPropertiesForType<T>()  
     {  
       // variables  
       var type = typeof(T);  
   
       // get types  
       if (!typeDictionary.ContainsKey(typeof(T)))  
       {  
         typeDictionary.Add(type, type.GetProperties().ToList());  
       }  
   
       // return  
       return typeDictionary[type];  
     }  
   
     public static T ToObject<T>(this DataRow row) where T : new()  
     {  
       // variables  
       IList<PropertyInfo> properties = GetPropertiesForType<T>();  
   
       // return  
       return CreateItemFromRow<T>(row, properties);  
     }  
   
     public static IList<T> ToList<T>(this DataTable table) where T : new()  
     {  
       // variables  
       IList<T> result = new List<T>();  
   
       // foreach  
       foreach (DataRow row in table.Rows)  
       {  
         result.Add(row.ToObject<T>());  
       }  
   
       // return  
       return result;  
     }  
   
     private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()  
     {  
       // variables  
       T item = new T();  
   
       // foreach  
       foreach (var property in properties)  
       {  
         // make sure a column exists in the table with this property name  
         if (row.Table.Columns.Contains(property.Name))  
         {  
           // get the value from the current data row  
           object value = row[property.Name];  
   
           // set property accordingly  
           if (value != null & value != DBNull.Value)  
           {  
             SetProperty<T>(item, property.Name, value);  
           }  
         }  
       }  
   
       // return  
       return item;  
     }  
   
     public static string GetProperty<T>(this T obj, string Property)  
     {  
       // reflection  
       PropertyInfo propertyInfo = obj.GetType().GetProperty(Property, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);  
       object property = null;  
   
       // make sure property is valid  
       if (propertyInfo != null)  
       {  
         property = propertyInfo.GetValue(obj, null);  
       }  
   
       // return value  
       if (property != null)  
       {  
         return property.ToString();  
       }  
       else  
       {  
         return string.Empty;  
       }  
     }  
   
     public static T SetProperty<T>(this T obj, string Property, object Value)  
     {  
       // reflection  
       PropertyInfo prop = obj.GetType().GetProperty(Property, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);  
   
       // trim strings  
       if (Value.GetType() == typeof(string))  
       {  
         Value = Value.ToString().Trim();  
       }  
   
       // make sure property is valid  
       if (prop != null && prop.CanWrite)  
       {  
         prop.SetValue(obj, Value, null);  
       }  
   
       // return  
       return obj;  
     }  

     public static bool HasValue(this string Value)  
     {  
       return !Value.IsBlank();  
     }  
   
     public static bool IsBlank(this string Value)  
     {  
       bool ReturnValue = true;  
   
       if (Value != null)  
       {  
         ReturnValue = Value.Trim().Length == 0;  
       }  
   
       return ReturnValue;  
     }  
   }  
 }  

Now to round this all out we need to create a shortened url and use it. To create a shortened url you simply call the Save method from the data layer:
 // create shorterned url  
 using (Data.Urls DB = new Data.Urls())  
 {  
    using (Model.UrlShortner MyUrl = DB.Save(Url: "http://www.google.com", CreatedBy: "Testing"))  
    {  
       // MyUrl now has a reference to your shortened url
    }  
 }  

I typically would use a shortened url like this if I was going to include some kind of custom url for a user, for example resetting their password. The url may include an encrypted token and could look pretty ugly, so having a way to provide a nice clean url makes that much more appealing for the email.

Example Url:
http://dev.sandbox.com/forgot-password.aspx?q=urXsbqy9knIpqqOddkFICXTAspidUXCmPS2vJsLqkVanePU%2bGgxLahBGGl1EA%2f%2fXyXxGT4EX6kcqYSvy8BTpib6eEB61Q5WxNcNfEjk1OYx4HGyvO5oKs34JZ%2f1p9jMuAvVpkbkKpBBjYD2UiotIiYob%2baSzHmxRUuUJYkRepd6kSosnXOXssKVQJ%2bWbQQkYfNZWt2OUe9nGw1UOBd1aeO3O9JyEqSiMkYoIF1blW3f9MUx461LSnB9FL2Q4Vbn%2bDGyK0kEHPQVaz5fhpSXIPrfN3Q%2flRtjyqWDXrRnx2%2bE%3e

Example Shortened Url:
http://dev.sandbox.com/url/?Z4cgw

So now we can create the shortened url and know what it looks like, all that's left to do is allow your web application to actually recognize the shortened url and redirect to the actual url.

Start off by creating a new folder in your web project. I called mine url, which is why in the example shortened url above I used /url. This could literally be any folder name you wanted, but something short makes sense.

After you've got the folder created, create a Default.aspx page in that folder, with the following:

Default.aspx
 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Sandbox.Web.url.Default" %>  
   
 <!-- see code behind -->  

Default.aspx.cs
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 using System.Web.UI;  
 using System.Web.UI.WebControls;  
 using Sandbox.Utilities;  
   
 namespace Sandbox.Web.url  
 {  
   public partial class Default : System.Web.UI.Page  
   {  
     protected void Page_Load(object sender, EventArgs e)  
     {  
       if (!Page.IsPostBack)  
       {  
         ProcessUrl();  
       }  
     }  
   
     private void ProcessUrl()  
     {  
       // variables  
       string Key = Request.QueryString.ToString();  
       string Url = string.Empty;  
   
       // check database for url  
       if (Key.HasValue())  
       {  
         using (Data.Urls DB = new Data.Urls())  
         {  
           using (Model.UrlShortner x = DB.Get(Key: Key))  
           {  
             Url = x.Url;  
           }  
         }  
       }  
   
       // redirect  
       if (Url.HasValue())  
       {  
         Response.Redirect(Url);  
         Response.End();  
       }  
       else  
       {  
         Response.Redirect("~/home.aspx");  
         Response.End();  
       }  
     }  
   }  
 }  

This page simply takes the querystring value and checks to see if there is a matching record in the Urls table with the specified Key. If found, it redirects to that url. If not, it falls back to redirecting the user to some default/home page.

This is just one way to build a URL shortner. You can strip out the requirement for the extension methods or change it to use your own style of model/data classes, but the overall concept doesn't change:

- Database table for the url data
- Views/Functions/Procs for creating/accessing the url data
- Model object for representing the url data
- Data layer class for calling the stored procedures and returning the model object
- Folder for processing the shortened url and redirecting appropriately

Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey

Saturday, May 2, 2015

ASP.Net C# Get Page Name With Optional Parameters To Include Extension And QueryString

Here's a simple static function that I keep in my utilities/functions class to let you easily get the the page name being requested.

Functions.cs
 public static string GetPageName(bool IncludeExtension = false, bool IncludeQueryString = false)  
 {  
    string AbsolutePath = HttpContext.Current.Request.Url.AbsolutePath;  
    string PageName = Path.GetFileName(AbsolutePath);  
    string Extension = Path.GetExtension(AbsolutePath);  
    string QueryString = HttpContext.Current.Request.QueryString.ToString();  
   
    if (!IncludeExtension && !IncludeQueryString && PageName.HasValue())  
    {  
       PageName = PageName.Replace(Extension, string.Empty);  
    }  
   
    if (IncludeQueryString && PageName.HasValue() && QueryString.HasValue())  
    {  
       PageName = string.Format("{0}?{1}", PageName, QueryString);  
    }  
   
    return PageName;  
 }  

The GetPageName function is dependent on the following extension methods, although it could easily be re-factored to check the string length directly; however, I prefer to use these types of extension methods throughout the projects to keep things consistent and concise.

Extensions.cs
 public static bool HasValue(this string Value)  
 {  
    return !Value.IsBlank();  
 }  
   
 public static bool IsBlank(this string Value)  
 {  
    bool ReturnValue = true;  
   
    if (Value != null)  
    {  
       ReturnValue = Value.Trim().Length == 0;  
    }  
   
    return ReturnValue;  
 }  

The GetPageName function is very basic but a nice way to get the page name, and optionally lets you indicate whether or not to include the extension in the page name, and whether you want to include the querystring parameters in the page name.

Example usage and output:
 string Url = Functions.GetPageName(IncludeExtension: true, IncludeQueryString: true);  
Test.aspx?x=1

Example usage and output:
 string Url = Functions.GetPageName(IncludeExtension: true, IncludeQueryString: false);  
Test.aspx

Example usage and output:
 string Url = Functions.GetPageName(IncludeExtension: false, IncludeQueryString: false);  
Test

Matt Pavey is a Microsoft Certified software developer who specializes in ASP.Net, VB.Net, C#, AJAX, LINQ, XML, XSL, Web Services, SQL, jQuery, and more. Follow on Twitter @matthewpavey