Archive

Archive for the ‘.net 2.0’ Category

Rendering the Web Equally on Mobile Devices

June 26, 2009 Comments off

I’ve been digging through the Interwebs for a while now and, I thought, had worked out all of the “kinks” of rendering on a mobile device—specifically iPhones.

The special ‘viewport’ meta tag means the world to iDevices.

meta name=“viewport” content=“width = device-width” />

I’m faced with a new challenge—the Palm Pre’s built-in web browser.  My shiny new phone is great, but it isn’t without glitches.

The first glitch I’ve found appears to be a DNS issue— http://myserver/web won’t resolve; however, http://123.45.67.89/web will.  It seems to be touchy.  Most of our webs work just fine, others don’t.  I haven’t narrowed it down to a single server or architecture as it seems to be a bit of everything.  Wonky.

The next glitch is more important—the rendering.  One of our tools is a simple form-based tool that looks great on the iPhone; however, renders partial screen and “garbles” when you move around the screen.

Palm Pre:

Garbled image

iTouch/iPhone:

I’ve also found that anything in an ASP.NET Update Panel (like those Select buttons) are unusable.  Other webs I’ve used (Bank of America, etc) use AJAX just fine, so I don’t think it’s that—probably a coding issue I need to dig into and resolve.

UPDATE: Explicitly adding “LoadScriptsBeforeUI=’true’” to the ASP.NET ScriptManager seems to help with this.. a little.

Anyone else worked specifically with the Pre devices and rendering?  I’d appreciate any meta tags or layout ideas that worked. ๐Ÿ™‚  The Pre isn’t a common device in our organization—yet.

Fluent NHibernate Repository of… integers?

April 21, 2009 Comments off

I’d like to preface this by the fact that this “works” doesn’t mean it “should”.  If there’s a proper way to do this, I’m all ears. ๐Ÿ˜€

I recently needed to do some revamp to an application that queried lookup data from another data system.  The system had a collection of composite keys (age and total score) that returned a percentile score.  Easy enough; however, there are a couple dozen of these tables and I didn’t want to create a couple dozen domain objects/repositories for a SINGLE query.

Typically, the NHibernateRepository* takes a type parameter that matches to the mapped object (and provide the proper return type); however, in this case, I didn’t have a type to return, simply an integer.  So why wouldn’t that work?

public class ScoreRepository : NHibernateRepository<int>, IDisposable

With that in place, I can now add a query into Session:

public int GetConceptPercentile(int age, int total)

