Bye Bye, Web ADF

Dave Bouwman noticed that ESRI has annouced that the Web ADF will be deprecated after ArcGIS 10. To me that’s not very surprising. I’ve blogged about my dislike to this framework more than once, and to me its death cannot come any sooner. I guess ESRI noticed that the simplicity of the REST API’s is winning the customers, and decided to back off the wretched thing. So is this good news? Ultimately, yes. The less people trying to make software with the ADF, the better.

But it is a mistake that ESRI should have noticed sooner. To think about the amount of changes the ArcGIS Server APIs went through since version 9.1 makes my head hurts. Upgraded from 9.1 to 9.2? Poof! The framework completely changed and WebADF with all new controls was born. 9.2 to 9.3? Poof! The Ajax framework got replaced and most of your Javascript code can go to the recycle bin. Wanted to change to the new and much better Javascript API? Better get on the dojo train and rewrite everything yet again.

If you were to try to take advantage of the greatest new features of each release, you probably had to rewrite your application every time. ESRI’s idea of backward compatability tends to be “let’s hope our customers won’t notice that we changed everything yet again”.

Posted by dorony | with no comments
תגים:

Easy Testing Of NHibernate.Spatial Code

In my current project we’re using NHibernate.Spatial against an SQL Server 2008 DB. This replaces our need to work with the cumbersome ArcSDE when writing and reading data in our applications (while still allowing ArcMap users to view the maps it holds by registering the tables in SDE). This made our life so much lovelier. We now have real domain objects to work with, and absolutely no COM is required to do stuff!

Anyway, we’re using the awesome Fluent NHibernate to map all our entities, and in order to test them, we have a base class (something like this) that all our tests use. The problem is, we want our tests to run against the in-memory SQLite database, but it doesn’t support the spatial stuff, and there is no NHibernate.Spatial dialect for it. A problem indeed – most of our code has nothing to do with geography, and it would be a damn shame if we couldn’t use an in-memory database to test it easily.

Well, we ended up using a cool feature of Fluent NHibernate, that allows us to specify conventions for our class maps. For instance, lets say we have a Country entity, which looks like this:

   1: public class Country
   2: {
   3:     public virtual int Id {get; set;}
   4:     public virtual string Name {get; set;}
   5:     public virtual IGeometry Location {get; set;}
   6: }

And the mapping for this class looks like this:

   1: public class CountryMap : ClassMap<Country>
   2:    {
   3:        public CountryMap()
   4:        {
   5:            Id(c => c.Id);
   6:            Map(c => c.Name);
   7:            Map(c => c.Location).CustomType(typeof(GeometryType));
   8:        }
   9:    }

This mapping class is problematic with SQLite, as this database doesn’t support using the custom GeometryType (which implements NHibernate’s IUserType) that comes with NHibernate.Spatial. If only we could do without specifying the custom type in our mapping, and use a different one in our tests files… Oh! Fluent NHibernate conventions let us do just that. We can use two different conventions, one for SQL 2008, which we will use in our production code, and one for SQLite, which will be used in most tests. Here’s how our convention for SQLite looks like (the one for SQL 2008 is exactly the same, only uses the above GeometryType custom type):

   1: public class SQLiteGeometryTypeConvention: IPropertyConvention
   2:    {
   3:        public void Apply(IPropertyInstance instance)
   4:        {
   5:            if (instance.Property.PropertyType.Equals(typeof(IGeometry)))
   6:            {
   7:                instance.CustomType(typeof (SQLiteGeometryType));
   8:            }
   9:        }
  10:    }

You can see that we’re using SQLiteGeometryType. If you don’t know what that is, it’s OK. It’s our own simple implementation, as defined below:

   1: public class SQLiteGeometryType: GeometryTypeBase<string>
   2:     {
   3:         public SQLiteGeometryType() : base(NHibernateUtil.String)
   4:         {
   5:         }
   6:  
   7:         protected override string FromGeometry(object value)
   8:         {
   9:             return value != null ? ((IGeometry) value).AsText() : null;
  10:         }
  11:  
  12:         protected override IGeometry ToGeometry(object value)
  13:         {
  14:             return value != null ? new WKTReader().Read((string) value) : null;
  15:         }
  16:     }

