Friday, 25 January 2013

Number test

Where'd I Put That Calculator?

More Numbers Every Awesome Programmer Must Know

http://highscalability.com/blog/2013/1/15/more-numbers-every-awesome-programmer-must-know.html

 

Colin Scott, a Berkeley researcher, updated Jeff Dean’s famous Numbers Everyone Should Know with his Latency Numbers Every Programmer Should Know interactive graphic. The interactive aspect is cool because it has a slider that let’s you see numbers back from as early as 1990 to the far far future of 2020. 

Colin explained his motivation for updating the numbers:

The other day, a friend mentioned a latency number to me, and I realized that it was an order of magnitude smaller than what I had memorized from Jeff’s talk. The problem, of course, is that hardware performance increases exponentially! After some digging, I actually found that the numbers Jeff quotes are over a decade old

Since numbers without interpretation are simply data, take a look at Google Pro Tip: Use Back-Of-The-Envelope-Calculations To Choose The Best Design. The idea is back-of-the-envelope calculations are estimates you create using a combination of thought experiments and common performance numbers to a get a good feel for which designs will meet your requirements.
And given most of these measures are in nanoseconds, to better understand the nanosecond you can do no better than Grace Hopper To Programmers: Mind Your Nanoseconds! 11.8 inches is the length of wire that light travels in a nanosecond, a billionth of a second.
Colin's post inspired some great threads On Reddit and On Hacker News. Here are some I found particularly juicy:
To the idea that these numbers are inaccurate Beckneard counters:

CSS is awesome

h2334A53E

http://cheezburger.com/6972892416

Wednesday, 12 December 2012

Stop VS2012 shouting!

  1. Start PowerShell
  2. copy/paste the following
    Set-ItemProperty -Path HKCU:\Software\Microsoft\VisualStudio\11.0\General -Name SuppressUppercaseConversion -Type DWord -Value 1

Next time you start VS the menus will be in mixed case.

See http://blogs.msdn.com/b/zainnab/archive/2012/06/14/turn-off-the-uppercase-menu-in-visual-studio-2012.aspx for more detail.

Monday, 10 December 2012

LuceneNet Sparse Faceted Search

I recently had a use for faceted search in a project using Lucene.net. The contrib project for Lucene.Net provides SimpleFacetedSearch which is great, but… it becomes very inefficient when your index has lots of values in the given field. So, for example, if you have a product category code that only has a hand full of values then SimpleFS is fine. If you facet against date and you have significant history then SimpleFS will eat all your memory very quickly.

SimpleFS represents a facet as a bitmap for each value. The size of the bitmap is equal to the number of documents in your index in bits (so numDocs/8 bytes). A quick calculation based on your index will give you very big numbers if you have large numbers of documents, values or both. In our case we have 100K values and 3.5M documents = 100K * (3.5M/8) = around 41GB!

SparseFacetedSearch to the rescue. SparseFS can deal with facets with many thousands of values (100’s of thousands in my case). Using much less memory. It is based on SimpleFacetedSearch but uses DocID lists instead of bitmaps. It’s suitable for high cardinality, sparsely populated facets. i.e. There are a large number of facet values and each facet value is hit in a small percentage of documents.
The memory usage is related to the number of hits each document has. In the product category example this would be exactly 1. The break even point is if you have more than 32 values. In our case we generally have between 1 and 5 values per document. Our memory usage is around 600MB. Quite a saving on 41GB!

There is a bunch of math that goes to explaining how that works out.

Wednesday, 29 August 2012

IEqualityComparer trick

In a previous post I showed that Distinct  uses GetHashCode to spot different values and used GroupBy to workaround.
But this isn’t actually completely true.
The code for Distinct looks like this…
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
  if (source == null)
    throw Error.ArgumentNull("source");
  else
    return Enumerable.DistinctIterator<TSource>(source, comparer);
}

private static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
  Set<TSource> set = new Set<TSource>(comparer);
  foreach (TSource source1 in source)
  {
    if (set.Add(source1))
      yield return source1;
  }
}