{

var result =

       Session.CreateSQLQuery(

“select perc from tblConcept where age = :age and total = :total”

             .SetInt32(“age”, age)

             .SetInt32(“total”, total)

.UniqueResult().ConvertTo<int>();

 

return result;

}

A few more of those, and our test looks like:

[Fact]

public void GetPercentiles_For_Student()

{

using (var repository = new ScoreRepository())

       {

              var languagePercentile =

             repository.GetLanguagePercentile(ageCalc_72months.TotalMonths, 18);

            

var motorPercentile =

             repository.GetMotorPercentile(ageCalc_72months.TotalMonths, 18);

            

var conceptPercentile =

             repository.GetConceptPercentile(ageCalc_72months.TotalMonths, 18);

 

             languagePercentile.ShouldBeEqualTo(12);

             motorPercentile.ShouldBeEqualTo(17);

             conceptPercentile.ShouldBeEqualTo(10);

}

}

Everything “appears” to be working; however, the extraneous methods that each NHibernateRepository includes (Get, GetAll, FindAll, etc) are defunct and just sitting there—very messy.

So is there a better way to use NHibernate/Fluent NHibernate WITHOUT mapping objects—those “lookup tables”?

Auto Incrementing Visual Studio Project Versions

March 9, 2009 Comments off

I’m not using NAnt or anything fancy for most of my projects—so I needed a simple, MSBuild way to automate my version numbers in a project..

<tangent>
HOLY CRAP! Why isn’t this built into Visual Studio Pro?
</tangent>

Here we go:

1. Download the latest build of AssemblyInfoTask (download here) (was 1.0.51130.0 for me).  This is a semi-Microsoft supported MSBuild task that gives you a lot of flexibility over your AssemblyInfo.cs files.

2. Install AssemblyInfoTask.  When prompted where—install into the GAC.  If you don’t have access to the GAC on your workstation, then why aren’t you developing on a VM? ๐Ÿ˜‰

3. Locate the Microsoft.VersionNumber.targets file.  If you installed to the GAC, it should be at %ProgramFiles(x86)%\MSBuild\Microsoft\AssemblyInfoTask Or %ProgramFiles%\MSBuild\Microsoft\AssemblyInfoTask (depending on your architecture).

4. Copy the Microsoft.VersionNumber.targets file into a location in your solution or project.  I recommend $(SolutionDir) so you can share it amongst all of your projects.  The guilde recommend pointing to the file directly; however, you can’t modify the base Major versions that way (without setting the same major version for ALL projects you ever work on).  You can also rename it as approprate.

“Int16s Are Too Small” Or “Why 2007 Broke Versioning” Fix

According to experts who are much smarter than me, the build version numbers are Int16s—meaning 65535 caps out the number.  Unfortunately, the year 2007 breaks this (070101 or 70101 for 07 jan 01) doesn’t fit within an Int16.  Stellar.

The MSBuild team recommended taking out the year and simply placing a 1 infront of it.  That works; however, I really like having the year in there somewhere.

For me, I’ve placed the year into the MinorVersion.  After reviewing most of our practices, the minor version for most of our projects changes with annual maintenance OR not at all (we bump the major version).  This, if nothing else, will help standardize when it changes. ๐Ÿ™‚  As always, YMMV.

No matter which solution you choose, you’ll need to remove the year from the BuildNumberFormats.

In your Targets file, you can change the two lines to report out the MMdd (0309, for example, today) to work around the bug.  I’ve bolded the two lines below.  As you can see, I also added the “9” to the MinorVersion to represent 2009. 

<PropertyGroup>

  <AssemblyMajorVersion>3</AssemblyMajorVersion>

  <AssemblyMinorVersion>9</AssemblyMinorVersion>

  <AssemblyBuildNumber></AssemblyBuildNumber>

  <AssemblyRevision></AssemblyRevision>

  <AssemblyBuildNumberType>DateString</AssemblyBuildNumberType>

  <AssemblyBuildNumberFormat>MMdd</AssemblyBuildNumberFormat>

  <AssemblyRevisionType>AutoIncrement</AssemblyRevisionType>

  <AssemblyRevisionFormat>00</AssemblyRevisionFormat>

</PropertyGroup>

 

<!– Properties for controlling the Assembly File Version –>

<PropertyGroup>

  <AssemblyFileMajorVersion>3</AssemblyFileMajorVersion>

  <AssemblyFileMinorVersion>9</AssemblyFileMinorVersion>

  <AssemblyFileBuildNumber></AssemblyFileBuildNumber>

  <AssemblyFileRevision></AssemblyFileRevision>

  <AssemblyFileBuildNumberType>DateString</AssemblyFileBuildNumberType>

  <AssemblyFileBuildNumberFormat>MMdd</AssemblyFileBuildNumberFormat>

  <AssemblyFileRevisionType>AutoIncrement</AssemblyFileRevisionType>

  <AssemblyFileRevisionFormat>00</AssemblyFileRevisionFormat>

</PropertyGroup>

 

This results in a version string that looks like 3.9.0309.{increment}.

5. Open up your project’s solution and unload the project you are wanting to auto-increment. Towards the end of the file, you’ll see the default MSBuild C# build path; add the location to your new .targets file in your solution directory.

<Import Project=$(SolutionDir)MyProject.VersionNumber.targets />

7. Save and Close and Reload the project.

8. Build/Rebuild your project and the AssemblyInfo.cs should set to the specified increment scheme.

You’re done!

“Too Many WebResources?” Fix 

My project references numerous resources for images and style sheets; however, having these inside of AssemblyInfo.cs seems to cause it to go haywire and throw array errors (assumingly because there is more than one [assembly:WebResource()] call).

To fix this, I moved my WebResources out of AssemblyInfo.cs and into a new file under Properties called WebResources (Add New Item > Assembly Information File).  Strip out everything except the WebResources you copy in and the project now compiles like a champ.

For additional setup details and options within the .targets files, the AssemblyInfoTask installer comes with a CHM help file that covers additional customizations available.

Using Multiple NHibernate Session Factories

March 6, 2009 1 comment

A few months ago, I rewrote a few of my base repositories to follow some of the archtecture practices of the S#arp Architecture project.  I liked how easy it was to setup NHibernate*, mark domain objects, and setup mappings and highly recommend checking out their project.  Unfortunately, the whole project was a bit too bulky for what I needed and much of the functionality already existed in existing framework libraries we use at the office.

* When I mention NHibernate, I’m using Fluent NHibernate, currently at build 366 with a bit of modification to allow Oracle data access.  I’ve also enhanced the S#arp Architecture project to take advantage of the latest feature changes in Fluent NHibernate.

One pitfall, however, that I recently ran into was needing to access NHibernate multiple session factories at the same time—such as to access PeopleSoft and our internal student system to pull in demographic information.  The S#arp web session storage, by default, uses a single key opening a separate session overwrites the prior.

I found an open Issue on the S#arp tracker (#12) for such a functionality and a bit of code from Russell Thatcher to allow multiple sessions; however, I could never get it fully working—so I set off to create my own.

Updating the WebSessionStorage

To address the issue of storing multiple sessions in the HttpContext’s session state, the keys must be uniquely named when created.  To do this, I replaced the previous constructor of WebSessionStorage(HttpApplication) and added an additional parameter to create WebSessionStorage(HttpApplication, string).

This appends a name to the session key used to store the NHibernate ISession.

private readonly string CurrentSessionKey = “nhibernate.”;

 

public WebSessionStorage(HttpApplication application, string factoryKey)

{

       application.EndRequest += Application_EndRequest;

       CurrentSessionKey += factoryKey;

}

Everything else in WebSessionStorage remains the same.

NOTE: This ensures that we can find the ISession within the ASP.NET context using WebSessionStorage and isn’t required if you’re managing the the ISession yourself or using another mechanism for storage.

Updating the NHibernateSession

Since we’re looking to support multiple, keyed sessions, the NHibernateSession’s private fields of SessionFactory and SessionStorage should be changed into keyed Dictionaries.

private static readonly Dictionary<string, ISessionFactory> SessionFactories =

       new Dictionary<string, ISessionFactory>();

 

private static readonly Dictionary<string, ISessionStorage> SessionStorages =

       new Dictionary<string, ISessionStorage>();

To use these new dictionaries, we replace the change the logic in the Init method to add the factory and storage to the dictionary, rather than assign to a single variable.

public static FluentConfiguration Init(

       this FluentConfiguration configuration,

       ISessionStorage storage,

       string factoryKey)

{

       Check.Require(storage != null,

              “The session storage was null, but must be provided.”);

       Check.Require(!string.IsNullOrEmpty(factoryKey),

               “A unique key for the session factory was null, but must be provided.”);

 

       try

       {

              if (!SessionFactories.ContainsKey(factoryKey))

             {

                     SessionFactories.Add(

                           factoryKey,

                           configuration.BuildSessionFactory());

                    

                     SessionStorages.Add(

                           factoryKey,

                           storage);

 

                     return configuration;

              }

       }

       catch (HibernateException ex)

       {

              throw new

                     HibernateException(

                           “Cannot create session factory.”, ex);

       }

 

       return configuration;

}

Our Init statement, when called, then looks like, passing along the factory key with the ISessionStorage.

configuration.Init(storage, “FactoryKeyNameGoesHere”);

The Current static public property also needs to address the new Dictionary; however, cannot as a property as it needs the parameter of the factory key.  I converted this into a method that accepts the factory key and returns an ISession based on what it finds.  If it cannot find an open sesson (is null) in the dictionary, it opens a new one, sets it in the dictionary, and returns it to the caller.

public static ISession Current(string factoryKey)

{

       var session =

              SessionStorages[factoryKey].Session ??

              SessionFactories[factoryKey].OpenSession();

 

       SessionStorages[factoryKey].Session = session;

      

       return session;

}

That’s it in the NHibernateSession.

Updating the Repository & Decorating Inheriting Repositories

The Repository base class provides functionality to the CRUD methods through a protected Session property.  This property needs to be updated to call the new Current(string) method.  However, since the Repository doesn’t know, we need to find a way to communicate the “key” that a repository uses to ensure the correct session is accessed (so, for example, that our PeopleSoft queries don’t try to hit our SAP session—that confuses it. *grin*)

Randall had a great solution for this—an attribute applied to repositories that simply provides a place to store the “name” of the session.

[AttributeUsage(AttributeTargets.Class)]

public class SessionFactoryAttribute : Attribute

{

       public readonly string FactoryKey;

 

       public SessionFactoryAttribute(string factoryKey)

       {

              FactoryKey = factoryKey;

       }

 

       public static string GetKeyFrom(object myClass)

       {

              var key = string.Empty;

 

              //Get member info via reflection

              var info = myClass.GetType();

 

              //Retrieve custom attributes

              var attributes =

                     info.GetCustomAttributes(

                           typeof (SessionFactoryAttribute),

                           true);

 

              //Check for attribute

              if (attributes.Length > 0)

              {

                     var att =

                           (SessionFactoryAttribute) attributes[0];

                     key = att.FactoryKey;

              }

             

              return key;

       }

}

Now we simply decorate the inheriting repositories with SessionFactory.  Notice how the key matches what we used in the Init statement earlier.

[SessionFactory(“FactoryKeyNameGoesHere”)]

public class CourseRepository :
       NHibernateRepository<Course>, ICourseRepository, IDisposable { }

For a bit better design, you can also create a constant called, perhaps, FactoryKey or {data}FactoryKey and use that for the attributes and Init statement.  Then, if it needs changed—change it in one place.

Inside our Configuration class (PSConfiguration, for example), we have:

public const string FactoryKey = “PSProd”;

and

configuration.Init(storage, PSConfiguration.FactoryKey);

and then decorating our Repository classes:

[SessionFactory(PSConfiguration.FactoryKey)]

Now, to take advantage of our new SessionFactory attribute, we modify the Session property on our Repository base class.

protected virtual ISession Session

{

       get

       {

              var factoryKey = SessionFactoryAttribute.GetKeyFrom(this);

              return NHibernateSession.Current(factoryKey);

       }

}

Now the interface implementations (Get, Find, etc) are fetching the correct Session.

In summary, I’ve enabled accessing multiple NHibernate Session Factories from the S#arp Architecture framework (or my small implementation of it) by…

  • Updating the WebSessionStorage to accept a factory key when the session is created.
  • Updating the NHibernateSession to store multiple Factories and Storages in dictionaries and then access these dictionaries when the “current” session is requested.
  • Adding an attribute to Repositories to “name” which factory a certain repository should be selecting out of and then providing that session when called.

So far, things are working like a champ—tweaking and refactoring from here. ๐Ÿ™‚

HTML Formatting and Spacing in ActiveReports

March 6, 2009 1 comment

ActiveReport’s RichTextBox is great for presenting HTML-styled content on PDF reports; however, there seems to be a snag with a few of the HTML tags and how the RichTextBox translates the commands to HTML.

To print out a bit of HTML (from a field), I have the following StringBuilder my details_Format:

builder

       .AppendFormat(

       @”{0}On {1}, <strong>{2}</strong> wrote: <br/>{3}<br/><br/>{4}”,

       […]);

However, the <strong> tags are not properly rendering spaces between them:

Issues with Spacing

There seems to be an existing notice on the DataDynamics forums (here) regarding the spacing, but it’s from 2007–-I’d assume it’s been fixed by now; however, I can’t find it in any of the AR3.0 release notes.

So what’s the fix?

Force the spaces in with non-breaking space commands.

       @”{0}On {1},&nbsp;<strong>{2}</strong>&nbsp;wrote: <br/>{3}<br/><br/>{4}”,

Bingo!

Fix With Spacing

Part 2: Converting Generic Collections to CSVs

February 6, 2009 1 comment

Part 1 | Part 2

In my last post, I wrote up the tests needed to meet our customer’s requirements, now I’m ready to flush out the tests.

Objective #1 – Export Collection to CSV With All Properties

Each of my tests implement one of two extension methods, ToDelimitedFile and ToDelimitedRow.  Collections focus on File whereas objects (singles) use the Row. 

Unfortunately, I couldn’t find a 100% accurate way to use the generic constraints—too bad we can’t put in “where T : !ICollection<T>”  Hence the separate methods.

For me, extension methods sit outside of my actual classes in their own happy static {whatever}Extensions class.  The CSV Conversions are no different.

For Test #1 to pass, we need to implement the extension method.

public static string ToDelimitedFile<T>(this ICollection<T> convertible)

{

       return DelimitedExporter.SerializeCollection(convertible);

}

This paves the way for creating our next class and method, DelimitedExporter and SerializeCollection.  The DelimitedExporter class will contain all of the methods and logic for converting our collections into CSV “lines”.  Let’s implement SerializeCollection real quick with some dummy data and rerun the test.

public static string SerializeCollection<T>(ICollection<T> convertible)

{

       return “Name,Level”;

}

Pass!  Test passes!

Now, of course, since the test passes, we’re ready to flush out the details of the SerializeCollection method.

Creating the Header Rows

Since we know we’ll be working with strings and doing a large number of appends, let’s start out by creating a StringBuilder to hold all of that text information.

public static string SerializeCollection<T>(IEnumerable<T> convertible)

{

       var stringBuilder = new StringBuilder();

 

       //grab properties of the method to create columns

       var columns = typeof(T).GetProperties();

 

GetProperties is a method on Type that returns all public properties as PropertyInfo objects.  These properties will be used to create the column header names for our CSV.

       // build header row

       stringBuilder.Append(BuildHeaderRowFrom(columns));

 

So let’s “build” our header row.  I’ve extracted this into a separate method from the start—you could have one mega method and refactor it down later.

 

private static string BuildHeaderRowFrom(IEnumerable<PropertyInfo> columns)

{

       var headerString = string.Empty;

       var counter = 0;

       foreach (PropertyInfo column in columns)

       {

              counter++;

             headerString += column.Name;

             if (counter != columns.Count)

                     headerString += “,”;

       }

       headerString += Environment.NewLine;

 

       return headerString;

}

The BuildHeaderRowFrom method takes the PropertyInfo enumeration from our GetProperties() call and iterates through it, capturing the Name of each property and appending it to a string.

Let’s close up the SerializeCollection() method …

       return stringBuilder.ToString();

}

… and run the test again and everything should pass up to this point.

Now we can focus on the data rows.

Creating the Data Rows

Our next task in SerializeCollection is to flush out how each enumerable object will be converted into a row in our CSV.

After our BuildHeaderRowsFrom() method, let’s now append the data rows with:

       // build item rows

       stringBuilder.Append(BuildDataRowsFrom(collection, columns));

BuildDataRowsFrom will take the original collection of objects (an IEnumerable<T>) and the columns we parsed for the header as parameters.

private static string BuildDataRowsFrom<T>(

       IEnumerable<T> collection,

       IEnumerable<PropertyInfo> columns)

{

       var rowString = string.Empty;

       foreach (T item in collection)

       {

              int counter = 0;

              var columnCount = columns.Count();

             foreach (PropertyInfo column in columns)

             {

                     counter++;

                     var value =

                           column.GetValue(item, null) ?? string.Empty;

 

                     // Append column value to the row.

                     rowString += “{0}”.AsFormatFor(value);

 

                     // Append delimiter if _not_ the last column.

                     if (counter != columnCount)

                           rowString += “,”;

              }

              rowString += Environment.NewLine;

       }

       return rowString;

}

Note: I could use a StringBuilder inside of this; however, the StringBuilder seemed to take a bit longer with the tests of 50,000ish records (max in the system I was designing this for).  A later revision may be to further examine ways to improve performance.

 

So, what’s all this do?

 

For the most part, it reads as a sentence:  For each item in the collection, loop through each known column and fetch the value from that item.  If the item is null, simply return an empty string. 

 

Since I’m using IEnumerable<T> rather than ICollection<T>, I don’t have access to a Count property (without calling the Linq extension method, so I’ve moved that outside of the foreach loop.  Perhaps there’s a better generic collection to be using, but that’s for later revisions.

 

Again, run our test again, and it should pass AND we should now have data.

 

can_convert_list_of_objects_to_csv : Passed

*** ConsoleOutput ***
Name,Level
SchoolA,Elementary
SchoolB,Elementary
SchoolC,Elementary
SchoolD,Elementary

 

Excellent.

 

That’s it for this post.  In the next part, I’ll start in on the second objective—being able to specify which parameters are picked up by the DelimitedExporter.

 

 

Part 1: Converting Generic Collections To CSVs

February 6, 2009 1 comment

I recently had a request to allow “exports” of lists from one application to the user in CSV (comma separated value) format.  After a bit of dinking, here’s what I came up with; however, I’m sure a bit more work will find that it’s overkill. ๐Ÿ™‚

Starting Off With The Tests

So what did I want to accomplish?  I had three objectives:

  1. Export a collection of objects (IList, ICollection, IEnumerable, etc) to CSV with all “properties”.
  2. Export a collection and be able to specify the properties to export.
  3. (optional) Export a single object to CSV with the same two requirements above (1, 2).

#1:

[Fact]

public void can_convert_list_of_objects_to_csv()

{

       var repository = new SchoolRepository();

       var schools = repository.GetBySchoolLevel(Level.Elementary);

 

       var csvOutput = schools.ToDelimitedFile();

 

       // check that the CSV has the headers

       // of the properties of the object

       csvOutput.ShouldContain(“Name,Level”);

}

#2:

[Fact]

public void can_convert_list_of_objects_to_csv_with_parameters()

{

       var repository = new SchoolRepository();

       var schools = repository.GetBySchoolLevel(Level.Elementary);

      

       var csvOutput = schools.ToDelimitedFile(“Name”);

       csvOutput.ShouldContain(“Name”);

 

       Console.Write(csvOutput);

}

#3:

[Fact]

public void can_convert_a_single_object_to_csv()

{

       var repository = new SchoolRepository();

       var school = repository.Get(120);

 

       var csvOutput = school.ToDelimitedRow();

       csvOutput.ShouldContain(“Great School,High”);

 

       Console.Write(csvOutput);

}

In the next post, we’ll begin flushing out our tests.