All we do here is tell NHibernate that when it sees an IGeometry property that it should persist, it should convert it to a string and save it as varchar. It also goes the other way around. GeometryTypeBase is a helper base class that exists in NHibernate.Spatial.

Now all that is left for us is to configure NHibernate to use our convention:

   1: Fluently.Configure()
   2:                .Database(SQLiteConfiguration.Standard.InMemory)
   3:                .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Country>()
   4:                                   .Conventions.Add(new SQLiteGeometryTypeConvention()));

And now we’re done. We can persist our spatially-enabled entities to SQLite and write tests for them. Granted, we can’t use more advanced features like spatial queries that run in the database, but most of our code doesn’t do that anyway. Having an in-memory database is perfect for testing the OR mappings and domain service operations.

The ideal workplace

In less than three months I will be leaving my current position, and I started thinking about the kind of place I want to work at. Here are the points I thought of, with the most important ones appearing first:

  1. Awesome developers to work with. In my current workplace I’ve been one of the most veteran developers for a while, and it naturally put me in a teaching position. While I like teaching, I feel that the best way to learn is from others, and lately I’ve been learning less. In my ideal workplace I would be the junior developer, where people point at my code and say “yeah, but you could have made it a lot simpler by…”. So, smart, talented developers to learn from is definitely the most important thing to me in the new place.
  2. An interesting product. I want to work on something I believe in. I prefer working for a start-up company doing something very cool, over a safer enterprise environment doing in-house development. Also, I’m young and don’t mind working hard if I’m having fun and there’s a chance to hit the jackpot :)
  3. An agile environment. I was one of the people who implemented agile methodologies at my current place, and I would really want it to already exist in the new place. It’s not an absolute must for me, I guess, but if I have to convince everyone, yet again, that unit-tests are very very important, I might get very tired indeed.
  4. A comfortable working environment: a strong computer with a two monitor setup, a good chair (not a must, I guess, I can bring my own as I did in the last place), quiet offices with no more than 2 or 3 developers in each. The best software tools should be available, or at least a management that agrees to buy the stuff the developers need. I shouldn’t have to beg for a ReSharper license, right?
  5. Great atmosphere. Because work should be fun, not a chore.

I know it might not be realistic to find a job at such a place, but I would be very excited if I could.

If you live in Israel, and happen to work in my ideal workplace (or even your ideal workplace), and you’re looking for someone who loves developing great software, you can contact me and I’ll send you my C.V.

Posted by dorony | 1 comment(s)

A Spoon That Tests Your Web Site

This is just a quick recommendation: I recently discovered Spoon’s browser sandbox, which allows you to test a site against any browser. This way, you don’t have to keep your own virtual machine with XP just to test your web-app with IE6. I also found it useful when browsing older web sites that don’t support my choice of browser. Spoon does require you to install a plug-in in your PC, but after that it worked flawlessly for me.

Posted by dorony | with no comments
תגים:

PowerShell script to convert VSTS tests to NUnit

A while ago we decided to convert all our test projects to NUnit. We found Microsoft’s test framework to be overly complicated, much slower, and with less features.

So, in order to make the transition easier, I wrote this PowerShell script to convert VSTS test projects and files to NUnit. I didn’t know any PowerShell beforehand, so it took me a bit to get it right, but the script works nicely now. We’ve run it against dozens of test projects (VS 2005, VS 2008, and it also works with VS 2010 beta 2, as I tried it at home) in our company and it did the job. It should be noted that the script only works against NUnit 2.5 and above, and while it has worked well for us, you’re using it at your own risk. If you have source control, and of course you do, then you’re not risking anything anyway :)

You can download the script right here.

In order to use it you’ll need to:

  1. Download and install PowerShell. You already have it if you’re running Windows 7.
  2. Run PowerShell, and run Set-ExecutionPolicy “unrestricted”, so you’ll be able to run scripts. Under Vista\7 you’ll have to run PowerShell as administrator to get this done.
  3. Copy the script to a solution or a VSTS test project folder (I do recommend doing the conversion one project at the time, but the script just scans for .cs and .csproj files, so you can run it against any number of projects). This step is optional, you can also provide the working directory as a parameter to the script.
  4. Check-out all the project’s files from source control, if you need to.
  5. In PowerShell, run “.\VstsToNUnit.ps1 [NUnit Version] [Path from .csproj file to NUnit.framework.dll] [Working directory (optional if the script is copied to the project folder)]
    Example: .\VstsToNunit.ps1 2.5.3.0 ..\lib\nunit.framework.dll c:\myprojects\MyVisualStudioProject

