System.ComponentModel.DataAnnotations for ASP.NET Web Forms
Although I'm primarily an ASP.NET Web Forms developer, I regularly dabble in new and interesting technologies. I've toyed with other Microsoft technologies such as ASP.NET MVC and Dynamic Data web sites.
ASP.NET MVC offers an interesting mechanism for validating view models called DataAnnotations. Examples can be seen here and here.
Some of the more useful attributes for validation include:
- RequiredAttribute
- RegularExpressionAttribute
- RangeAttribute
- DataTypeAttribute
I recently decided to implement a data validation schema (for lack of a better term) for web forms similar to that of ASP.NET MVC. This would allow us to maintain validation of a model object via attributes at the class level, instead of dispersing these validation rules through the web form code or any of the business logic layers. However, there will be occasions where an object's validity depends upon some state or validity of another object. I've handled this by allowing the validation method to accept any action to be called after the object has been validated.
In order to make a domain object validatable, I'm going to implement the following interface:
IValidatable.cs
namespace Validatable
{
interface IValidatable
{
System.Type EntityType { get; set; }
System.Collections.Generic.List<object> Errors { get; set; }
/// <summary>
/// Determine whether the object is valid. If invalid, errors are added to item.Errors
/// </summary>
/// <returns>true if valid, false if invalid</returns>
bool IsValid();
/// <summary>
/// Validate this item against a supplied action
/// </summary>
/// <param name="action">The action to use for validation</param>
void Validate(System.Action action);
/// <summary>
/// Validate this object against a Repository.
/// <example>
/// Item a = new Item();
/// a.Validate(new ItemRepository());
/// </example>
/// Note: Repository must have a Validate(T item) method
/// </summary>
/// <param name="repository">The Repository to use for validation</param>
void Validate(object repository);
}
}
As you can see, this abstract class adds a property accessor for the entity's type, a list of validation errors, an IsValid method, and two Validate methods.
Validatable<T>
I'll take the code in chunks, since there is a lot more than I usually post.
First of all, the using directives required for this class are pretty sparse:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
The class definition is:
[Serializable]
public abstract class Validatable<T> : IValidatable where T : class, new()
{ /* class */ }
If you're unfamiliar with generic constraints, the where T : class, new() above enforces the T passed as in to be a class and to have a parameterless constructor (new())
The properties can be auto-implemented properties, or they can implement any backing logic you prefer. The constructor as I have it is:
protected Validatable()
{
Errors = new List<object>();
EntityType = typeof(T);
}
Now for the methods and a slight explanation of each:
IsValid()
public virtual bool IsValid()
{
Errors = new List<object>();
PropertyInfo[] props = this.GetType().GetProperties();
foreach (PropertyInfo property in props)
{
foreach (ValidationAttribute va in
property.GetCustomAttributes(true).OfType<ValidationAttribute>())
{
var value = property.GetValue(this, null);
if (!va.IsValid(value))
{
Errors.Add(va.ErrorMessage);
}
}
}
return Errors.Count <= 0;
}
As a side note, all DataAnnotation attributes inherit from ValidationAttribute. You can create your own Validation Attributes by inheriting from this class. This method clears the list of errors and repopulates it by looping over any properties with validation attributes of this object, validates that property's value against the attribute, and adds the ErrorMessage associated with it to the list of Errors.
Validate Methods
public virtual void Validate(Action action)
{
if (IsValid())
{
action();
}
}
public virtual void Validate(object repository)
{
MethodInfo method = repository.GetType().GetMethods()
.Where(x => x.Name.Equals("Validate")).FirstOrDefault();
if (method != null)
{
object[] parameters = new object[] { this };
Validate(() => method.Invoke(repository, parameters));
}
}
These two methods can be changed to throw an exception if the object is invalid. I'll leave that up to you. The way these are set up is to allow you to validate the object and perform either some unknown action or call Validate(T item) against some other object, arbitrarily called a repository. In actuality, you could have a Validate method on some other object that takes an object of this type and essentially chain validations.
The simplicity of the Validate(Action action) method is what makes it beautiful. For instance, you can have an item (a) and cause it save after validation by doing something like:
a.Validate(() => new ItemDAL().Save(a));
As you can see, a Save method in your Item's Data Access Layer taking Item as a parameter can be called only when a.IsValid() is true. Again, there are a number of ways to tweak this and change it to your liking, but I'll leave that up to you.
DataAnnotationValidator
Here is the wonderful part about all of this. You can create a custom validator that validates in a similar way as the Validatable class. I'll post the code and quickly explain what it does. I got the idea from this from another blog and tweaked it a little.
DataAnnotationValidator.cs
// DataAnnotationValidator.cs
namespace Validatable
{
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
[ToolboxData("<{0}:DataAnnotationValidator runat=\"server\" ControlToValidate=\"[Required]\" Display=\"Dynamic\" Text=\"*\" SourceTypeName=\"[FullyQualifiedTypeName]\" PropertyToValidate=\"[PropertyName]\" />")]
public class DataAnnotationValidator : BaseValidator
{
/// <summary>
/// THe Property that should be checked
/// </summary>
public string PropertyToValidate { get; set; }
/// <summary>
/// The object's type
/// </summary>
public string SourceTypeName { get; set; }
protected override bool EvaluateIsValid()
{
Type source = GetValidatedType();
PropertyInfo property = GetValidatedProperty(source);
string value = GetControlValidationValue(ControlToValidate);
foreach (ValidationAttribute va in property
.GetCustomAttributes(typeof(ValidationAttribute), true)
.OfType<ValidationAttribute>())
{
if (!va.IsValid(value))
{
if (string.IsNullOrEmpty(ErrorMessage))
{
this.ErrorMessage = va.ErrorMessage;
}
return false;
}
}
return true;
}
private Type GetValidatedType()
{
if (string.IsNullOrEmpty(SourceTypeName))
{
throw new InvalidOperationException("Null SourceTypeName can't be validated");
}
Type validatedType = Type.GetType(SourceTypeName);
if (validatedType == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}", "Invalid SourceTypeName", SourceTypeName));
}
return validatedType;
}
private PropertyInfo GetValidatedProperty(Type source)
{
PropertyInfo property = source.GetProperty(PropertyToValidate,
BindingFlags.Public | BindingFlags.Instance);
if (property == null)
{
throw new InvalidOperationException(
string.Format("{0}:{1}", "Validated Property Does Not Exists", PropertyToValidate));
}
return property;
}
}
}
This creates a validator web control which has 'PropertyToValidate' and 'SourceTypeName'. When a Validator web control is added to a page, it must have ControlToValidate, PropertyToValidate, and SourceTypeName specified or an error will be thrown. The validation method verifies the type and the property, gets the value of the property and finally, much like the Validatable abstract class, it loops over all ValidationAttributes and validates the value against that attribute. You can use this with multiple attributes, so it doesn't hurt to add this validator to a property-- that just means you can add an attribute in the future and everything is already wired up! That's pretty cool.
Example
As an example, I'm going to create a single page to **input** a Customer object. The customer object is very simple:
public class Customer : Validatable.Validatable<Customer>
{
[Required(ErrorMessage="UserId is Required")]
public int UserId { get; set; }
[DataType(DataType.PhoneNumber, ErrorMessage="Invalid Phone Number")]
public string PhoneNumber { get; set; }
[RegularExpression(@"(^\d{5}(-\d{4}){0,1}$)", ErrorMessage="Invalid Zip Code")]
public string ZipCode { get; set; }
}
And the Page in full is collapsed below:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Example._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="lblStatus" runat="server" />
<asp:ValidationSummary ID="ValidationSummary1" runat="server" />
<div>
<asp:FormView ID="FormView1" runat="server" DataSourceID="srcRepository"
Width="119px" AllowPaging="True">
<EditItemTemplate>
UserId:
<asp:TextBox ID="UserIdTextBox" runat="server" Text='<%# Bind("UserId") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator1" runat="server" ControlToValidate="UserIdTextBox" Display="Dynamic"
PropertyToValidate="UserId" ErrorMessage="Invalid User Identification Number"
SourceTypeName="Example.Customer, Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Text="*"/>
<br />
PhoneNumber:
<asp:TextBox ID="PhoneNumberTextBox" runat="server"
Text='<%# Bind("PhoneNumber") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator2" runat="server" ControlToValidate="PhoneNumberTextBox" Display="Dynamic"
PropertyToValidate="PhoneNumber" OnInit="GetTypeName" SourceTypeName="Example.Customer, Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Text="*"/>
<br />
ZipCode:
<asp:TextBox ID="ZipCodeTextBox" runat="server" Text='<%# Bind("ZipCode") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator3" runat="server" ControlToValidate="ZipCodeTextBox" Display="Dynamic"
PropertyToValidate="ZipCode" SourceTypeName="Example.Customer, Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Text="*"/>
<br />
EntityType:
<asp:TextBox ID="EntityTypeTextBox" runat="server"
Text='<%# Eval("EntityType") %>' />
<br />
<asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True"
CommandName="Update" Text="Update" />
<asp:LinkButton ID="UpdateCancelButton" runat="server"
CausesValidation="False" CommandName="Cancel" Text="Cancel" />
</EditItemTemplate>
<InsertItemTemplate>
UserId:
<asp:TextBox ID="UserIdTextBox" runat="server" Text='<%# Bind("UserId") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator1" runat="server" ControlToValidate="UserIdTextBox" Display="Dynamic"
PropertyToValidate="UserId" OnInit="GetTypeName" Text="*"/>
<br />
PhoneNumber:
<asp:TextBox ID="PhoneNumberTextBox" runat="server"
Text='<%# Bind("PhoneNumber") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator2" runat="server" ControlToValidate="PhoneNumberTextBox" Display="Dynamic"
PropertyToValidate="PhoneNumber" OnInit="GetTypeName" Text="*"/>
<br />
ZipCode:
<asp:TextBox ID="ZipCodeTextBox" runat="server" Text='<%# Bind("ZipCode") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator3" runat="server" ControlToValidate="ZipCodeTextBox" Display="Dynamic"
PropertyToValidate="ZipCode" OnInit="GetTypeName" Text="*"/>
<br />
EntityType:
<asp:TextBox ID="EntityTypeTextBox" runat="server"
Text='<%# Eval("EntityType") %>' />
<br />
<asp:LinkButton ID="InsertButton" runat="server" CausesValidation="True"
CommandName="Insert" Text="Insert" />
<asp:LinkButton ID="InsertCancelButton" runat="server"
CausesValidation="False" CommandName="Cancel" Text="Cancel" />
</InsertItemTemplate>
<ItemTemplate>
UserId:
<asp:Label ID="UserIdTextBox" runat="server" Text='<%# Bind("UserId") %>' />
<br />
PhoneNumber:
<asp:Label ID="PhoneNumberTextBox" runat="server"
Text='<%# Bind("PhoneNumber") %>' />
<br />
ZipCode:
<asp:Label ID="ZipCodeTextBox" runat="server" Text='<%# Bind("ZipCode") %>' />
<br />
EntityType:
<asp:Label ID="EntityTypeLabel" runat="server"
Text='<%# Bind("EntityType") %>' />
<br />
<asp:LinkButton ID="EditButton" runat="server" CausesValidation="False"
CommandName="Edit" Text="Edit" />
<asp:LinkButton ID="NewButton" runat="server" CausesValidation="False"
CommandName="New" Text="New" />
</ItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="srcRepository" runat="server"
DataObjectTypeName="Example.Customer" InsertMethod="Save"
oninserted="srcRepository_Saved" onupdated="srcRepository_Saved"
SelectMethod="Get" TypeName="Example.CustomerRepository" UpdateMethod="Save"></asp:ObjectDataSource>
</div>
</form>
</body>
</html>
Of particular note here is in the EditTemplate, you'll see
UserId:
<asp:TextBox ID="UserIdTextBox" runat="server" Text='<%# Bind("UserId") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator1" runat="server" ControlToValidate="UserIdTextBox" Display="Dynamic"
PropertyToValidate="UserId" ErrorMessage="Invalid User Identification Number"
SourceTypeName="Example.Customer, Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Text="*"/>
in which the SourceTypeName must be the full Assembly Qualified Name. This is necessary if the Validatable
UserId:
<asp:TextBox ID="UserIdTextBox" runat="server" Text='<%# Bind("UserId") %>' />
<example:DataAnnotationValidator ID="DataAnnotationValidator1" runat="server" ControlToValidate="UserIdTextBox" Display="Dynamic"
PropertyToValidate="UserId" OnInit="GetTypeName" Text="*"/>
and the code in the code behind is very simple:
protected void GetTypeName(object sender, EventArgs e)
{
DataAnnotationValidator validator = (DataAnnotationValidator)sender;
validator.SourceTypeName = new Customer().EntityType.AssemblyQualifiedName;
}
Also notice that the first validator (the one with the assembly qualified name) has an ErrorMessage property specified. Doing this allows you to override the error message returned from the validated object. We can do this if the model says "UserId" and you want the user to see this property referred to as "User Identification".
Conclusion
Sometimes dabbling in other technologies can open the door for new and simpler ways of doing things. I like this example because it allows you to build web applications quickly and easily, while making them maintainable in the future (new validations only have to be added to the model, not to every control where the model must be validated).
For further reference, this project is linked below. Please download and modify if necessary.
Validatable.zip
Finding a user’s local time from UTC offset
I answered a question over at StackOverflow, and I really liked the answer, so I thought I would share this nifty extension method:
/// <summary>
/// Convert a given DateTime object to a user's local time,
/// taking into account changes in TimeZone rules.
/// For example, if you were to perform this operation on
/// a time now, during EST Daylight Saving, and that time falls
/// outside the scope of Daylight Saving time, the rule will adjust accordingly.
/// </summary>
/// <param name="dateTime">The DateTime object</param>
/// <param name="offset">offset from UTC</param>
/// <returns>User's local time</returns>
public static DateTime ConvertToLocalDateTime(this DateTime dateTime, int offset)
{
TimeZoneInfo destinationTimeZone = TimeZoneInfo.GetSystemTimeZones()
.Where(x => x.BaseUtcOffset.Hours.Equals(offset)).FirstOrDefault();
var rule = destinationTimeZone.GetAdjustmentRules().Where(x =>
x.DateStart <= dateTime && dateTime <= x.DateEnd)
.FirstOrDefault();
TimeSpan baseOffset = TimeSpan.Zero;
if (rule != null)
{
baseOffset -= destinationTimeZone.IsDaylightSavingTime(dateTime) ?
rule.DaylightDelta : TimeSpan.Zero;
}
DateTimeOffset dto = DateTimeOffset.Parse(dateTime.ToString());
return new DateTime(TimeZoneInfo
.ConvertTimeFromUtc(dateTime,
destinationTimeZone).Ticks + baseOffset.Ticks);
}
The summary basically says it all. I ran through a couple of tests with this, I'd like to know if anyone uses this and makes modifications to it!
NHibernate.Criterion Extensions workaround
I've been working with Fluent NHibernate for the past month or so, and I realized while writing an NHibernate query that the NHibernate Criterion's Restrictions class isn't very refactor-friendly. The refactorability of Fluent NHibernate is ultimately why we've decided to use it. It does take a little longer to set up than some other ORMs, but in the long-run it's nice to be able to change a property name and refactor.
... except the property names are hard-coded in the Criteria!
My workaround for this is to use a bit of static reflection a-la-FNH, and maintain code that is easily refactored.
For instance, I've created a static utility class (Showing the Restrictions.Eq() substitute):
public static class CriterionExtensions
{
public static SimpleExpression Eq<T>(
Expression<Func<T, object>> exp, object value
)
{
var memberExpression = GetMemberExpression(exp);
string propertyName = ((PropertyInfo)memberExpression.Member).Name;
if (!string.IsNullOrEmpty(propertyName))
return Restrictions.Eq(propertyName, value);
else
return null;
}
private static MemberExpression GetMemberExpression<T>(
Expression<Func<T, object>> expression
)
{
MemberExpression memberExpression = null;
if (expression.Body.NodeType == ExpressionType.Convert)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpression = expression.Body as MemberExpression;
}
if (memberExpression == null)
{
throw new ArgumentException("Not a member access", "member");
}
return memberExpression;
}
}
And to use this code, you can create an aliased using directive and call it in code:
using Ensure = MyNamespace.Extensions.Criterion.CriterionExtensions;
/* class declarations and whatnot */
internal IEnumerable<Product> GetAll(int shelfNumber)
{
var session = SessionManager.GetCurrentSession();
return session.CreateCriteria<Product>()
.Add(Ensure.Eq<Product>(x => x.ShelfNumber, shelfNumber))
.List<Product>();
}
It's pretty simple to use and is easily refactored. You may be able to add your *extensions* to a namespace: NHibernate.Criterion and call your class Restrictions, but I didn't try this (I don't like mixing namespaces). Let me know what you think!
IHttpModule Gotchas @dominicpettifer.co.uk
I came across this blog post about the Init() method of an HttpModule being called more than once. The solution is simple: create a boolean flag to indicate whether or not the app is started and an empty object to lock. Then, in the Init() method, check the flag, lock the object, then check the flag again before performing any application code.
For example:
private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();
public void Init(HttpApplication context)
{
if (!HasAppStarted)
{
lock (_syncObject)
{
if (!HasAppStarted)
{
// Run application StartUp code here
HasAppStarted = true;
}
}
}
}
The full blog post can be viewed here.
I was directed to the above post via The Morning Brew.
log4net configuration in .NET 3.5
Step 1 Add Reference
Right click on the "References" folder and choose add reference. Browse to the location of log4net and add it to the project.
Step 2 Add Config Section Reference
In web.config, add a reference to the log4net config section handler. This will look like:
<?xml version="1.0"?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/> <!-- other sections/sectionGroups --> </configSections> </configuration>
Step 3 Add the log4net configuration Section
All that is left is to add the actual configuration. There are an unlimited number of ways this can be done, with numerous different logs and log types. An example configuration follows. This configuration creates a log for NHibernate with a max size of 8MB, a rolling file (max size 3MB), and a a console log. Anything logged to the logger named NHibernate.SQL will record only if they are ERROR level or higher. Anything logged without specifying a logger name is logged to root, and only goes to the console and rolling file.
<log4net>
<appender name="NHibernateFileLog" type="log4net.Appender.RollingFileAppender,log4net">
<file value="logs/nhibernate.txt"/>
<appendToFile value="true"/>
<rollingStyle value="Size"/>
<maxSizeRollBackups value="0"/>
<maximumFileSize value="8MB"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout,log4net">
<conversionPattern value="%d [%t] %-5p %c - %m%n"/>
</layout>
</appender>
<appender name="console" type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d{ABSOLUTE} %-5p %c{1}:%L - %m%n"/>
</layout>
</appender>
<!-- log4net uses 5 levels, namely DEBUG, INFO, WARN, ERROR and FATAL. -->
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender,log4net">
<param name="File" value="logs/RollingLog.txt"/>
<param name="AppendToFile" value="false"/>
<param name="RollingStyle" value="Date"/>
<param name="DatePattern" value="yyyy.MM.dd"/>
<param name="StaticLogFileName" value="true"/>
<maximumFileSize value="3MB"/>
<maxSizeRollBackups value="0"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%d{HH:mm:ss.fff} [%t] %-5p %c - %m%n"/>
</layout>
</appender>
<logger name="NHibernate.SQL" additivity="false">
<level value="ERROR"/>
<appender-ref ref="NHibernateFileLog"/>
<appender-ref ref="console"/>
<appender-ref ref="rollingFile"/>
</logger>
<root>
<level value="ERROR"/>
<appender-ref ref="console"/>
<appender-ref ref="rollingFile"/>
<!--Uncomment the following appender for verbose output (degrades performance)-->
<!--<appender-ref ref="rollingFile"/>-->
</root>
</log4net>
Step 4
Now, all that's left is to call log4net's configure method before any other (read: **error prone**) code is called.
For example, in an ASP.NET Web application, you could add this to Global.asax:
public class GlobalAsax : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
}
}
Other logging options include database, mail, net, access.
For more information and examples on log4net, visit http://logging.apache.org/log4net/release/config-examples.html
An example Logger implementation in C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using log4net;
using log4net.Config;
namespace ETeacherWeb.Common
{
/// <summary>
/// Logger class for log4net programmatic logging
/// </summary>
public static class Logger
{
/// <summary>
/// Severity/Level of the log entry
/// </summary>
public enum LogLevel
{
DEBUG = 1,
ERROR,
FATAL,
INFO,
WARN
}
#region Members
private static readonly ILog logger = LogManager.GetLogger(typeof(Logger));
#endregion
#region Constructors
static Logger()
{
XmlConfigurator.Configure();
}
#endregion
#region Methods
/// <summary>
/// Write a string to the log with a specified level of severity
/// </summary>
/// <param name="logLevel">The severity of the log entry</param>
/// <param name="log">The log entry</param>
public static void WriteLog(LogLevel logLevel, String log)
{
if (logLevel.Equals(LogLevel.DEBUG))
{
logger.Debug(log);
}
else if (logLevel.Equals(LogLevel.ERROR))
{
logger.Error(log);
}
else if (logLevel.Equals(LogLevel.FATAL))
{
logger.Fatal(log);
}
else if (logLevel.Equals(LogLevel.INFO))
{
logger.Info(log);
}
else if (logLevel.Equals(LogLevel.WARN))
{
logger.Warn(log);
}
}
#endregion
}
}
To use the above code, you would call
Logger.WriteLog(LogLevel.DEBUG, "The expected logic failed validation");
Basics: Posting data to a remote server via .NET
Class for Remote Posting:
public class RemotePost
{
#region Fields and Properties
private string _url;
private string _postData;
private byte[] _data;
/// <summary>
/// URL to which the Post is sent
/// </summary>
public string Url
{
get
{
if (!_url.StartsWith("http://"))
return String.Format("{0}{1}", "http://", _url);
else
return _url;
}
set
{
_url = value;
}
}
/// <summary>
/// The Method to Use. Can also change to "GET"
/// </summary>
private string Method
{
get
{
return "POST";
}
}
/// <summary>
/// A string of data you want to post to a URL
/// </summary>
public string PostData
{
get
{
return _postData;
}
set
{
_postData = value;
}
}
/// <summary>
/// Pulls the string of PostData
/// </summary>
public byte[] Data
{
get
{
if (_data != null)
return _data;
else
return ASCIIEncoding.Default.GetBytes(PostData);
}
set
{
_data = value;
}
}
#endregion
/// <summary>
/// Posts to remote URL, and returns the response
/// </summary>
/// <returns></returns>
public string Post()
{
if (string.IsNullOrEmpty(Url) | string.IsNullOrEmpty(PostData))
return "Error with URL or Post Data, try again";
HttpWebRequest req =
(HttpWebRequest)WebRequest.Create(Url);
req.Method = Method;
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = Data.Length;
Stream newStream = req.GetRequestStream();
// send!
newStream.Write(Data, 0, Data.Length);
newStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Encoding enc = System.Text.Encoding.GetEncoding(1252);
StreamReader reader =
new StreamReader(resp.GetResponseStream(), enc);
string response = reader.ReadToEnd();
return response;
}
}
Usage:
RemotePost app2 = new RemotePost();
app2.PostData = "var1=encodeme&var2=encodemetoo";
app2.Url = "http://www.example.com/ProcessingPage.aspx";
txtServerResponse.Text = app2.Post();
The comments and code are pretty self-explanatory. Have fun!
Quickly create a Dictionary using Linq to Objects
When working with collections or lists, sometimes you just need to get a simple dictionary.
There are a number of ways to do this, but the ToDictionary method seems promising.
For isntance:
IList<Person> people = new List<Person>();
people.Add(new Person
{
FirstName = "Jim",
LastName="Schubert",
Age=29,
Phone="123-333-4444"
});
people.Add(new Person
{
FirstName = "Joe",
LastName="Schubert",
Age=25,
Phone="800-999-1111"
});
Dictionary<int,string> names = people.ToDictionary(k => k.Age, v => v.FirstName);
There may not be many uses for this type of syntax, but simple transformations like this make Linq fun.
UpdatePanel won’t AsycPostback for anything.
I spent a lot of time today and Friday trying to get my UpdatePanel to be handled properly. No matter what I did, my asynchronous triggers were only posting back. I searched and searched, and couldn't find anything. Then, I ran across a post on asp.net's forum which had the answer.
<xhtmlConformance mode="Legacy"/> :
Change Legacy to Strict or Transitional
Storing Generics in ASP.NET Profile object
Sometimes, I'll come across a problem that I research for an hour or two. When I find the solution, I think "Wow, that should have been the first thing I tried!"
This is one of those occasions.
I decided to use the ASP.NET profile instead of Session objects to store a list of products. So, in web.config, I tried a number of "work-arounds" that I found online, including:
<profile>
...
<properties>
<add name="RecentlyViewed" allowAnonymous="false" type="System.Collections.Generic.List`1[Model.Product]" serializeAs="Xml"/>
</properties>
</profile>
The problem I was having, and it seems a lot of people are having, is that System.Collections.Generic.List`1[Model.Product] throws an error. For some people, it seems to work. Well, I tried changing the lt and gt in the generic list so the XML would parse it as it is written in the code-behind. That didnt work.
The solution: Create a class which inherits from
List<Model.Product>
For instance:
using System;
using System.Collections.Generic;
namespace Model
{
/// <summary>
/// This class wraps the Generic List into a class to serialize in Profile provider
/// </summary>
[Serializable]
public class RecentlyViewed : List<Model.Product>
{
public RecentlyViewed()
{
}
}
}
This works beautifully! Now, in web.config, you can do the following:
<profile>
...
<properties>
<add name="RecentlyViewed" allowAnonymous="false" type="Model.RecentlyViewed" serializeAs="Xml"/>
</properties>
</profile>
