Deploying Assemblies to the GAC

Posted 15 May 2013, 14:50 | by | Perma-link

Pulling together a couple of helpful posts into one place so that I can find it later...

I wanted to deploy an assembly to the Global Assembly Cache (GAC) on a Windows 2008 server - GacUtil isn't neccessary installed, and I don't really want to install the entire SDK on to the server just to get it.

PowerShell to the rescue!

The first thing I needed to do was enable PowerShell to load .Net 4 assemblies, this was done by adding a config file into the PowerShell home directory (run $pshome from a PowerShell console), with the following contents:

<configuration>
  <startup uselegacyv2runtimeactivationpolicy="true">
    <supportedruntime version="v4.0.30319" />
    <supportedruntime version="v2.0.50727" />
  </startup>
</configuration>

You can then run the following commands to load the 4.0 instance of EnterpriseServices, which will install your libraries into the correct GAC depending on it's target runtime:

[System.Reflection.Assembly]::LoadWithPartialName("System.EnterpriseServices")
$publish = New-Object System.EnterpriseServices.Internal.Publish
$publish.GacInstall("C:\PathTo\Your.Assembly.dll")

Filed under: .Net

Implementing Flickr.Net

Posted 26 April 2010, 15:27 | by | Perma-link

Flickr.NET is a ".NET library for accessing the Flickr API". It's hosted on CodePlex, and (thankfully for me) updated to version 3.0 Beta just before I downloaded it.

I host all my images here on the site, provide RSS feeds of both the latest photos and each album, however beyond using a service like twitterfeed I'd not found a nice way to integrate it into other social networks - Facebook can only accept one blog feed directly and while Google Buzz picked up on the blog feed through the "Connected Sites" options (I guess through the WebMaster tools association) it only seems to accept the first feed it finds, so I thought if I post the images to Flickr as well, then there would be more integration for them.

So, after spending a little time nosing around the Flickr API docs, I went off and got the latest version of Flickr.NET, dropped it in my projects /bin folder, and added a reference to it.

The next step was to request an API key from Flickr - you'll want a different key for each application you produce as the query limits are per key - and then set up the application - you need to give it a description, pick your application type (Desktop, Web or Mobile) and then define how you're handling authentication, for a web application this means supplying a callback url - while testing http://localhost/ addresses (with port numbers if required) worked fine.

Flickr.NET comes with it's own configuration section that you could use to set up Flickr.NET:

<flickrNet apiKey="APIKEY" secret="SHAREDSECRET" cacheDisabled="true">

Which then allows you to call the library as:

var flickr = new FlickrNet.Flickr();