What does this script do? Well, its essentially a bunch of regex replacements running against every .cs and .csproj file in the working directory. It replaces the VSTS attributes with NUnit ones, changes VSTS test projects to standard class libraries, and adds a reference to NUnit instead of the VSTS one. It will also:

  1. Save you from a few gotchas, such as the arguments order in Assert.IsInstanceOfType being different between the two frameworks.
  2. Remove some TestContext stuff Visual Studio generates automatically when you use the unit-tests generation feature. Note, however, that if you actually used the TestContext object you’ll have to refactor your unit tests.
  3. For users of Rhino.Mocks.Constraints, adds stuff like “using Is=Rhino.Mocks.Constraint.Is”, as NUnit also defines Is\List\Text classes, and it causes ambiguity when wanting to use the Rhino Mocks constraints.

Stuff the script won’t do:

  1. As I said, remove all the TestContext references. You’ll need to get some of those manually.
  2. Remove the [DeploymentItem] attribute. You’ll need to manually delete those, while making sure your test files are copied to the output directory for NUnit to find (right-click the file in your solution, and in the properties select “Copy to output directory – always”.
  3. Delete .vsmdi and .testrunconfig files. You’ll have to get them yourself. Wouldn’t want to steal that joy from you :)
  4. Perhaps a bunch of other VSTS specific stuff our projects don’t use.

If you’re using this, please let me know if you ran into any issue, or anything that should be added to the script, and I will update it here.

Posted by dorony | 1 comment(s)

Visual Studio 2010 Beta 2: My 5 Minutes Impressions

So I just installed VS 2010 beta 2, played with it for 5 whole minutes, and I have to say it looks pretty damn good.

  • Performance was horrid on Beta 1, but now it seems to be much improved.
  • Startup time is still too long if you ask me.
  • Opening the Fonts and Colors dialog is still the slowest thing in the world.
  • But the Add Reference dialog opens really fast! Finally they realized it is better to default on the project reference tab there.
  • The new WPF UI is beautiful. I like the “Code Optimized” web settings that remove all the non-critical toolbars and panels and leave lots of room for the code.
  • The new IntelliTrace (or historical debugging) feature looks very interesting.
  • F# Interactive seems to always be open, even on a C# project. Could be a useful tool, so I should probably get serious about learning F#.

I would also like to point out this excellent article, which talks about the VS2010 architecture. It’s great to see how Microsoft “dogfoods” its own products and in the process makes them better for the rest of us. It stands to reason that if WPF can work for a project like Visual Studio, and TFS can handle the entire Microsoft organization, these could be good choices for us simpletons.

Posted by dorony | with no comments
תגים:

Running VSTS tests without mstest.exe

I’ve been looking at ways to run VSTS tests without using the actual Visual Studio testing framework. It all started when I wanted to integrate code-coverage in our TeamCity daily build. All the solutions I found were using the NUnit runner for this, and while I could have used mstest.exe to run the tests when I’m gathering code-coverage, I didn’t want to. Why? Well, because:

  1. TeamCity doesn’t use it to run the tests, so I might get different result from the regular test-run and the code-coverage test-run.
  2. It requires me to install Visual Studio on the server.
  3. It creates annoying TestResult folders.
  4. It requires maintaining a testrun.config file.

In essence, using mstest.exe just seems a lot more cumbersome. Still, all our tests are indeed using the VSTS attributes, so I need a way to run them “NUnit-style”, i.e. without mstest.exe. TestDriven.Net and TeamCity already seem to be doing this, but without exposing anything that I could use. Google did not help, neither. So, I turned to ask at Stackoverflow.com. After a while, an answer was received by a fellow named Bryan Cook: an addin to NUnit exist, but alas, it turns out it doesn’t support the latest NUnit version. Kindly enough, Bryan just went and fixed it. Not a trivial task, mind you, as I had a look at the code, and it seems that the NUnit API went through quite a lot of changes going into version 2.5.

Anyway, you can go over and download the addin from right here. Thanks Bryan!

On a different note, we ended up deciding to switch all our tests to NUnit at the end, but I’m sure this could come handy in the future :)

