Archive for the ‘C#’ Category
September 3rd, 2011
I guess the title is a little misleading because there is seldom any fun to be had with the .NET Framework.
But if you've got a Linq to SQL *.dbml and the tool is generating those swell ExtensionDataObject properties preventing you from easily serializing your model classes to JSON and you don't want to make shadow classes (::gasp:: yes I know the .NET Fx loves ceremony, I however do not) then you can use a custom JavaScriptConverter to ignore those properties. When you have control of a class you can put a NonSerialized attribute on properties but that becomes substantially more difficult when a tool is responsible for generating the class file and cheerfully overwrites any changes you may make if you even open the file to glance at its contents. Yes shitty *.dbml tool in Visual Studio, I'm looking at you.
So let's say you have this simple helper class:
// JsonSerializationUtility.cs
public static class JsonSerializationUtility
{
public static string ToJson(this object obj, string wrapper = null)
{
var json = new JavaScriptSerializer().Serialize(obj);
if (!string.IsNullOrEmpty(wrapper))
json = "{ \"" + wrapper + "\": " + json + " }";
return json;
}
public static T Deserialize<T>(string json)
{
var jss = new JavaScriptSerializer();
jss.RegisterConverters(new [] { new ExtensionDataObjectConverter() });
var result = jss.Deserialize<T>(json);
return result;
}
}
... then you'll need this handy ExtensionDataObjectConverter class!
// ExtensionDataObjectConverter.cs
public class ExtensionDataObjectConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
return new ReadOnlyCollection<Type>(new [] { typeof(ExtensionDataObject) });
}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
return null;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return null;
}
}
July 16th, 2009
A colleague of mine and I were discussing good verb replacements for “GetOrCreate” data retrieval patterns, where the “Create” part is responsible for the instantiation of a new instance of something. While the pattern I present here did not solve his particular problem, its at least worth sharing. Its obvious, and probably in use commonly already, but I figure its worth noting.
Essentially the goal is to get away from a “GetOrCreate” master method call and just have two methods: Get(args) and Get(args, Foo default) and then leave the responsibility of default generation up to Foo. This way there’s no unintended side effects and you don’t have to explicitly coalesce (though with this pattern you could as easily coalesce; strictly speaking it isn’t as “discoverable”)…
var foo = FooService.Get(23) ?? new Foo { ... };
So here’s the pattern defined in code:
public class Foo : ICloneable
{
private readonly static Foo __default =
new Foo { Bar = "...", Baz = Int32.MinValue };
public string Bar { get; set; }
public int Baz { get; set; }
public static Foo Default()
{
return (Foo)__default.Clone();
}
public object Clone()
{
return new Foo { Bar = this.Bar, Baz = this.Baz };
}
}
public class FooService
{
public Foo Get(int baz)
{
// retrieve from data store...
return null;
}
public Foo Get(int baz, Func<Foo> @default)
{
return Get(baz) ?? @default.Invoke();
}
}
You’ll see that any associated overhead with Clone only occurs if Get(baz) returns null. I have specifically stepped around the common naming standard of “GetDefault()” for the function – its named like a property - because its intended usage is as follows:
var foo = new FooService().Get(12, Foo.Default);
An ICloneable reference type is pretty much required, though any immutable reference type could get by without cloning because any changes gives you a new copy of that type.
April 29th, 2009
Just making sure this is correct. By this I mean PreCode (requires Windows Live Writer 2009) with SyntaxHighligher setup. PreCode is also a stand alone program.
public bool Validate(IValidationDictionary modelState, string prefix)
{
// xVal example code
var dataAnnotationErrors = from prop in TypeDescriptor.GetProperties(_entity).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(_entity))
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), _entity);
var brokenRules = GetBrokenRules();
if (!String.IsNullOrEmpty(prefix))
prefix += ".";
if (dataAnnotationErrors.Any())
dataAnnotationErrors.ForEach(ei => modelState.AddError(prefix + ei.PropertyName, ei.ErrorMessage));
if (brokenRules.Any())
brokenRules.ForEach(rule => modelState.AddError(prefix + rule.Property, rule.Message));
return modelState.IsValid;
}
There is a “Fix Indentation” button in PreCode. I am in love.
Now just to setup the clipboard SWF thing.
I should seriously consider a theme with a wider content area.
March 30th, 2009
The Lightweight Test Automation Framework is a browser automation framework like WatiN and Selenium RC that allows you to create automated integration tests. Its a beta release from MS and can be found on Codeplex at:
http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=22739
I first read about it from Steve Sanderson's blog where he describes how to use it with an ASP.NET MVC project.
I am supporting a legacy web forms application and have a typical web application setup so I'll explain what I did to get up and running in a matter of minutes with LTAF.
- Download LTAF; you can use the "... for ASP.NET Samples" link and pull the *.dll out of the Bin folder and the contents of the Test folder, or you could build from source using the "... for ASP.NET Source"
- Create a new Class Library project in your solution and name it appropriately - Company.Tests.Integration.Site - for example
- For this project's properties alter the output assembly's name to begin with App_Code - this will slightly speed up web test load times and can help solve problems you may experience if your web application references tons of assemblies, i.e. App_CodeCompany.Tests.Integration.Site
- Add a reference to Microsoft.Web.Testing.Light.dll to your Company.Tests.Integration.Site project
- Copy and add to your web project (Company.Site) the Test folder; you can name the folder anything you want and you don't strictly have to add it to your project but I did for convenience for Start > Debug
- Remember to alter your build process to remove the Test folder for production releases
- Alter the web.config file for your Company.Site to allow anonymous access to the Test folder:
<location path="Test">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
- Again, remember to alter your build process to eliminate this node from your production web.config
- Add a reference to your integration test project, Company.Tests.Integration.Site, to your web application, Company.Site
- Let's test the login page (here I assume that "Login.aspx" is the name of your login page and its in the root of the application and you've used an <asp:Login /> control)...
- Create a new class LoginTests in your Company.Tests.Integration.Site project and eliminate the file's contents - it is very important that all tests exist outside of a namespace
- Paste this code into the body of your LoginTests.cs file:
using Microsoft.Web.Testing.Light;
[WebTestClass]
public class LoginTests
{
[WebTestMethod]
public void SignInAndSignOut()
{
var page = new HtmlPage("Login.aspx");
page.Elements.Find("UserName").SetText("someuser");
page.Elements.Find("Password").SetText("yourPW");
page.Elements.Find("LoginButton").Click(WaitFor.Postback);
// you'll need to determine what a good landmark for your
// application is upon successful login
var logoutLink = page.Elements.Find("LogoutHyperlink");
Assert.AreEqual("[Logout]", logoutLink.GetInnerText());
logoutLink.Click(WaitFor.Postback);
Assert.IsNotNull(page.Elements.Find("LoginButton"));
}
}
- Set your Company.Site as the startup project if its not already, and Tests/Default.aspx as the startup page; run from Visual Studio
Magic! This is my first use of a browser automation framework for integration testing and while it has its pitfalls, it does work and looks very promising.
Notes
- Looks like all your integration tests must exist in the same assembly
- Source code for LTAF is available so making this a configuration option is a possibility
- All tests must exist outside of namespaces
- Prefixing your integration tests assembly with App_Code can help work around assembly loading problems as the test runner searches for WebTestClass attributes
- Remember to have your production build process eliminate the Tests folder and associated integration test assembly
January 8th, 2009
So I had written a SmtpClientWrapper for System.Net.SmtpClient because the aforementioned class implements no interface making it impossible to perform dependency injection using it (as anyone who has to unit test any sort of email functionality can attest to). Then I had the idea that during production we may want to mirror email messages sent to a debugging email address so I also made a MirrorSmtpClientDecorator. Herein lies the problem - System.Net.MailMessage does not implement ICloneable and the MirrorSmtpClientDecorator.Send(MailMessage) either needs a copy or its going adversely affect MailMessage with a side affect (by altering the MailMessage.To MailAddressCollection for a second message sent to the debugging email addresses). I explored numerous ways to copy MailMessage and eventually I resorted to some brute force extension methods (to ObjectModel.Collection<T>; ShallowCopy and Replace, suck I know) but during my research I discovered some older MemoryStream serialization I refactored and felt its worth sharing.
The problem with this approach is that the class you want to deep copy requires that it be marked with the SerializableAttribute. I've noticed that a portion of my time is spent writing hackarounds for classes in the .NET BCL and MailMessage is no exception - yup no ICloneable and no SerializableAttribute either.
public class Serializer
{
public static void Serialize(Stream target, object source)
{
var formatter = new BinaryFormatter();
formatter.Serialize(target, source);
}
public static T Deserialize<T>(Stream source) where T : class
{
var formatter = new BinaryFormatter();
var obj = (T)formatter.Deserialize(source);
return obj;
}
}
public class MemorySerializer<T> : IDisposable where T : class
{
#region Member Variables
private readonly MemoryStream _ms;
#endregion
#region Constructors
public MemorySerializer(MemoryStream ms)
{
_ms = ms;
}
public MemorySerializer()
{
_ms = new MemoryStream();
}
#endregion
#region Methods
public MemorySerializer<T> Serialize(T obj)
{
Serializer.Serialize(_ms, obj);
_ms.Position = 0;
return this;
}
public T Deserialize()
{
return Serializer.Deserialize<T>(_ms);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
if (_ms == null)
return;
try
{
_ms.Close();
}
finally
{
_ms.Dispose();
}
}
#endregion
}
public static class ObjectUtility
{
/// <summary>
/// Creates a deep copy of an object for any object which
/// supports binary serialization.
/// </summary>
/// <typeparam name="T">Type to serialize/deserialize.</typeparam>
/// <param name="source">Object to deep copy.</param>
/// <returns>New instance of the object with all properties and
/// fields that support serialization copied.</returns>
/// <remarks>This method isn't as fast as a provided Clone method
/// but when ICloneable is not implemented on a BCL class its a
/// pretty close substitute.</remarks>
public static T DeepCopy<T>(this T source) where T : class
{
T ret;
using (var ms = new MemorySerializer<T>())
{
ret = ms.Serialize(source).Deserialize();
}
return ret;
}
}