However, I found that this didn't play nicely in a Medium Trust environment (the main disadvantage of shared hosting), as the constructor threw an exception complaining that the it couldn't write to the cache location (even though it's configured to be disabled) - I'll admit now that I didn't look too hard into diagnosing this issue - I'm only submitting images, rather than downloading them, so I didn't feel much need for caching.

So I pulled the API Key and Shared Secret out into the main appSettings settings, and factored out the constructor into a simple factory class:

public class FlickrControl
{
  internal static Flickr GetFlickr() {
    // Disable Cache before calling anything else
    Flickr.CacheDisabled = true;

    // Create Flickr instance with simple constructor.
    var flickr = new Flickr(WebConfigurationManager.AppSettings["FlickrApi"],
                            WebConfigurationManager.AppSettings["FlickrSecret"]);

    return flickr;
  }
}

I then created two new controllers - one to handle the initial login request, and one to handle the response back from Flickr:

// User wants to authenticate with Flickr
public ActionResult Flickr() {
  // Store calling page in session to return the user to later
  Session["Referrer"] = null != Request.UrlReferrer ?
                       Request.UrlReferrer.PathAndQuery : string.Empty;

  // Get an instance of Flickr
  var flickr = FlickrControl.GetFlickr();

  // Redirect the user to Flickr authentication service
  // asking for Delete priviledges (so we can remove images).
  return new RedirectResult(flickr.AuthCalcWebUrl(AuthLevel.Delete));
}

// Flickr is returning a logged in user
public ActionResult FlickrReturn(string frob) {
  var flickr = FlickrControl.GetFlickr();

  // Generate an Auth Token from the "frob" returned from Flickr
  var auth = flickr.AuthGetToken(frob);

  // Store the authentication token for later use
  // ExternalSites.FlickrAuth is a static string value for finding this object.
  Session[ExternalSites.FlickrAuth] = auth;

  // See if we can find the users previous request to return them to.
  var referrer = Session["Referrer"] as string;

  if (!string.IsNullOrEmpty(referrer)) {
    // We found their previous page, bounce them back
    return new RedirectResult(referrer);
  }

  return RedirectToAction("Index");
}

Then when a user uploads, edits or deletes an image from the site, we check to see if they are authenticated with Flickr, and perform the same action on Flickr:

// Inside the Upload action
// Check to see if the user is Authenticated, and that they want to upload the image
if (null != Session[ExternalSites.FlickrAuth] && editPhoto.UploadToExternal) {
  var auth = Session[ExternalSites.FlickrAuth] as Auth;

  if (auth != null) {
    var flickr = FlickrControl.GetFlickr();
    // Add the user's auth token to our Flickr instance
    flickr.AuthToken = auth.Token;

    // See below
    FlickrControl.UploadImage(photo, flickr, ImageData.FileName);

    // Upload image adds the Flickr id to the photo, so we need to save
    // that to the image as well.
    m_PhotoRepository.Save();
  }
}

This is the method for uploading an image:

internal static void UploadImage(DoodlePhoto photo, Flickr flickr, string fileName) {
  if (null == photo.PhotoDetail) {
    // If editing a photo, the image won't be in memory, so needs to be loaded.
    photo.PhotoDetailReference.Load();
  }

  if (null == photo.Album) {
    // If editing a photo, the Album info won't be in memory, so needs to be loaded.
    photo.AlbumReference.Load();
  }

  // UploadPicture returns the id of the image on Flickr
  photo.FlickrId = flickr.UploadPicture(
              // Raw stream of image bytes
              new MemoryStream(photo.PhotoDetail.BytesOriginal.ToArray()),
              // might as well pass in string.Empty here,
              // not really needed as we're passing in a stream
              fileName,
              // The title that's displayed in Flickr
              photo.Caption,
              // The description that appears under the image in Flickr
              photo.Description,
              // The tag list
              photo.CollapsedTags,
              // Set the permissions on the Album - if it's public,
              // whether Family or Friends can see it.
              photo.Album.IsPublic, true, true,
              // What type of image it is (they are effectively all photos).
              ContentType.Photo,
              // I'm not uploading anything inappropriate
              SafetyLevel.Safe,
              // What search level should be used
              photo.Album.IsPublic
                ? HiddenFromSearch.Visible
                : HiddenFromSearch.Hidden);

  // Once the image is uploaded, we need to add it to the a photo set as well.
  addPhotoToPhotoset(photo, flickr);
}

To update an existing image on Flickr I use the following method:

internal static void UpdateImage(DoodlePhoto photo, Flickr flickr) {
  // Update the metadata for an existing image
  flickr.PhotosSetMeta(photo.FlickrId, photo.Caption, photo.Description);
  // Replace all tags for an existing image
  flickr.PhotosSetTags(photo.FlickrId, photo.CollapsedTags);

  // Check to see if we've created this photoset already
  if (string.IsNullOrEmpty(photo.Album.FlickrPhotoSetId)) {
    // Photo set doesn't exist, therefore we can safetly add the image
    addPhotoToPhotoset(photo, flickr);
  } else {
    // Photoset already exists, so we need to check if the image is
    // not already in the photoset, and add it if needed
    if (!photoSetContainsPhoto(photo, flickr)) {
      addPhotoToPhotoset(photo, flickr);
    }
  }
}

To add a photo to a photoset:

private static void addPhotoToPhotoset(DoodlePhoto photo, Flickr flickr) {
  // Check to see if we've already created a photoset
  if (String.IsNullOrEmpty(photo.Album.FlickrPhotoSetId)) {
    // Create a new Photoset, with the Photo's FlickrId as the Primary Photo Id
    var photoset = flickr.PhotosetsCreate(photo.Album.Caption,
                                          photo.Album.Description,
                                          photo.FlickrId);

    // Store the Photoset Id with the Album.
    photo.Album.FlickrPhotoSetId = photoset.PhotosetId;
  } else {
    // Simply add the Photo to the Photoset.
    flickr.PhotosetsAddPhoto(photo.Album.FlickrPhotoSetId, photo.FlickrId);
  }
}

Finally, the code to check if a Photo already exists in a PhotoSet:

private static bool photoSetContainsPhoto(DoodlePhoto photo, Flickr flickr) {
  // Get the details of the photos in a Photoset, will become an issue
  // only when I add more than 500 images to a Photoset!
  var photos = flickr.PhotosetsGetPhotos(photo.Album.FlickrPhotoSetId);
  // Return true if the photo's Id appears in the list.
  return null != photos.SingleOrDefault(p => p.PhotoId == photo.FlickrId);
}

Deleting a photo from Flickr is a trivial exercise, simply requiring a call to PhotosDelete with the Flickr Id of the photo.

All in all, I found this to be very clean, and fairly intutive - especially if you take the time to read the API as you go.

Filed under: .Net

Implementing DataAnnotations

Posted 26 March 2010, 00:29 | by | Perma-link

Update: As of Entity Framework 4.1, the MinLengthAttribute is no longer needed as it's now part of the framework, but the length is the second parameter in the string format argument list, with the field name in position {0}. 06/06/2011

As I hinted at in my post "Upgrading to ASP.NET MVC 2.0", one of the main remaining changes I wanted to make after upgrading to version 2.0 was to move to using the DataAnnotations that gained first class support.

I've now done this, which meant that I've removed all the code based on the Nerd Dinner validation patterns, and switched to using the Validation Attributes classes.

I started with the basic RequiredAttribute to ensure that all required fields had a value, but then wanted to do a bit more - on my Contact form for example wanted to ensure that the name supplied was at least 3 characters long, that the email address used my rather more rigorous regex than ScottGu's, and that the message was at least 10 characters long.

A simple answer was to use the RegularExpressionAttribute, with settings such as @".{3,}" for the minimum length, and the lengthy pattern for emails, however I wasn't happy with this for a number of reasons:

  1. I wanted to use the "minimum length" validator on multiple properties, with different minimum lengths but very similar error messages including details of the length.
  2. Because my email pattern is compiled using string.Format to make it more manageable, I can't use it in a attribute as it needs to be a "compile time constant".

I also wanted something similar to the IntegerValidator in the Configuration namespace that allows me to specify just a MinValue, rather than the Range Validator, where I needed to supply an arbitrary high value to meet my needs.

As I'm not yet using the .NET 4 framework, I don't have access to the CustomValidatorAttribute, that's no bad thing in my mind, as I'm not a big fan of the "Magic String" form of configuration that I would need to use.

To that end I've created three validators:

public class IsEmailAttribute : RegularExpressionAttribute
{}
public class MinValueAttribute : ValidationAttribute
{}
public class MinLengthAttribute : ValidationAttribute
{}

IsEmailAttribute just supplies the email pattern (built in a read only property) to the base constructor, while MinValueAttribute and MinLengthAttribute both implement the IsValid method, and override the FormatErrorMessage to enable me to include the set value of the validator.

The full code for the MinLengthAttribute is:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class MinLengthAttribute : ValidationAttribute
{
  public int MinLength { get; set; }

  public MinLengthAttribute(int minLength) {
    MinLength = minLength;
  }

  public override bool IsValid(object value) {
    bool isValid = false;

    var stringValue = value as string;

    if (null != stringValue
        && MinLength <= stringValue.Length) {
      isValid = true;
    }

    return isValid;
  }

  public override string FormatErrorMessage(string name){
    return string.Format(ErrorMessageString, MinLength, name);
  }
}

The one big problem I've had with doing it this way is that the automation of the client-side validation doesn't work, but as it doesn't plug into the Validation Summary area, this is no great loss, as I'd have to redesign my forms more than I'd like to implement that.

The other major change I had to make to my code was to move to more of a ViewModel pattern - on some of my admin screens I was taking a FormsCollection rather than an explicit model which allowed me to have a photo form with text boxes for Caption, Order and Tags (which holds a comma seperated list of tags), but this doesn't map nicely to the Photo model, where Tags is a collection of Tag models. Writing a ViewModel for editing photos, all the annotations wired up nicely, and gave me much better control of what was being sent to the view.

One thing that was needed uploading photos that I did need to do was handle the case of a user not including an image - thankfully, the previous code set me in good stead:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadPhoto(int id,
                                EditPhoto editPhoto,
                HttpPostedFileBase ImageData) {
  ViewData["Title"] = "Create Photo";
  ViewData["Message"] = "Upload a new photo";

  if (ModelState.IsValid
      && null != ImageData
      && ImageData.ContentLength != 0) {
    // Persist to database
    // Return appropriate view based on Request.IsAjaxRequest
  }

  if (null == ImageData || ImageData.ContentLength != 0) {
    // ImageData is missing
    ModelState.AddModelError("ImageData", "You must supply a photo.");
  }

  // Return appropriate view based on Request.IsAjaxRequest
}

So we can still add additional errors to the ModelState property, allowing us to highlight additional fields that aren't part of the ViewModel.

Where I haven't created a ViewModel, I've used the MetadataTypeAttribute on the partial classes created for the Entity Framework classes:

[MetadataType(typeof(AlbumMetadata))]
public partial class Album {
  public string Description {
    get {
      return string.Format(
        "Photos from the album \"{0}\" on http://www.doodle.co.uk/Albums.aspx/{1}/{2}",
        Caption, AlbumId, Caption.CreateSlug());
    }
  }
}

public class AlbumMetadata {
  [Required(ErrorMessage = "You must supply a caption that is at least 3 characters long.")]
  [MinLength(3, ErrorMessage = "The caption must be at least {0} characters long.")]
  public string Caption { get; set; }
}

Next time: Implementing FlickrNet.

Filed under: .Net

LINQ to SQL: Changes since Beta 2

Posted 30 November 2007, 10:29 | by | Perma-link

So, Visual Studio 2008 is now in RTM, which also means that LINQ has moved out of Beta. Yay.

However, I've found some breaking changes between the Beta 2 and RTM bits:

  1. Table<t> no longer has a method Remove() - this has been renamed to DeleteOnSubmit(). This apparently is to "better convey that the updates are deferred until the call to DataContext.SubmitChanges()" (MSDN Forums)
  2. The XML declaration of the .DBML file has changed, in Beta 2 this was:
    <?markup version="1.0" encoding="utf-16"?>
    In the RTM this has changed to:
    <?markup version="1.0" encoding="utf-8"?>
    Curiously, this UTF-16 caused VS to complain about unicode support, and wouldn't open the design surface. Changing it to UTF-8 fixed the issue.

I'll post more as I find them.

Filed under: .Net

Playing with LINQ For SQL in a Windows Forms Application

Posted 18 September 2007, 14:45 | by | Perma-link

Don’t look now, but I think this might actually be a technical posting!

So, I’ve finally had an excuse to start playing around with LINQ – some data clean up tasks that I could have done by hand, but it’s always more fun to throw together a little app or two than spend your life in Enterprise Manager (or SQL Studio).

The key issues I came up against were with DataGrids, and picking and populating columns.

A fairly simple setup for editing the Taxonomy (remember, this is a developer application, so it works for me):
Editing the taxonomy

We have a ComboBox providing the Category, and then the DataGrid allows us to edit the Subjects. The “Subject ID” is an auto-generated GUID that is occasionally useful for the developer to know, so I wanted to display it, the data model looks like this:
The database schema

As you can see there’s an additional column in the database that I’m not interested in for the DataGrid: “TAX_CATEGORY_ID” this is always powered by the Category selected in the combo box, so I didn’t want to have to set it every time.

Part 1: Designer Support for LINQ Database Models (DataContext)

When wiring up the DataGrid, I initially tried the following:

editSubjects.DataSource = from ts in db.TaxonomySubjects
   where ts.TAX_CATEGORY_ID == (Guid)chooseCategory.SelectedValue
   orderby ts.SORT_ORDER
   select new { ts.TAX_SUBJECT_ID, ts.SORT_ORDER, ts.DISPLAY_NAME, ts.METADATA_NAME };

This almost worked – I had the columns I wanted, but I couldn’t edit the values at all – it turns out that if you use an anonymous type you lose the ability to edit it – the DataSource doesn’t know how to safely update the object, so disables editing.

On top of that, you have no way to change the column properties of the DataGrid in the designer.

I’d read about the ASP.Net LinqDataSource control, and so (naively) assumed there’d be a similar one for WinForms, but apparently not, and after hunting around and reading the readme of the samples that come with VS 2008 (namely the WinFormsDataBinding sample), I found the follow “solution”:

  1. Add a BindingSource to your form
  2. In the .Designer.cs class, add the following in the InitializeComponent() method:
    this.taxonomySubjectsBinding.DataSource = typeof(MetadataManager.TaxonomySubject);
    Replacing the binding name with your binding name, and the value in typeof() with the value from your “Linq To Sql” classes
  3. You can now assign that BindingSource to your DataGrid and get access to the column builder:
    Editing columns
    As you can just about see, the DataGrid doesn’t display the TAX_CATEGORY_ID column although it is there, and I’ve been able to supply nice names for the columns.

My data source wire up now looks like this:

editSubjects.DataSource = from ts in db.TaxonomySubjects
   where ts.TAX_CATEGORY_ID == (Guid)chooseCategory.SelectedValue
   orderby ts.SORT_ORDER
   select ts

Which is just taking all the TaxonomySubject objects that match the query, and the DataSource is then filtering their properties into the correct columns without me having to worry about anything else.

Part 2: Populating hidden columns/Auto Generated Ids in Linq DataContexts

Having successfully cleaned up my DataGrid columns, I now had two remaining problems to solve around adding a new record:

  1. Generating the new GUID for the subject.
  2. Adding the TAX_CATEGORY_ID value to the hidden column.

In the database, the TAX_SUBJECT_ID field is defined as:

[TAX_SUBJECT_ID] [uniqueidentifier] NOT NULL   CONSTRAINT [DF_TAX_SUBJECTS_TAX_SUBJ_ID]
  DEFAULT (newid()),

So, it’s the primary key column, with a default value of a new GUID. This is where our problems begin.

LINQ understands that SQL can auto-generate the values for columns on a database, provided:

Primary keys that are designated IsDbGenerated=true should also have a DBType with the IDENTITY modifier.

Note that my column here is not marked as an Identity column – to use the IsDbGenerated property, anything that goes in that column must be an integer.

I noticed that my DataContext class contained the following methods in a region called “Extensibility Method Definitions”:

partial void InsertTaxonomyCategory(TaxonomyCategory instance);
partial void UpdateTaxonomyCategory(TaxonomyCategory instance);
partial void DeleteTaxonomyCategory(TaxonomyCategory instance);
partial void InsertTaxonomySubject(TaxonomySubject instance);
partial void UpdateTaxonomySubject(TaxonomySubject instance);
partial void DeleteTaxonomySubject(TaxonomySubject instance);

So, let’s implement some of them in a new class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MetadataManager {
  public partial class MetadataDatabaseDataContext {
    partial void InsertTaxonomySubject(TaxonomySubject instance) {
      if (Guid.Empty == instance.TAX_SUBJECT_ID) {
        instance.TAX_SUBJECT_ID = Guid.NewGuid();
      }

      this.ExecuteDynamicInsert(instance);
    }
  }
}

A really key thing to note here – if you create these methods, even as empty methods, you must call this.ExecuteDynamicInsert(instance); otherwise any changes you try to submit will be lost.

Basically, before the changes are inserted into the database, this method fires for each one, and I’m creating a new GUID if the column is empty.

When adding just one new row this seems to work ok, but as soon as I try and add more rows, or two at a time, I get exceptions like:

"Cannot add an entity with a key that is already in use."

Hardly ideal – the key’s not in use, but I guess there’s something else I’m missing – any thoughts? On top of that, there’s no way to insert the Category ID into the foreign key column in this method.

So the final solution I came up with to resolve this was to hook into the RowEntered event on the DataGrid:

private void editSubjects_RowEnter(object sender, DataGridViewCellEventArgs e) {
  int rowIndex = e.RowIndex;
  if (null == editSubjects["taxSubjectId", rowIndex].Value) {
    editSubjects["taxSubjectId", rowIndex].Value = Guid.NewGuid();
  }

  if (null == editSubjects["taxCategoryId", rowIndex].Value) {
    editSubjects["taxCategoryId", rowIndex].Value = (Guid)chooseCategory.SelectedValue;
  }
}

This lets me generate a new GUID for the row, and adds the Category’s ID to the hidden column.

Which feels to me like an awful cludgy hack (but then so does a lot of the LINQ stuff as well – it’s all inline SQL really isn’t it? Isn’t that supposed to be bad?), but then I read the rest of ScottGu’s blog post – to get a custom query hooked up to a LinqDataSource you have to hook into the Selecting event and replace the result property with your new query…

Any thoughts, let me know.

Filed under: .Net

Trials with .Net 3.0 Framework

Posted 27 June 2006, 17:00 | by | Perma-link

Or, "Quick hack to get Windows Presentation Foundation running with Firefox".

I have FF installed on my desktop, it's my default browser, and this is, in general "a good thing". However, I've just installed the latest bits of the .Net Framework, and things are not all well in the world.

Attempting to debug (or even open) an .xbap or .xaml file resulted in FF starting up, and asking if I wanted to open this file with the "Windows Presentation Foundation Host". Well, of course I did! So FF asked me again, and again, each time opening a new tab - Don't check that little box "Don't ask me about this file type again", FF will just keep opening new tabs.

To get around this for now, as a quick fix I did the following:
Tools | Options | Downloads
View & Edit Actions
Scroll down in the list, and select the XBAP, and change the action to Internet Explorer, and the world's all happy again.

Filed under: .Net