Posted by dorony | 3 comment(s)
תגים:

Improving Upon LINQ’s Distinct

Suppose you have a collection of objects, and you want only the distinct values. You could easily use Linq for this, like that:

var items = new[] {"BMW","Fiat","Ferrari","Fiat"};
var distinctItems = items.Distinct();

This uses the default comparer for the objects in order to see if they’re equal. There’s also another overload, which lets you specify your own comparer. This is its signature:

public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);

It accepts an IEqualityComparer to compare between items. But the usage of IEqualityComparer is a bit annoying, as you have to create you own class that implements that interface. It would be nice if I could just do this:

var items = new[] {"HI", "hi","hello","HELLO"};
var distinctItems = items.Distinct((str1, str2) => str1.ToLower().Equals(str2.ToLower()));

So in this example we would expect the result array to contain two items and not four.

Let’s see if we can make this happen. We’ll create our own IEqualityComparer which accepts a lambda.

   1: public class LambdaEqualityComparer<T> : IEqualityComparer<T>
   2: {
   3:     public Func<T,T,bool> _comparison;
   4:  
   5:     public LambdaEqualityComparer(Func<T,T,bool> comparison)
   6:     {
   7:         _comparison = comparison;
   8:     }
   9:     
  10:     public bool Equals(T x, T y)
  11:     {
  12:         return _comparison(x, y);
  13:     }
  14:  
  15:     public int GetHashCode(T obj)
  16:     {
  17:         return obj.GetHashCode();
  18:     }
  19: }

Here we have a class that accepts the comparison lambda as a parameter. Now we can add our own Distinct extension method:

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, Func<TSource,TSource,bool> comparison)
    {
        return source.Distinct(new LambdaEqualityComparer<TSource>(comparison));
    }
}

Once again, let’s try to run this:

var items = new[] {"HI", "hi","hello","HELLO"};
var distinctItems = items.Distinct((str1, str2) => str1.ToLower().Equals(str2.ToLower()));

Well, that didn’t work out well at all. It returns a four items array. Digging into it a bit, we can see that the problem is in our LambdaEqualityComparer class, specifically with our naive GetHashCode implementation (lines 15-17 above):

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

As you can see our comparison doesn’t come into play at all for GetHashCode. It also appears that the original Distinct uses (at least in some cases) GetHashCode and not Equals to determine if two objects are equal, and so our lambda comparison doesn’t even run.

We need to attack this differently, then. Let’s try to make this code work:

var items = new[] {"HI", "hi","hello","HELLO"};
var distinctItems2 = items.DistinctBy(str1 => str1.ToLower());

So in this new DistinctBy method, we’re not comparing two items, but selecting something to compare by for every item. The implementation is similar to before, and here is the entire thing:

   1: public static class EnumerableExtensions
   2: {
   3:     public static IEnumerable<TSource> DistinctBy<TSource, TSelection>(this IEnumerable<TSource> source, Func<TSource, TSelection> selector)
   4:     {
   5:         return source.Distinct(new SelectorEqualityComparer<TSource, TSelection>(selector));
   6:     }
   7:  
   8:     private class SelectorEqualityComparer<TSource, TSelection> : IEqualityComparer<TSource>
   9:     {
  10:         public Func<TSource, TSelection> _selector;
  11:  
  12:         public SelectorEqualityComparer(Func<TSource, TSelection> selector)
  13:         {
  14:             _selector = selector;
  15:         }
  16:  
  17:         public bool Equals(TSource x, TSource y)
  18:         {
  19:             return Object.Equals(_selector(x), _selector(y));
  20:         }
  21:  
  22:         public int GetHashCode(TSource obj)
  23:         {
  24:             return _selector(obj).GetHashCode();
  25:         }
  26:     }
  27: }

As you can see, both the implementation of GetHashCode and Equals now use our selector lamda, and so the code works, and all is well with the world.

Posted by dorony | 4 comment(s)
תגים:,

ArcGIS Web ADF 9.3 – Is it Any Better?

A while back I’ve posted my opinion about the ArcGIS Web ADF 9.2. It wasn’t a positive one. Since then, I’ve had a chance to work a little with the new version – the ADF 9.3, and even attended a seminar held by Rex Hansen, which – as I understood - is one of the main contributors to the design of the Web ADF.