So if it can add the item to a Set<T> then item will be returned. So duplicate items are ignored. Quite sneaky!

Notice the Set is created with a comparer. Inside set.Add it looks like this…

private bool Find(TElement value, bool add)
{
  int hashCode = this.InternalGetHashCode(value);
  for (int index = this.buckets[hashCode % this.buckets.Length] - 1; index >= 0; index = this.slots[index].next)
  {
    if (this.slots[index].hashCode == hashCode && this.comparer.Equals(this.slots[index].value, value))
      return true;
  }
  // other stuff trimmed
}

It’s the if statement that is interesting. What it implies is that if the hash code is the same as another entry then it will separate the two values using comparer.Equals.

What if we supply a comparer that always returns a fixed value for GetHashCode (like 0). Then it will always have to use Equals.

public class AlwaysEqualsEqualityComparer<T> : IEqualityComparer<T>
{
    public AlwaysEqualsEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
    }

    private readonly Func<T, T, bool> comparer;

    public bool Equals(T x, T y)
    {
        return comparer(x, y);
    }

    public int GetHashCode(T obj)
    {
        return 0;
    }
}

So, given I have a collection of these…

public class MyThing
{
  public string ID { get; set; }
  public string Name { get; set; }
  public string Type { get; set; }
  public string Colour { get; set; }
}

I can use the new comparer like this…

