Execution in the Kingdom of Nouns - a rant...
I love a good rant. you.read(this);
All the (developer) news that's unfit to read...
I love a good rant. you.read(this);
Posted by IDisposable at 3/30/2006 06:29:00 PM 2 comments
[Via Knowing.NET]Microsoft is looking for a developer whose "first task will be to drive the exploration of other dynamic languages such as Ruby and JavaScript on the CLR". Dang. If only they allowed people to work remotely...
Makes you wonder how much other talent they're missing out on...
Posted by IDisposable at 3/28/2006 10:13:00 PM 0 comments
The coolest thing about the blog culture is people that respond to comments (and suggested topics). One person that consistently goes above the call is Michael Kaplan. On Friday, I asked a question via his contact me link, and by 9am Saturday he had grokked my question, found a solution, and blogged about it for the universe to share. This is awesome! Microsoft is lucky to have someone of Michael's caliber, and we are lucky to have him exposed and working on weekends for us.
Unfortunately, it's not all peaches and cream in .Net land. Frankly the solution he gave is suboptimal for my use (through no fault of Michael's). Let me explain my situation, the initial solution, the problems, and the eventual resolution
CurrentCulture
and CurrentUICulture
So, first step is to enumerate the CultureInfo
objects like this (using a custom business object called Locale and WilsonORMapper)
public static void Fill() { ObjectSpace space = Manager.ObjectSpace.IsolatedContext; using (Transaction transaction = space.BeginTransaction(IsolationLevel.ReadCommitted)) { Dictionary<string, Locale> locales = new Dictionary<string, Locale>(); foreach (Locale existingLocale in space.GetCollection<Locale>(string.Empty)) { locales.Add(existingLocale.LocaleCode, existingLocale); } // Grab a type that we know is in mscorlib Assembly mscorlib = Assembly.GetAssembly(typeof(System.Object)); // Enumerate through all the languages .NET may be localized into foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) { string name = ci.Name; if ( ! locales.ContainsKey(name)) { Locale newLocale = new Locale(); newLocale.LocaleCode = name; newLocale.Description = ci.NativeName; newLocale.InvariantDescription = ci.DisplayName; space.StartTracking(newLocale, InitialState.Inserted); locales.Add(name, newLocale); } } transaction.PersistChanges(locales.Values, PersistDepth.ObjectGraph); transaction.Commit(); } }
This is pretty simple. The problem is that I end up with more than 159 specific cultures (and 69 neutral cultures) available, only a few of which have localized .Net runtimes installed. This is where I searched and eventually punted to Micheal. He suggested that I can use the Assembly.GetSatelliteAssembly(CultureInfo ci)
method to feel out the correct culture. He also changed his call to CultureInfo.GetCultures()
to also include the CultureTypes.NeutralCultures
enumeration flag.
Several problems with this approach pop up. Firstly, since we are calling GetSatelliteAssembly
, it has to have a way to communicate when the assembly cannot be located. It does this by throwing a FileNotFoundException
. That sucks because I'm going to get hundreds of exceptions for the 24 or so available localizations, and exceptions are a terrible performance hit (not to mention bad API design). This could be easily cured if Microsoft had exposed the (Reflectored) InternalGetSatelliteAssembly
method, which takes a boolean flag to determine if it should throwOnFileNotFound
. Alas, that isn't exposed and I certainly am not going to start twiddling the internal members of mscorlib
. Another possibility is the tantalizing ResourceManager.TryLookingForSatellite()
, but it's private as well.
The other problem with this is the fact that I am getting back neutral cultures. These are great when doing localizations because you can create an English translation without worrying about the differences between British English and United States English. This sucks because you cannot set the Thread.CurrentUICulture
to any neutral culture (it wants you to be specific, and it'll thunk down to the neutral culture if needed). Thus I've got a tiny list of cultures of which only a couple are actually useable as-is.
Sigh... what to do? I take my hint from the path Michael has started down and do some more reflectoring. So after much digging, I find that the best I can do is catch is basically what Michael suggests, so I tried to minimize the number of exceptions thrown and solve the neutral/specific culture issue, so what I did was track the list of known installed neutral localizations and test the parents of each specific culture. The final code loos likes this:
// Grab a type that we know is in mscorlib private static readonly Assembly s_MSCorLib = Assembly.GetAssembly(typeof(System.Object)); public static void Fill() { ObjectSpace space = Manager.ObjectSpace.IsolatedContext; using (Transaction transaction = space.BeginTransaction(IsolationLevel.ReadCommitted)) { Dictionary<string, Locale> locales = new Dictionary<string, Locale>(); foreach (Locale existingLocale in space.GetCollection<Locale>(string.Empty)) { locales.Add(existingLocale.LocaleCode, existingLocale); } Dictionary<string, CultureInfo> neutralCultures = new Dictionary<string, CultureInfo>(); // Enumerate through all the neutral cultures first (this saves time in checking all the others...) foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.NeutralCultures)) { if (AddIt(locales, neutralCultures, ci)) { neutralCultures.Add(ci.Name, ci); } } // Enumerate through all the specific languages .NET may be localized into foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures)) { if (AddIt(locales, neutralCultures, ci)) { // if we got here, the culture localizations are installed. Locale newLocale = new Locale(); newLocale.LocaleCode = ci.Name; newLocale.Description = ci.NativeName; newLocale.InvariantDescription = ci.DisplayName; space.StartTracking(newLocale, InitialState.Inserted); locales.Add(ci.Name, newLocale); } } transaction.PersistChanges(locales.Values, PersistDepth.ObjectGraph); transaction.Commit(); } } private static bool AddIt(Dictionary<string, Locale> locales , Dictionary<string, CultureInfo> neutralCultures , CultureInfo ci) { bool addIt = false; if (!locales.ContainsKey(ci.Name)) { // if it exists in neutral form, we can use it, special-case English since that is // always installed.. if (neutralCultures.ContainsKey(ci.Name) || ci.TwoLetterISOLanguageName == "en") { addIt = true; } else { try { Assembly satellite = s_MSCorLib.GetSatelliteAssembly(ci); addIt = true; // if we got here, the localization was found... } catch (FileNotFoundException) { if (ci.Parent != null && ci.Parent != CultureInfo.InvariantCulture) addIt = AddIt(ref locales, ref neutralCultures, ci.Parent); } } } return addIt; }
Posted by IDisposable at 3/27/2006 06:02:00 PM 2 comments
Shawn Farkas notes here that you can't display some of the information available in SecurityException
when not running in fully trusted code (like an ASP.Net host). Then along comes Dominick Baier with the fix. I smell a setup.
In any case, the trick is to put a fully-trusted assembly (in the GAC, of course!) to handle the extraction of this information, and pass the unmangled SecurityException
in. If the trusted assembly's method asserts the needed ControlEvidence
and ControlPolicy
then you can get the extra goodies in ToString().
Cool!
[Via www.leastprivilege.com]using System; using System.Security.Permissions; using System.Security; [assembly: AllowPartiallyTrustedCallers] namespace LeastPrivilege { [ SecurityPermission(SecurityAction.Assert, ControlEvidence=true, ControlPolicy=true) ] public class SecurityExceptionViewer { public static string[] ViewException(SecurityException ex) { return new string[] { ex.ToString(), ex.Demanded.ToString(), ex.GrantedSet.ToString() }; } } }
Posted by IDisposable at 3/23/2006 02:39:00 PM 1 comments
Justin Palmer has done it again! Simplify the whole process of linking up behavior using CSS selectors. This time, though, support for events are included. This is like behavior.js with even more code-refactoring, integrated with prototype.js to make the simple things simple. Head on over! Introducing CSS event:Selectors
Posted by IDisposable at 3/22/2006 03:44:00 PM 0 comments
This is simply amazing... being an old BBS sysop, I would have KILLED for this ability in the day... Old Skool Trippin', Part I
Posted by IDisposable at 3/15/2006 12:42:00 PM 0 comments
PHLAT is PHAT! I may finally have to switch from Google Desktop Search to Windows Desktop Search, because PHLAT lets you tag your files just like GMail lets you tag your messages (why Google couldn't figure this out, I don't know). The search/filter strategy is also really cool, letting you "refine" your search while clearly seeing the filters being applied. PHLAT - Intuitive Personal Search
Posted by IDisposable at 3/14/2006 11:21:00 PM 0 comments
I've found that occasionally we're seeing VS startup times being very slow. In one recent case, it was the MRU files/folders (that list that shows when the File / Recent Files or File / Recent Projects has references to the files or folders that are no longer reachable (say on a disconnected network drive). All you have to do is delete the HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\FileMRUList or HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\ProjectMRUList registry keys.
Another couple of hints come from Tim Noonan
If you have start-up speed issues when the Team Foundation Server is not accessible, you can add a DWORD value called AutoLoadServer under HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\TeamFoundatio and set the value to 0. That way VS won't try to connect to the TFS machine on startup. You can always set the value to 1 to enable auto-reconnect. I've personally used this trick while doing the maintenance on my TFS server.
The final trick is best described by the hippie coder himself as I've never used it
[Via Rants of a hippie coder]Don't Automatically Get Missing Files
The next setting is to control whether or not we attempt to automatically get files from the Team Foundation Server that are missing from a solution or project. This can be very helpful when you have files that are part of a project or solution but are generated by the build process as well as situations where you are working offline.
Add a DWORD value called DisableGetMissingFiles under HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\TeamFoundation\SourceControl or HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\8.0\TeamFoundation\SourceControl. Any value other than zero will disable getting files from the server automatically.
Posted by IDisposable at 3/14/2006 11:01:00 PM 1 comments
UPDATE: This project is hosted on CodePlex as the Dynamic Reflection Library for all further updates.
RE: Dynamic sorting of objects using lightweight code generation.
I got a report of issues sorting objects that have null (not Nullable<T>
) values. In the previous implementation, I blindly call down to the CompareTo method associated with the property's Type
. This obviously doesn't work with virtual methods. For some types (like String
) it was enough to simply make the emitted code a Call
instead of a CallVirt
, but I didn't want to be half-right. So the new version does the right thing and handles the null-checks for the left and right filed/property values.
The emitted code is slightly more complex, but still about as fast as I now check for IsFinal
methods and use Call
instead of CallVirt
when possible. This optimization is in place for property get calls, and the call the CompareTo, so it is a bit faster.
I also fixed an issue if you compared a whole bunch of attributes and blew the generate IL so large that the loop-break label wasn't reachable by a short branch (nobody reported this, just noticed in code review).
I've updated both the DynamicSorter.zip file and the Utilites.zip file (the latter has other updates to improve fxCop compliance).
The emitted code for "FirstName, lastName, Gender" [String
property, double
field, Enum
property, respectively] now looks like this:
IL_0000: ldarg.0 IL_0001: callvirt System.String get_FirstName()/DynamicComparerSample.Person IL_0006: dup IL_0007: brtrue.s IL_0018 IL_0009: pop IL_000a: ldarg.1 IL_000b: callvirt System.String get_FirstName()/DynamicComparerSample.Person IL_0010: brtrue.s IL_0015 IL_0012: ldc.i4.0 IL_0013: br.s IL_0023 IL_0015: ldc.i4.m1 IL_0016: br.s IL_0023 IL_0018: ldarg.1 IL_0019: callvirt System.String get_FirstName()/DynamicComparerSample.Person IL_001e: call Int32 CompareTo(System.String)/System.String IL_0023: dup IL_0024: brtrue IL_0060 IL_0029: pop IL_002a: ldarg.0 IL_002b: ldfld Double age/DynamicComparerSample.Person IL_0030: stloc.0 IL_0031: ldloca.s V_0 IL_0033: ldarg.1 IL_0034: ldfld Double age/DynamicComparerSample.Person IL_0039: call Int32 CompareTo(Double)/System.Double IL_003e: dup IL_003f: brtrue IL_0060 IL_0044: pop IL_0045: ldarg.0 IL_0046: callvirt DynamicComparerSample.Gender get_Gender()/DynamicComparerSample.Person IL_004b: box DynamicComparerSample.Gender IL_0050: ldarg.1 IL_0051: callvirt DynamicComparerSample.Gender get_Gender()/DynamicComparerSample.Person IL_0056: box DynamicComparerSample.Gender IL_005b: call Int32 CompareTo(System.Object)/System.Enum IL_0060: ret
Posted by IDisposable at 3/09/2006 06:42:00 PM 4 comments
Labels: CodePlex, Dynamic, DynamicMethod, Emit, IL, LCG, lightweight code generation
So today I was looking at some code and some advice I read more than a year ago finally clicked. Quick, what does this code mean?
Provider provider = Enum.Parse(typeof(Provider), mySettings.ProviderName, true);
Back from looking up that overload of Enum.Parse
yet? So my point is this, you don't know what that true
means without looking it up. How much more obvious is this code?
enum { CaseSensitive = 0 , IgnoreCase = 1 } MatchCase;Provider provider = Enum.Parse(typeof(Provider), mySettings.ProviderName, IgnoreCase);
The moral of this story is that instead of building methods that take boolean flags, construct them to take enumerations that clearly document the meaning of the flag. This way you don't have to do an API lookup, or just know. I first saw this idea about a year ago... if my head wasn't so full of things I had to just know it might have sunk in earlier.
Posted by IDisposable at 3/07/2006 11:11:00 AM 1 comments
The current "stateless" design of ObjectDataSource when using the DataObjectTypeName is not viable for use in modern Ajax-driven applications. Since the ObjectDataSource creates a new object on every postback, there is no way to persist between call-backs any changes made by other previous postbacks without committing the infomation to the datastore.
Previously proposed "solutions" like not using DataObjectTypes are unacceptible due to the coupling of the view to the needed Insert/Update/Delete methods.
Microsoft should allow ObjectDataSource to have "acquire" symantics when handling postbacks, so that it can get the object instance being manipulated from the session store (whereever that is). Then ObjectDataSource/GridView/etc. can set the just properties bound on that specific view.
The easiest implementation would be to call an Acquire method (parameterized with the declare key values) and use that object. This would be an overridable method that could in the base implementation simply do the current behavior of an ex-nilo call to the DataObjectType's default constructor. In derived classes, we can offer whatever symantics we need (including cache, session, ORM, or web-service based object acquisition).
Of course we could do this ourselves using a derived ObjectDataSource and ObjectDataSourceView, except they didn't do the implementation of that correctly in the framework. ObjectDataSource never calls the derived-classes GetView(String) method. If they simply fixed that so that ObjectDataSource's GetView() method called GetView(String.Empty), I could do it myself.
If you agree, feel free to vote on MSDN Product Feedback Center
Posted by IDisposable at 3/03/2006 01:44:00 PM 2 comments
I got a report of a bug in the DynamicComparer sources when comparing Enum values. Turns out that you need to box the values before you call System.Enum.CompareTo()
.
I've made the necessary changes in both the DynamicComparer.zip and Utilities.zip files.
RE: Dynamic sorting of objects using lightweight code generation.Posted by IDisposable at 3/01/2006 11:49:00 AM 0 comments