On the surface it seems that the Web ADF took a leap forward at 9.3. It is now built upon the Microsoft Ajax library, instead of ESRI’s own callback-based solution (you know, the whole ICallbackEventHandler headache). Performance should be better, as the map now uses a custom .ashx handler to serve the images, instead of doing a bunch of full postbacks. But the question that remains is – Is the API any better? Are simple stuff simple? Is customization any easier?

Sadly, the answer to that seems to be a big NO. Almost none of the issues I addressed in my original post were fixed. The API is still overly complex. Resources, functionalities and all these so-called abstractions remain in place, emphasizing the strength of the JavaScript API’s simplicity. And yes, there are still about a billion classes that are named “Converter” in the API. It seems like ESRI insists that you write the entire namespace before every class you use.

Ajax is a bit easier this time around, since we can use ScriptManager to register client scripts, but it still took us a while to figure out how to make it all play nicely together (this article provides some solutions, by the way). I hate having to read mountains of documentation before I can get anything done (on the bright side – at least there is documentation), and trying to explain a newbie programmer about the difference between a “partial postback” and a callback is hard.

There are some more unpleasant surprises for you here. Noam Sheffer (hebrew link) mailed me about a problem he had. It seems that the map control still initiates a full postback to the page with every zoom operation.  This is of course bad for performance, and also caused him issues with maintaining the state of some of his controls. Checking out an example site with Fiddler or Firebug, can show you that this is indeed the case.

And a word about the seminar by Rex Hansen. He showed some of the product’s new features, performance tips, and solutions to common problems. Thing is, I don’t care about being able to customize the MapTips template. It shows that ESRI focus is on the wrong thing – instead of simplifying the API, they’re adding more features so that it is even more complex.

Therefore, I still recommend anyone with a choice to go for one of the REST APIs. I’ve only worked with the JavaScript APIs, but I hear that Silverlight and Flex are similar and are equally simple. For now, I’m keeping my strong belief that doing anything complex with the Web ADF is putting yourself in a world of hurt.

Posted by dorony | 5 comment(s)
תגים:

ArcGIS JavaScript API: IE Memory Leak Fix

Since version 1.2 of the ArcGIS Javascript API, we've noticed a strange issue in both IE6 and IE7. When zooming in, memory consumption of the iexplore.exe process takes a huge jump and reaches up to 800-900 megabytes. When the operation ends, it goes back down to 50-60 megabytes. This has caused us a great deal of sorrow, as it sometimes caused our users' systems to hang.

This post in the ESRI forums explains, and also provides a fix. Apparently, the upgrade to dojo 1.2 has caused this zoom-animation related issue. To fix it, you should create a javascript file with this text:

 

/* * override the dojo 1.2, 1.3 version of dojo._setOpacity to revert back to the dojo 1.1 implementation * this version doesnt allow multiple filters on an element, but it is free from the memory spike issue */ (function() { var d = dojo; if(dojo.isIE) { dojo._setOpacity = function(/*DomNode*/node, /*Number*/opacity){ if(opacity == 1){ var filterRE = /FILTER:[^;]*;?/i; node.style.cssText = node.style.cssText.replace(filterRE, ""); if(node.nodeName.toLowerCase() == "tr"){ d.query("> td", node).forEach(function(i){ i.style.cssText = i.style.cssText.replace(filterRE, ""); }); } }else{ var o = "Alpha(Opacity="+ opacity * 100 +")"; node.style.filter = o; } if(node.nodeName.toLowerCase() == "tr"){ d.query("> td", node).forEach(function(i){ i.style.filter = o; }); } return opacity; }; } })();

 

Reference this javascript file from every page that has a reference to the Javascript API, and you're good to go.

Still, one can't help but wonder how the ESRI QA guys didn't notice this obvious shortcoming (which will be fixed at 1.4, they claim). Maybe they only used machines with lots of RAM. That's how we missed this at first... :)

Writing Maintainable Javascript Guidelines

We've been doing a lot of Javascript development lately, while writing an Ajax-based web application. Up until recently, my team's Javascript experience consisted mainly of the occasional disable-that-checkbox function inside an .aspx web-form. Nothing wrong with that, I guess, but it doesn't really count. If you're going to write a lot of Javascript, you have to do so in an organized and clean way, or you'll have a maintenance nightmare on your hands.