var distinctList = thingList.Distinct(new AlwayEqualsEqualityComparer<MyThing>((x,y) => x.Colour == y.Colour);

…and get a new collection of Colours.


Here’s the caveat… You should not use this with large collections.

Sets (and other collections) use a system of “buckets” (generally implemented with arrays) to store the items. Which array your item is in is determined with the hash code. You can see some of this in the for statement of the Set.Find method above. So if every item returns the same hash code, every item will go in the same bucket. Scanning a bucket for a duplicate value will get slower the large the bucket.

So only use this trick when you know that the source collection is small (say less than a few thousand).

Distinct - Doesn’t work the way you think it does

I recently wanted to retrieve the set of unique values out of a list of objects. Say the object looked like this…
public class MyThing
{
  public string ID { get; set; }
  public string Name { get; set; }
  public string Type { get; set; }
  public string Colour { get; set; }
}

I have 100 of these in a List<MyThing>. How do I get a list of unique Type/Colour combinations. You might think that the obvious way would be to use the Distinct linq operator. It gets a little complicated because you need to define an IEqualityComparer<MyThing> to define which objects are equal (ie where Type and Colour are the same). So you need one of these…

public class MyThingComparer : IEqualityComparer<MyThing>
{
  public bool Equals(MyThing x, MyThing y)
  {
    // Object.ReferenceEquals and null check left out for clarity

    return x.Type == y.Type && x.Colour == y.Colour;
  }

  public int GetHashCode(MyThing obj)
  {
    return obj.GetHashCode();
  }
}

Seems straight forward enough. We’re checking that the Type and Colour properties are the same in two objects. So we try…

var distinctList = thingList.Distinct(new MyThingComparer());

…expecting a nice short list of unique Type/Colour combinations. What we actually get is the whole list. Much head scratching and ranting ensues.

Then you run into a post from 2008 http://blog.jordanterrell.com/post/LINQ-Distinct()-does-not-work-as-expected.aspx and you learn that it isn’t using you’re Equals function. It’s using GetHashCode to determine the objects with the same hash code are equal. Once you calm down and think about it, it kind of makes sense. But is a little counter intuitive. To quote from the MSDN page for Object.GetHashCode
“A hash code is a numeric value that is used to identify an object during equality testing.”
It goes on to describe IEqualityComparer<T> as a “hash code provider”.

So we could try to figure out some algorithm to calculate a hash code that is equal and unique for our combination of property values. But this is incredibly difficult to get provably correct.

There is another way…

var distinctList = thingList.GroupBy(x => new { x.Type, x.Colour }).First();

We’re using an anonymous object to group by the required properties then taking the first out of the group.
Yay, it works! But let’s dig a little.

Enumerable.GroupBy says…
“returns a collection of IGrouping<TKey, TElement> objects, one for each distinct key that was encountered.”

“The default equality comparer Default is used to compare keys.”
Oops, there’s that “distinct” word again. Read a little further and we discover that this “Default” thing is a IEqualityComparer that uses Object.GetHashCode. Aren’t we back where we started? Why does the GroupBy seem to work as expected? A little further research leads to http://odetocode.com/blogs/scott/archive/2008/03/25/and-equality-for-all-anonymous-types.aspx Where the summary states…
“Turns out the C# compiler overrides Equals and GetHashCode for anonymous types. The implementation of the two overridden methods uses all the public properties on the type to compute an object's hash code and test for equality. If two objects of the same anonymous type have all the same values for their properties – the objects are equal. This is a safe strategy since anonymously typed objects are essentially immutable (all the properties are read-only). Fiddling with the hash code of a mutable type gets a bit dicey.”
So an anonymous type implements GetHashCode as a combination of each of the properties of the type. So we can rely on a bunch of clever engineers to figure out how to figure out the algorithm.

Friday, 24 August 2012

WebAPI - AddWithoutValidation method not found

If you are using WebAPI and have recently installed VS2012. Your WebAPI stuff will be broken.
Your controller method will be called then you’ll just get a 500 response. Poking VS got it to give up the underlying exception which is "Method not found System.Net.Http.HttpHeaders.AddWitoutValidation".

Several hours of spelunking, trying framework source stepping, break on exception, beating it with a big stick and compiling from a command line with “msbuild /v:d” which shows assembly reolution resulted in realising that VS was compiling against the correct assemblies (ie I’d previously grabbed the RC from nuget). But…

Using the Fusion Log Viewer (fuslogvw) showed that when I ran the project the System.Net.Http assembly was being redirected to the new framework version instead of the file reference to my copy of the dll.

Here’s my solution: Add your own assembly redirect to ensure the right version of the assembly is used.
Simply add the following section to your app.config (if you are self hosting) or to web.config (if you are hosting in IIS).

<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0 - 2.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

Some will ask “Why bother? Why not just use the RTM?”. Well, I have a new version of our product about to RTM. I really don’t want to have to refactor a bunch of code and have it all go back through QA and regression testing.
We will move on to WebAPI RTM but not until our next version.

This is the risk that the decision to make 4.5 an in-place upgrade exposes us to. What other subtle changes in the framework are there?

I’m glad we test and deploy from build servers and not from a developers machine. You should check that your build environment is compatible with your production/runtime environment.

Wednesday, 7 September 2011

JavaScript OMG!

http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/tags/JavaScriptOMG/default.aspx

Mike Taulty shares a series of posts continuing 20 (so far) interesting and surprising aspects of the JavaScript Language he has discovered in his reading and learning about JavaScript. Each point has some code samples and references to books / articles where you can learn more.

Wednesday, 31 August 2011

It's Not Just Standing Up

http://martinfowler.com/articles/itsNotJustStandingUp.html

 

Daily stand-up meetings have become a common ritual of many teams, especially in Agile software development. However, there are many subtle details that distinguish effective stand-ups and a waste of time.

Thursday, 18 August 2011

JSONP FTW

We’ve been looking at how to integrate our stuff with MS Dynamics CRM. Dynamics does not play nice with others. I won’t go into the details here but I think we’re going to end up using javascript as a proxy to get things done. As a result I’ve been looking at the new (ish) WebAPI bits from the WCF team (http://wcf.codeplex.com). The basic motivation behind this project is to make WCF talk HTTP like a native. Things like content format negotiation are baked in. So you can write a single service and have different clients receive differently formatted responses. So a JQuery ajax call will see JSON another client might see XML. You can even point a browser at your service and get HTML via Razor templates (very useful if you want to add an admin UI to your service.
The point of this post is about content format negotiation and dealing with JsonP.
A lot (errr most) of this is taken from Alexander Zeitlers article. He mentions part way through to grab a file from the WebAPI project. I’ve added a little of my own flavour to this part.

WebAPI has the concept of MediaTypeFormatters. When a request comes in it will have an “accept” header which tells the server which media types the client can handle. A JQuery ajax request would send “application/json”, a browser would send “text/html”.
The accept header value is used to look up which formatter to use to format the response.
There are times, however, when you want to force the format. Testing via a browser is one. But more importantly when using JsonP the request has an accept header of “*/*”. In this case you always want the response in json.
In the ContactManager_Advanced project in the samples included in the codeplex project there is an example of a “MessageChannel” that inspects the uri and sets the accept header. I’ve customised this a little so that it also looks for a “format” parameter in the querystring. It also forces to json if the is a “callback” parameter in the querystring.
Lastly I’ve changed the fluent interface. It made little sense to me to have an extension method on HttpApplication.
Here’s the listing:
    public static class UriFormatExtensionMessageChannelExtensions
    {
        public static IHttpHostConfigurationBuilder AddUriFormatExtension(this IHttpHostConfigurationBuilder builder)
        {
            return builder.AddMessageHandlers(typeof(UriFormatExtensionMessageChannel));
        }
    }

    public class UriFormatExtensionMessageChannel : DelegatingChannel
    {
        public UriFormatExtensionMessageChannel(HttpMessageChannel handler) : base(handler) { }

        private static Dictionary<string, MediaTypeWithQualityHeaderValue> extensionMappings = new Dictionary<string, MediaTypeWithQualityHeaderValue>();

        public static FluentExtensionMappings SetUriExtensionMapping(string extension, string mediaType)
        {
            extensionMappings[extension] = new MediaTypeWithQualityHeaderValue(mediaType);
            return new FluentExtensionMappings();
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!TryGetLastSegmentFormat(request))
                TryGetQSFormat(request);

            return base.SendAsync(request, cancellationToken);
        }

        /// <summary>
        /// Try to get the format from the last segment of the Uri
        /// </summary>
        /// <example>http://example.com/product/1/json</example>
        /// <param name="request"></param>
        /// <returns>true if a format was found</returns>
        private static bool TryGetLastSegmentFormat(HttpRequestMessage request)
        {
            var segments = request.RequestUri.Segments;
            var lastSegment = segments.LastOrDefault();

            MediaTypeWithQualityHeaderValue mediaType;
            if (extensionMappings.TryGetValue(lastSegment, out mediaType))
            {
                var newUri = request.RequestUri.OriginalString.Replace("/" + lastSegment, "");
                request.RequestUri = new Uri(newUri, UriKind.Absolute);
                request.Headers.Accept.Clear();
                request.Headers.Accept.Add(mediaType);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Try to get the format from the query string of the Uri.
        /// If it's a JsonP callback then force to json
        /// </summary>
        /// <example>http://example.com/product/1?format=json</example>
        /// <param name="request"></param>
        /// <returns>true if a format was found</returns>
        private static bool TryGetQSFormat(HttpRequestMessage request)
        {
            var qsValues = HttpUtility.ParseQueryString(request.RequestUri.Query);
            var format = qsValues["format"];
            bool rebuildUri = false;
            if (!string.IsNullOrEmpty(format))
                rebuildUri = true;

            // if it's a JsonP callback then force to json
            if (!string.IsNullOrEmpty(qsValues["callback"]))
                format = "json";

            MediaTypeWithQualityHeaderValue mediaType;
            if (!string.IsNullOrEmpty(format) && extensionMappings.TryGetValue(format, out mediaType))
            {
                if (rebuildUri)
                {
                    var newUriBuilder = new UriBuilder(request.RequestUri);
                    qsValues.Remove("format");
                    newUriBuilder.Query = qsValues.ToString();
                    request.RequestUri = newUriBuilder.Uri;
                }

                request.Headers.Accept.Clear();
                request.Headers.Accept.Add(mediaType);
                return true;
            }

            return false;
        }

        protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }

        public sealed class FluentExtensionMappings
        {
            public FluentExtensionMappings SetUriExtensionMapping(string extension, string mediaType)
            {
                extensionMappings[extension] = new MediaTypeWithQualityHeaderValue(mediaType);
                return this;
            }
        }
    }



So now your global.asax Application_Start will have a snippet like this.


UriFormatExtensionMessageChannel
  .SetUriExtensionMapping("xml", "application/xml")
  .SetUriExtensionMapping("json", "application/json")
  .SetUriExtensionMapping("png", "image/png")
  .SetUriExtensionMapping("odata", "application/atom+xml");

var config = HttpHostConfiguration.Create()
  .AddUriFormatExtension()
  .AddJsonpHandler();



The AddJsonpHandler line is a simple extension method to wrap Alex’s JsonpResponseHandler.


    public static class JsonpResponseHandlerExtensions
    {
        public static IHttpHostConfigurationBuilder AddJsonpHandler(this IHttpHostConfigurationBuilder builder)
        {
            return builder.AddResponseHandlers(c => c.Add(new JsonpResponseHandler()), (s, d) => true);            
        }
    }



Although this example is using IIS to host the service all of this is equally applicable to self hosted services.

Tuesday, 15 February 2011

Hidden Features of C#?

http://stackoverflow.com/questions/9033/hidden-features-of-c

 

This is kind of a meta answer that lists a set of answers related to features/keywords etc that you may already know about but there is some interesting debate and further links to some of them.

Thursday, 2 December 2010

Google Beatbox

http://kottke.org/10/11/google-beatbox

The latest big thing from Google: beatboxing. Just go to this page on Google Translate and press "Listen"

Wednesday, 23 June 2010

Building without Visual Studio


If you don’t have Visual Studio installed, maybe because this is a build server, then you will almost certainly get msbuild errors because it cannot find some tool or other from the Windows SDK.

Like:

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(1835, 9):
error MSB3454: Tracker.exe is required to correctly incrementally generate resources
in some circumstances, such as when building on a 64-bit OS using 32-bit MSBuild.
This build requires Tracker.exe, but it could not be found. The task is looking for
Tracker.exe beneath the InstallationFolder value of the registry key
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A. To solve the
problem, either: 1) Install the Microsoft Windows SDK v7.0A or later. 2) Install
Microsoft Visual Studio 2010. 3) Manually set the above registry key to the correct
location. Alternatively, you can turn off incremental resource generation by setting
the "TrackFileAccess" property to "false".

You may also get a similar message regarding LC.EXE.

The v7.0A version of the Windows SDK is installed when you install Visual Studio 2010 and is expected by the .Net 4.0 version of msbuild. However, you don’t seem to be able to get it as a separate download!

A bit of Googling shows a few “solutions. Such as adding “TrackFileAccess=false” as a configuration option for msbuild (http://bradwilson.typepad.com/blog/2010/05/working-around-build-error-msb3454.html).

This works fine for the TRACKER.EXE problem but not for the LC.EXE version.

Here’s my solution:

  1. Download the v7.1 version of Windows SDK (for .Net 4.0 and good for all version of Windows)
    http://www.microsoft.com/downloads/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b&displaylang=en
    Other versions are available from the Windows SDK MSDN Developer Center (http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx)
  2. This is a web installer. If you decide to go find the full ISO of the SDK it’s around 1.6GB. The web installer lets you only select the bits you want and should download/install in a few minutes.
  3. Run winsdk_web.exe you downloaded above. Click through until you get to the “Installation Options” page.
  4. De-select everything except the .NET Development – Tools. The page should look like this

  5. On Windows 2008 server it will let you also completely unselect the Intellisense assemblies too
  6. Select next until it starts installing.
    Even on a slow connection it should only take a few minutes.
  7. Lastly you need to convince msbuild to use this version of the SDK
  8. On the Start menu you will have a “Microsoft Windows SDK v7.1” folder
  9. Select “Windows SDK 7.1 Command Prompt”
  10. Enter the following commands
    > cd Setup
    > WindowsSdkVer –version:v7.1

    See http://msdn.microsoft.com/en-us/library/ff660764.aspx (Configuring Visual Studio for Visual C++ Development with the Windows SDK) for info
  11. Done. Msbuild will now be able to find the tools it needs

You will also run into problems is you’re trying to compile web apps with an error like

error MSB4019: The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.

Simply copy the folder “C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0” from your dev machine with VS2010 installed onto your build server.

YMMV but this is what works for me.
Good luck

Wednesday, 17 March 2010

Don't just roll the dice

http://www.neildavidson.com/dontjustrollthedice.html

Don't just roll the dice

How do you price your software? Is it art, science or magic? This usefully short book will help you get the theory, practical advice and case studies you need to stop you reaching for the dice.

Download the free eBook (.pdf)

To buy a physical copy, or read or write reviews, visit Amazon.com or Amazon.co.uk

To find out more about the author, go to the neildavidson.com homepage

 

 

Tuesday, 9 March 2010

TFS considered dangerous

Martin Fowler did a survey of version control software coming up with an “approval” rating. Subversion 93%; TFS 0%.
Yes _zero_ percent!

All the others with high approval rating were DVCSs (git, Mercurial etc).

http://martinfowler.com/bliki/VcsSurvey.html

Monday, 8 March 2010

Enabling SQL Server access through the Windows firewall

This is one of the annoying things about setting up a new machine. You think that you’ve got all the system stuff installed. Application deployed. Everything seems good. Then, later, you try to resolve some issue by connecting from some other machine (like you’re dev machine) and it won’t let you. The firewall is blocking remote connections. You know you should “do the right thing” and put in a very specific rule to just allow SQL Server traffic but you need to get this done _now_ so you just disable the firewall to get your problem fixed.

Do you remember to re-enable it???

So here’s how you do it the command line way.

netsh advfirewall firewall add rule name = SQLPort dir = in protocol = tcp action = allow localport = 1433 remoteip = localsubnet profile = DOMAIN

Copy it to a bat file somewhere then run it on whatever machines you need access to.

 

Taken from http://msdn.microsoft.com/en-us/library/cc646023.aspx

Monday, 2 July 2007

Overriding ConfigurationManager

So... we're building a system that will certainly need to be hosted across many machines to provide load balancing and splitting of tasks into granular pieces that can be distributed across the pool.
The problem comes when you need to duplicate configuration across all those machines. Sure we could copy app.exe.config and web.configs across the pool. But management of this soon becomes tendious and error prone. Especially once you start needing to assign clusters to specific customers to support SLAs.

System.Configuration.ConfigurationManager is the normal place to access various settings. But as with several System classes overriding behavior can seem impossible. A lot of the parts you want to override are internal and/or sealed.

Reflector to the rescue!

All the useful methods (like AppSettings) delegate via s_configSystem which is private static.
You'll notice that there's a SetConfigurationSystem method that sets s_configSystem. But of course it's private static (sigh). But at least it takes an interface type, so maybe there's a chance.

OK let's use FileDisassembler (a Reflector plugin) and get the sourse of some System assemblies out into files so we can do some searching.

SetConfigurationSystem is actually called from System.Web.Configuration.HttpConfigurationSystem where they use Type.GetType to create some more internal, sealed types from System.Configuration.Internal that in turn call SetConfigurationSystem. They do this to redirect to the web.config file and deal with the file changing so that the web app is restarted.

So the trick is to create a class that implements the interface IInternalConfigSystem. Three straight forward methods. Then use the technique from HttpConfigurationSystem to inject our class into ConfigurationManager.

Not as hard after all. Here's a really simple example...


public class MyConfigSystem: IInternalConfigSystem
{
public static void Install()
{
MyConfigSystem confSys = new MyConfigSystem();
Type configFactoryType = Type.GetType("System.Configuration.Internal.InternalConfigSettingsFactory, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true);
IInternalConfigSettingsFactory configSettingsFactory = (IInternalConfigSettingsFactory)Activator.CreateInstance(configFactoryType, true);
configSettingsFactory.SetConfigurationSystem(confSys, false);

Type clientConfigSystemType = Type.GetType("System.Configuration.ClientConfigurationSystem, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true);
clientConfigSystem = (IInternalConfigSystem)Activator.CreateInstance(clientConfigSystemType, true);
}
private static IInternalConfigSystem clientConfigSystem;

#region IInternalConfigSystem Members

object IInternalConfigSystem.GetSection(string configKey)
{
switch (configKey)
{
case "appSettings":
NameValueCollection nvc = new NameValueCollection();
nvc.Add("fred", "wilma");
return nvc;
case "connectionsStrings":
ConnectionStringSettingsCollection cssc = new ConnectionStringSettingsCollection();
cssc.Add(new ConnectionStringSettings("aName", "someConnectionString", "aProvider"));
return cssc;
default:
return clientConfigSystem.GetSection(configKey);
}
}

void IInternalConfigSystem.RefreshConfig(string sectionName)
{
switch (sectionName)
{
case "appSettings":
break;
case "connectionsStrings":
break;
default:
clientConfigSystem.RefreshConfig(sectionName);
break;
}
}

bool IInternalConfigSystem.SupportsUserConfig
{
get { return true; }
}

#endregion
}

Just ensure that MyConfigSystem.Install() is called before you call any method on Configurationmanager.

A bit of WCF and a bit of caching and we can have whichever sections we like retrieved from a service. We stil need to deal with single point of failure issues. But we have other parts for that :)

Wednesday, 27 June 2007

vsvars32.bat in PowerShell = vsvars32.ps1

I've been using PS to do batch changes to my c# projects. Things like changing the build location to a common \bin folder and changing approriate HintPaths etc.

Anyway... Even though I've been using PS for a while I still use the old cmd line to do various tasks using msbuild, gacutil etc. It's always been a little annoying not having a ps1 version of vsvars32.bat. I've finally gotten around to doing it. It's a very literal translation but it works fine.


$env:VSINSTALLDIR="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE"
$env:VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 8"
$env:FrameworkDir="c:\WINDOWS\Microsoft.NET\Framework"
$env:FrameworkVersion="v2.0.50727"
$env:FrameworkSDKDir="C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0"
# Root of Visual Studio common files.

if("$env:VSINSTALLDIR" -eq ""){
echo "VSINSTALLDIR variable is not set."
exit
}
if("$env:VCINSTALLDIR" -eq ""){
$env:VCINSTALLDIR=$env:VSINSTALLDIR
}

# Root of Visual Studio ide installed files.
$env:DevEnvDir=$env:VSINSTALLDIR

# Root of Visual C++ installed files.
$env:MSVCDir="$env:VCINSTALLDIR\VC"

echo "Setting environment for using Microsoft Visual Studio 8 tools."
echo "(If you have another version of Visual Studio or Visual C++ installed and wish"
echo "to use its tools from the command line, run vcvars32.bat for that version.)"

#@REM VCINSTALLDIR\Common7\Tools dir is added only for real setup.

$env:PATH="$env:DevEnvDir;$env:MSVCDir\BIN;$env:VCINSTALLDIR\Common7\Tools;$env:VCINSTALLDIR\Common7\Tools\bin\prerelease;$env:VCINSTALLDIR\Common7\Tools\bin;$env:FrameworkSDKDir\bin;$env:FrameworkDir\$env:FrameworkVersion;$env:PATH;"
$env:INCLUDE="$env:MSVCDir\ATLMFC\INCLUDE;$env:MSVCDir\INCLUDE;$env:MSVCDir\PlatformSDK\include\prerelease;$env:MSVCDir\PlatformSDK\include;$env:FrameworkSDKDir\include;$env:INCLUDE"
$env:xLIB="$env:MSVCDir\ATLMFC\LIB;$env:MSVCDir\LIB;$env:MSVCDir\PlatformSDK\lib\prerelease;$env:MSVCDir\PlatformSDK\lib;$env:FrameworkSDKDir\lib;$env:LIB"