This is not because Javascript is a bad language. It can be quite beautiful actually. But it does allow to work in ways that are not elegant, so I would like to show you some tips on what you should do, and what you should avoid - Framework Design Guidelines style.

DO use separate .js files to store your Javascript code.

AVOID writing a lot of Javascript inside an .aspx form.

When you do the shift to a heavily scripted client, and you begin writing loads of Javascript, this approach fails miserably, for several reasons:

  1. Performance - .js files are usually cached on the client side, and .aspx are not, as they usually contain dynamic data. Also, .js files can usually be compressed, so they get even lighter to download.
  2. Maintenance - It is just easier to maintain the script code when it is in a separate file.

DO use different .js files for different modules.

It might seem obvious, but I've seen .js files that contain thousands of lines of un-related code. Again, this is hard to maintain. In C# we like having a separate file for each class, don't we? Well, Javascript shouldn't be a lot different. If you're worried about the performance penalty of having many files, there are tools you can use to combine your scripts to a single file on deployment.

DO use a Javascript library instead of rolling your own infrastructure.

Really, these days it is just silly to write your own Ajax implementation, or your own collection types. Libraries like jQuery, ASP.NET Ajax and Dojo, already do so much for you. This will save you a lot of time and effort, and will also allow you to create cross-browser Javascript that works even on crappy-old browsers such as IE6.

CONSIDER writing only object-oriented Javascript.

Most Javascript libraries will allow you to write your code in an object-oriented manner, based on the prototype model available in the language. Object oriented code is easier to follow and understand. And yes, you even get the benefits of inheritance, design-patterns and all that good OO stuff you know from C#. Here's how a class in the Dojo library is declared:

dojo.declare("Person", null, { constructor: function(name, age, currentResidence){ this.name=name; this.age=age; this.currentResidence=currentResidence; }, moveToNewCity: function(newState) { this.currentResidence=newState; } });

As you can see, we have a constructor, fields, methods and all.

AVOID using a lot of global variables and functions.

This is really just good programming behavior. Globals are bad! That's what they always used to tell you, and this is still correct. Unlike C# or Java, Javascript allows you to define global variables and global functions. In fact, for many this is the standard way of scripting. I know it was for me. But when you start having hundreds and thousands lines of code, which access some variable that was defined in god-only-knows-where, you'll see that it's a bad idea. When you have 5, 10 or 20 .js files, you just don't want to take the chance that two of them define the same function or variable, do you?

Granted, you can't avoid using some globals, but you should keep this to a bare minimum. Our pattern for global functions is to use them as gateway-only to some class. Usually they will be event-handlers which just create a class and call one of its methods. We pass data around from constructor to constructor instead of defining it globally.

AVOID specifing an Html element ID in a class declaration.

This is bad:

dojo.declare("ButtonHider", null, { hide: function() { $get('MyButton').style.display = 'none'; }

This is good:

dojo.declare("ButtonHider", null, { hide: function(buttonId) { $get(buttonId).style.display = 'none'; }

Also good:

dojo.declare("ButtonHider", null, { constructor: function(buttonId) { this.buttonId = buttonId; } hide: function() { $get(this.buttonId).style.display = 'none'; }

Not only this makes your code more generic, but in ASP.NET is essential. ID's for server controls can easily be changed if they're dropped inside some container. You'll want to pass the ClientID property of the button you want to hide to the class above, and not the actual ID.

DO remember that Javascript is a functional and dynamic language, and use that to make your code more readable.

Functions can be passed around as data in Javascript. Properties and methods can be added to any object. You can misuse these features, of course, but when used correctly they make Javascript a very powerful language. Having classes doesn't make it C# code! Here's a StackOverflow thread to get you started on some of Javascript's less known features.

DO maintain coding standards to your Javascript.

If you want your code to be readable, you need to have standards. Among other things, we use camelCase naming for functions, use underscore prefix to signal "private" members (even if the language does allow them to be accessed outside, the underscore suggest the user of your code to not do so), and use the syntax 'var arr = [];' to define an Array (and never 'new Array()'). What are your standards? Define them, and have everyone follow them.

CONSIDER writing unit-tests for your Javascript.

I admit we're still taking baby-steps in that direction, but as our code-base gets more and more complex, we'll have to cover it with unit-tests for it not to break on us. Here are some frameworks recommendations we're considering to aid us in that.

To conclude, I would like to say that crappy code can be written in any language. If your application is Javascript oriented, it is no excuse to write bad code. The guidelines I have put are nothing new. Mainly, they only remind you to also use them when writing Javascript, and not only in your favorite compiled language.

Posted by dorony | 3 comment(s)
תגים:

ESRI JavaScript API: Now with Error Handling

The JavaScript API v1.3 has been released, and it now supports error and timeout handling. Finally! We actually had gone and implemented this feature ourselves by overriding the esri.request function, but it was a hack that we'll be glad to be rid off. I can't really understand why this wasn't there to begin with, but still - happyness.

Posted by dorony | with no comments

Pretty Quick Sort With C# 3.0

Jafar Husain writes about the prettiness of F#'s type inference, but what I really liked in his post was his C# 3.0 implementation for Quick Sort:

1 public static IEnumerable<T> QuickSort<T>(this IEnumerable<T> list) 2 where T : IComparable 3 { 4 if (!list.Any()) 5 { 6 return Enumerable.Empty<T>(); 7 } 8 var pivot = list.First(); 9 var smaller = list.Where(item => item.CompareTo(pivot) <= 0).QuickSort(); 10 var larger = list.Where(item => item.CompareTo(pivot) > 0).QuickSort(); 11 12 return smaller.Concat(new[]{pivot}).Concat(larger); 13 }

Yes, the F# implementation is more concise, but Jafar really reminded how concise can C# be as well. When I first heard about C# 3.0's var keyword, I belonged to the group that worried about code getting unreadable. Now I've grown to realize that type information can be over-rated. Sometimes it is just irrelevant noise.

Posted by dorony | 6 comment(s)
תגים:

Extending the ArcGIS Javascript API

Working with the new Javascript API for ArcGIS Server 9.3, I am now writing quite a bit of javascript. If you try and write large amounts of javascript code (I would consider >200 lines as large enough) without using objects, soon enough you're going to have an unreadable nightmare in your hands.

In order for the code to be maintainable, I've realized I have to take advantage of dojo's object-orientation capabilities, and especially inheritance. This allows me to extend ESRI's default objects, compensate for missing behaviors, and add logic of my own. Luckily, in javascript the can't mark their objects sealed, or need to remember to mark methods as "virtual", so extending this API's objects is easier than doing so in the ADF.

For instance, suppose you want an ArcGISDynamicMapServiceLayer to tell you which layers are currently visible for it. Thing is, the API only exposes a setVisibleLayers method, but not a getVisibleLayers(). So let's try and add this, shall we?

1 //doron.common.js 2 dojo.require("esri.map"); 3 4 dojo.declare("Doron.ExtendedDynamicMapServiceLayer", esri.layers.ArcGISDynamicMapServiceLayer, { 5 constructor: function () { 6 dojo.connect(this, "onLoad", function () { 7 this._currentVisibleLayers = this._getDefaultVisibleLayers(); 8 }); 9 }, 10 setVisibleLayers: function(layers) { 11 this._currentVisibleLayers = layers; 12 //dojo's way of calling base method - so that default behavior is preserved 13 this.inherited(arguments); 14 15 }, 16 getCurrentVisibleLayers: function() { 17 return this._currentVisibleLayers; 18 }, 19 _getDefaultVisibleLayers: function () { 20 var defaultVisibleLayers = []; 21 for (var i=0; i < this.layerInfos.length; i++) { 22 if (this.layerInfos[i].defaultVisibility) 23 defaultVisibleLayers.push(this.layerInfos[i].id); 24 } 25 return defaultVisibleLayers; 26 } 27 });

I am using dojo.declare to declare my new class, which inherits from esri.layers.ArcGISDynamicMapServiceLayer. My constructor first saves the layers that are visible by default in a field (this can only be done once the layer is loaded, so I'm registering myself to that event). I also override setVisibleLayers(), so that when it is called, I remember to update that field as well.  Now I can easily expose the getCurrentVisbleLayers() method.

This class is something you can put in a .js file and reference everywhere you want - it is a much better approach than putting all your scripts inside the .html/.aspx/.whatever file.

A page that would use it, might look like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Dynamically Create Map Service Layer List</title> <link rel="stylesheet" type="text/css" href="http://serverapi.arcgisonline.com/jsapi/arcgis/1.1/js/dojo/dijit/themes/tundra/tundra.css"> <script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.1"></script> <script type="text/javascript" src="doron.common.js"></script> <script type="text/javascript"> dojo.require("esri.map"); function init() { map = new esri.Map("map"); layer = new Doron.ExtendedDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer"); map.addLayer(layer); } function showVisibleLayers() { alert( layer.getCurrentVisibleLayers().join(',')); } dojo.addOnLoad(init); </script> </head> <body> <div id="map" class="tundra" style="width:600px; height:400px; border:1px solid #000;"></div> <input type="button" onclick="showVisibleLayers()" value="Show Visible Layers" /> </body> </html>

Here I just took one of ESRI's samples, and replaced their class with mine. I also added a button that shows the layers that are currently visible. The reference to "doron.common.js" allows me to use my own class that overrides the ESRI one. 

Hope this was helpful. If you know of any more ways to make life with the Javascript API more comfortable, feel free to share.

ArcGIS Server 9.3 Javascript API Impressions

After a long wait, I've finally been able to start using the new Javascript API that comes with ArcGIS Server 9.3. And the truth? It's pretty great.

It was difficult to start with, though. I didn't realize that the actual API (.js files and images mostly) doesn't come with the AGS installation discs - you have to request that separately. I have no idea why they won't just let you download an installation zip, as anyone is free to use the API via this link here. Our internal network doesn't have an Internet connection, and even if it did, I wouldn't want to rely on unneeded external resources in my application.

Well, I finally got the bits and "installed" them - and by that I mean followed the readme.html file that comes with the disc and tells you to copy this directory over there, and edit that .js file and that .CSS file. An installation script would have been in order, and now I have to create one myself, which I am too lazy to do, but I will. I promise.

Also, the installation doesn't come with an esri.debug.js file as is costumed with Javascript API's. The .js file is a "compiled" one, and is completely obfuscated and unreadable. Gosh, I can't understand ESRI's constant need to obfuscate everything they make. The Web ADF DLLs are also obfuscated, and the only ones they're hurting are the developers who use their product. If you still wish to be able to "step into" the ESRI scripts, I recommend that you use this website here to create your own esri.debug.js. It still won't be the clearest thing, as all the variables will still be 'meaningfully' named, but it is better than nothing.

Well, anyway, enough ranting, eh? Finally there's an API from ESRI that I rather like! You can produce with this mapping applications with code that is actually readable. Look at this sample (and view source) to see how easy it is to create a map with a toolbar. And the beauty is that it is easy even without a designer. I guess that since they couldn't rely on anyone using Javascript to use a designer, they had to make the code usable, and they got it right.

The outcome is that the code is a lot more extensible. It took me a few minutes to change this code to use the RadToolbar control. In the Web ADF, on the other hand, the controls are meant to use as-is (preferably with the cool-yet-kinda-silly "application builder" they have inside the manager application. Or at least the VS2005 designer). Therefore they are a lot less extensible. Yes, they give you more out of the box, like a TOC control. But it will cost you dearly to try to do something different. For this current application I have to build a dynamic TOC which can hold up to 200 layers, coming from different map objects, depending on the user. The prospect of making that work with the Web ADF is a bit daunting, but combining the js API and the REST services of ArcGIS 9.3 should make this a lot simpler.

So for now I am rather pleased. Things behave as they should - there aren't many surprises and heads banging on desks. I am a bit concerned about writing more Javascript - we are still at VS 2005, so no intellisense for us, and a heavy reliance on the documentation (which is pretty good, by the way). Another concern is the usage of another Javascript library - Dojo. We are already using ASP.NET Ajax (the RAD controls use this as well), and now adding Dojo and ESRI's own .js files, and maybe even JQuery I've been hearing a lot about - this is going to make for a big download to our clients. It does seem pretty weird that ESRI is using ASP.NET Ajax for the new .NET ADF controls, and Dojo for the Javascript API. Oh well.

To summarize, I would say ESRI has a winner with the new REST + Javascript combination. I doubt that I would now bother with anything that has ADF in its namespace - I'm guessing I would use the Javascript API for the client side, and open a direct ServerContext if I need to do something on the server. I'll keep you posted on how this worked for me.

Shana Tova!

 
More Posts Next page »