Wednesday, January 18, 2012
It seems that I'm always struggling to simplify my code. As such, I was playing with
Knockout today, and found that in multiple places, I needed a string containing concatenated values of multiple observables
Take this simple example of binding a dropdown to a view model, and also showing the selected value in a span. The items being bound are not simple strings, but rather, are complex objects containing multiple attributes. The string that I ultimately want to display is a concatenation of the first and last names of the employee, but without doing anything special, the rendered text will be: [object Object]
Note: I seem to be having formatting issues with embedded jsFiddle iframes... I'll just provide links to the actual fiddles.
Fiddle 1
To do the concatenation for the dropdown's items, I could have provided a function for optionsText in the options binding.
Fiddle 2
The selected object itself is being persisted in the view model as the observable named "selectedEmployee". This is what's bound to the span's text, so once you select an employee, the same [object Object] issue arises.
You can provide a simple expression to Knockout's Text binding, but you cannot "dot into" an object in order to access sub properties. However, you can use the With binding to set the context for a block of markup that is being bound, and then be able to use the simple expression in the Text binding (referencing the "properties" of that context):
Fiddle 3
This does what I wanted, but now I have that code repeated twice. I would really like to have something that performed the operation only once.
Then it dawned on me: since Knockout's default behavior for complex types is to call toString(), I should just overload the toString() function on the employee object itself. Then, I wouldn't need to do anything special!
function employee(firstName, lastName, employeeID)
{
this.employeeID = ko.observable(employeeID);
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.toString = ko.computed(function() { return firstName + ' ' + lastName });
}
Fiddle 4
.
Wednesday, July 27, 2011
Background
From the Wikipedia article, Leadership in Energy and Environmental Design (LEED) is described as “an internationally recognized green building certification system, providing third-party verification that a building or community was designed and built using strategies intended to improve performance in metrics such as energy savings, water efficiency, CO2 emissions reduction, improved indoor environmental quality, and stewardship of resources and sensitivity to their impacts.”
In my own words, LEED is a certification system that awards points for following certain environmentally-friendly practices when constructing a building. In the end, a building can be qualify for one of four different levels of certifications, based on the number of points: Certified, Silver, Gold, Platinum. There are often tax benefits associated with having a LEED certification, and many new government buildings (especially Federal) are required to be LEED certified.
Two points in particular (out of of 100, or so) from the LEED checklist are related to geospatial data. One point is awarded if at least 20% of the building materials (by cost) used in construction were manufactured within 500 miles of the job site. A second point is awarded if 10% of the raw materials of those building materials were extracted, harvested, or recovered within 500 miles of the job site.
As a window glass manufacturer, Tempuri Glass is often asked to provide data about its products that are being considered for use in construction. Tempuri Glass may have a certain advantage over its competitors if it can quickly show that its products would count towards these two points for any arbitrary job site.
Data
Tempuri is a simple organization, making only a single type of product (Soda Lime glass) that is then cut into different sizes per order. Therefore, regardless of how many different sized glass panes are produced by a given facility, the ingredients for that glass is the same. The formulas used will be different between facilities, though, since the raw ingredients will be sourced from different locations, and adjustments may need to be made to the ratios due to environmental factors (things like: elevation, temperature, humidity, etc).
So, for our data model, we just need to know where each facility is, and then the formula used to make the glass at that facility (including the ingredients of that formula and the location where they were harvested from).
Within the data store, the [Geocode] columns of the Facility and FormulaComponent tables use the SQL Server geography type. This is useful for the large-scale/real-world distance calculations that Tempuri Glass needs to perform, since the way that you calculate distance on an sphere or ellipsoid (like the Earth) is vastly different than on a flat map.
In the Entity Framework model (using the June 2011 CTP), the SQL Server geography types are mapped as the new System.Data.Spatial.DbGeography type. This makes the geospatial data a first class citizen of our data model, and not just a castable opaque BLOB, as was the case in the past.
Geospatial data can take on many forms, including Points, Line Strings, Polygons, and collections of these shapes. Even though it’s not apparent from the data model, our [Geocode] data will contain only Points (i.e., a single Latitude/Longitude pair). Likewise, a job site will be specified as a single Point, though there is no hard requirement for this because distance can still be calculated between a Polygon and a Point with no coding change required.
Facility Sample Data
|
FacilityID
|
FacilityName
|
City
|
State
|
Geocode
|
|
1
|
Greenfield, IA
|
Greenfield
|
IA
|
POINT (-94.4547843933106 41.3151755156904)
|
|
2
|
Spring Green, WI
|
Spring Green
|
WI
|
POINT (-90.053981 43.17431)
|
|
3
|
Tomah, WI
|
Tomah
|
WI
|
POINT (-90.477058 43.989319)
|
|
4
|
Fremont, IN
|
Fremont
|
IN
|
POINT (-84.9314403533936 41.7186070559443)
|
|
5
|
Fargo, ND
|
Fargo
|
ND
|
POINT (-96.8667125701904 46.8985894795683)
|
|
6
|
Waxahachie, TX
|
Waxahachie
|
TX
|
POINT (-96.8427014350891 32.4424403136322)
|
|
7
|
Hood River, OR
|
Hood River
|
OR
|
POINT (-121.51526927948 45.630620334868)
|
|
8
|
Vinton, VA
|
Vinton
|
VA
|
POINT (-79.863876 37.263329)
|
|
9
|
Casa Grande, AZ
|
Casa Grande
|
AZ
|
POINT (-111.78155422210693 32.882073958767954)
|
|
10
|
Mountain Top, PA
|
Mountain Top
|
PA
|
POINT (-75.896477 41.141327)
|
|
11
|
Winlock, WA
|
Winlock
|
WA
|
POINT (-122.926218509674 46.5449155194259)
|
|
12
|
Durant, OK
|
Durant
|
OK
|
POINT (-96.4133548736572 34.0001619910696)
|
|
13
|
Mooresville, NC
|
Mooresville
|
NC
|
POINT (-80.7865476608277 35.6316281732984)
|
FormulaComponent Sample Data
|
FormulaComponentID
|
Name
|
Percentage
|
SourceLocation
|
Geocode
|
|
14
|
Limestone
|
13
|
Genola, UT
|
POINT (-111.808204650879 40.0098667779887)
|
|
1
|
Silica Sand
|
75
|
Houck, AZ
|
POINT (-109.241695404053 35.2062151838369)
|
|
27
|
Soda Ash
|
12
|
Trona, CA
|
POINT (-117.311668395996 35.6955040738332)
|
|
15
|
Limestone
|
13
|
Genola, UT
|
POINT (-111.808204650879 40.0098667779887)
|
|
2
|
Silica Sand
|
75
|
Houck, AZ
|
POINT (-109.241695404053 35.2062151838369)
|
|
28
|
Soda Ash
|
12
|
Trona, CA
|
POINT (-117.311668395996 35.6955040738332)
|
|
16
|
Limestone
|
13
|
Chicago, IL
|
POINT (-87.6176834106445 41.5738476278005)
|
|
3
|
Silica Sand
|
75
|
Overton, NV
|
POINT (-114.4313621521 36.5146030619859)
|
|
29
|
Soda Ash
|
12
|
Green River, WY
|
POINT (-109.448783397675 41.5090754257687)
|
Spatial Querying Algorithm
Input: Job Site Latitude/Longitude
Steps:
A. Query for closest facility to Job Site within 500 miles:
- Calculate the distance between the job site and each facility.
- Filter the list of facilities to just those where distance < 500 miles.
- Order the list of facilities by distance in ascending order.
- The first element (if any) will be the closest facility, and also signifies that the product qualifies as being manufactured within 500 miles
B. If there is a facility within 500 miles, then sum the percentage of formula components that were sourced from within 500 miles of the Job Site:
- Calculate the distance between the job site and each of the facility’s formula components
- Filter the list of formula components to just those where distance < 500 miles
- Sum the Percentages
Output: Boolean of whether the product qualifies; Percentage of the product’s ingredients that qualifies.
Implementation
Before we can calculate distance using an instance method of the DbGeography type, we need to actually create an instance to represent the Job Site. DbGeography is immutable and does not have a constructor, so instead, a static method must be called to create a new object. There are a number of these factory methods available to create specific kinds of shapes (Point, Line String, Polygon, etc) given different kinds of input (text, byte arrays).
For simplicity, let’s use the .Parse() method, which accepts Well-Known Text (WKT) as input, and assumes a Spatial Reference ID of 4326 (the same coordinate system that GPS and internet mapping sites use).
Note: WKT uses a (Longitude, Latitude) ordering for points, which adheres to the same concept as (X, Y) ordering for Cartesian coordinates.
private static DbGeography CreatePoint(double latitude, double longitude)
{
return DbGeography.Parse(String.Format("POINT({1} {0})", latitude, longitude));
}
The first spatial query, written as a LINQ expression, finds the closest qualifying facility. Since SRID 4326 uses meters as the unit of measure, we need to convert 500 miles into meters within the predicate:
private Facility GetNearestFacilityToJobsite(DbGeography jobsite)
{
var q1 = from f in context.Facilities
let distance = f.Geocode.Distance(jobsite)
where distance < 500 * 1609.344
orderby distance
select f;
return q1.FirstOrDefault();
}
Assuming that a facility was returned, a second LINQ expression can be used to find the sum of Percentage from qualifying Formula Components:
private decimal SumQualifyingPercentages(Facility nearestFacility, DbGeography jobsite)
{
var q2 = from fc in nearestFacility.Formula.FormulaComponents
where fc.Geocode.Distance(jobsite) < 500 * 1609.344
select fc;
return q2.Sum(c => c.Percentage.GetValueOrDefault(0));
}
Finally, putting all of the parts together (using a Tuple<> for the output):
private Tuple<bool, decimal> GetResults(double latitude, double longitude)
{
DbGeography jobsite = CreatePoint(latitude, longitude);
Facility nearestFacility = GetNearestFacilityToJobsite(jobsite);
if (nearestFacility != null)
{
return new Tuple<bool,decimal>(true, SumQualifyingPercentages(nearestFacility, jobsite));
}
return new Tuple<bool, decimal>(false, 0);
}
private void PerformQuery()
{
double latitude = 47.63752;
double longitude = -122.13343;
var results = GetResults(latitude, longitude);
}
Thursday, July 21, 2011
| DbGeography Static Property |
Return Type |
DbGeometry Static Property |
Return Type |
| DbGeography.DefaultSrid |
int |
DbGeometry.DefaultSrid |
int
|
|
|
|
|
| DbGeography Static Method |
Return Type |
DbGeometry Static Method |
Return Type |
| DbGeography.FromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.FromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.FromGml(string geographyMarkup, int srid) |
DbGeography |
DbGeometry.FromGml(string geometryMarkup, int srid) |
DbGeometry |
| DbGeography.FromText(string geographyText, int srid) |
DbGeography |
DbGeometry.FromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.GeographyCollectionFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.GeometryCollectionFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.GeographyCollectionFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.GeometryCollectionFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.LineFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.LineFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.LineFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.LineFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.MultilineFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.MultilineFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.MultilineFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.MultilineFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.MultipointFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.MultipointFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.MultipointFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.MultipointFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.MultipolygonFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.MultipolygonFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.MultipolygonFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.MultipolygonFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.Parse(string geographyText) |
DbGeography |
DbGeometry.Parse(string geometryText) |
DbGeometry |
| DbGeography.PointFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.PointFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.PointFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.PointFromText(string geometryText, int srid) |
DbGeometry |
| DbGeography.PolygonFromBinary(byte[] geographyBinary, int srid) |
DbGeography |
DbGeometry.PolygonFromBinary(byte[] geometryBinary, int srid) |
DbGeometry |
| DbGeography.PolygonFromText(string geographyText, int srid) |
DbGeography |
DbGeometry.PolygonFromText(string geometryText, int srid) |
DbGeometry |
|
|
|
|
| DbGeography Instance Property |
Return Type |
DbGeometry Instance Property |
Return Type |
| g.Area |
double? |
g.Area |
double? |
|
|
g.Boundary |
DbGeometry |
|
|
g.Centroid |
DbGeometry |
|
|
g.ConvexHull |
DbGeometry |
| g.Dimension |
int |
g.Dimension |
int |
| g.EndPoint |
DbGeography |
g.EndPoint |
DbGeometry |
|
|
g.Envelope |
DbGeometry |
|
|
g.ExteriorRing |
DbGeometry |
| g.GeometryType |
string |
g.GeometryType |
|
| g.IsClosed |
bool? |
g.IsClosed |
bool? |
| g.IsEmpty |
bool |
g.IsEmpty |
bool |
|
|
g.IsRing |
bool? |
|
|
g.IsSimple |
bool |
|
|
g.IsValid |
bool |
| g.Latitude |
double? |
|
|
| g.Length |
double? |
g.Length |
double? |
| g.Longitude |
double? |
|
|
| g.M |
double? |
g.M |
double? |
| g.NumGeometries |
double? |
g.NumGeometries |
int? |
|
|
g.NumInteriorRing |
int? |
| g.NumPoints |
int? |
g.NumPoints |
int? |
|
|
g.PointOnSurface |
DbGeometry |
| g.ProviderValue |
object |
g.ProviderValue |
object |
| g.Srid |
int |
g.Srid |
int |
| g.StartPoint |
DbGeography |
g.StartPoint |
DbGeometry |
| g.WellKnownValue WellKnownValue |
DbGeographyWellKnownValue |
g.WellKnownValue |
DbGeometryWellKnownValue |
| g.WellKnownValue.Srid |
int |
g.WellKnownValue.Srid |
int |
| g.WellKnownValue.WellKnownBinary |
byte[] |
g.WellKnownValue.WellKnownBinary |
byte[] |
| g.WellKnownValue.WellKnownText |
string |
g.WellKnownValue.WellKnownText |
string |
|
|
g.X |
double? |
|
|
g.Y |
double? |
| g.Z |
double? |
g.Z |
double? |
|
|
|
|
| DbGeography Instance Method |
Return Type |
DbGeometry Instance Method |
Return Type |
| g.AsBinary() |
byte[] |
g.AsBinary() |
byte[] |
| g.AsGml() |
string |
g.AsGml() |
string |
| g.AsText() |
string |
g.AsText() |
string |
| g.Buffer(double distance) |
DbGeography |
g.Buffer(double distance) |
DbGeometry |
|
|
g.Contains(DbGeometry other) |
bool |
|
|
g.Crosses(DbGeometry other) |
bool |
| g.Difference(DbGeography other) |
DbGeography |
g.Difference(DbGeometry other) |
DbGeometry |
| g.Disjoint(DbGeography other) |
bool |
g.Disjoint(DbGeometry other) |
bool |
| g.Distance(DbGeography other) |
double |
g.DistanceDbGeometry other) |
double |
| g.GeometryN(int index) DbGeography |
DbGeography |
g.GeometryN(int index) |
DbGeometry |
|
|
g.InteriorRingN(int index) |
DbGeometry |
| g.Intersection(DbGeography other) |
DbGeography |
g.Intersection(DbGeometry other) |
DbGeometry |
| g.Intersects(DbGeography other) |
bool |
g.Intersects(DbGeometry other) |
bool |
|
|
g.Overlaps(DbGeometry other) |
bool |
| g.PointN(int index) |
DbGeography |
g.PointN(int index) |
DbGeometry |
|
|
g.Relate(DbGeometry other, string matrix) |
bool |
| g.SpatialEquals(DbGeography other) |
bool |
g.SpatialEquals(DbGeometry other) |
bool |
| g.SymmetricDifference(DbGeography other) |
DbGeography |
g.SymmetricDifference(DbGeometry other) |
DbGeometry |
|
|
g.Touches(DbGeometry other) |
bool |
| g.Union(DbGeography other) |
DbGeography |
g.Union(DbGeometry other) |
DbGeometry |
|
|
g.Within(DbGeometry other) |
bool |
Wednesday, July 20, 2011
Today, I began to look into the
Entity Framework June 2011 CTP which includes first class support for Spatial data types. The ADO.NET team created an abstraction layer, based on the OGC simple features specification, with a goal being to support spatial implementations from multiple vendors. As you would expect, SQL Server 2008 Spatial is supported out of the box.
For some reason, I was expecting a lot of work to be done on the client-side within their abstraction data type. I was pleasantly surprised to see EF pass the heavy lifting to the underlying data store as part of the query.
For instance, I have a table in my database called Facility with a Geography column named [Geocode]. This field contains a point (single latitude/longitude pair) identifying the location of the Facility. Even though this would normally be represented in client-side code as a SqlGeography type, EF wraps it in the new DbGeography type (i.e., the abstraction data type for ellipsoidal data).
My first query was a LINQ expression to return a list of all facilities that are within 500 miles of a given location:
var q = from f in context.Facilities
let p = DbGeography.Parse("POINT(-83 45)")
where f.Geocode.Distance(p) < 500 * 1609.344
select new { f.FacilityID, wkt=f.Geocode.AsText() };
A couple things about this query:
- The default SRID of 4326 is used. This spatial reference system uses meters for distance, so my predicate needs to convert the 500 miles into meters. Like the SqlGeography.Parse() method, DbGeography.Parse() will default to 4326.
- The return type is an anonymous type. I wanted to see how the DbGeography type's .AsText() method was executed (i.e., would it be expressed in the resulting query, or would it be handled client side, etc).
When executed, the LINQ expression above generates the following TSQL:
SELECT [Extent1].[FacilityID] AS [FacilityID],
[Extent1].[Geocode].STAsText() AS [C1]
FROM [dbo].[Facility] AS [Extent1]
WHERE ([Extent1].[Geocode].STDistance(geography::Parse(N'POINT(-83 45)'))) < cast(804672 as float(53))
As you can see, the DbGeography.AsText() was translated into the STAsText() method in the query. And, as you might expect, the predicate's DbGeography.Distance() was properly translated into STDistance() in the TSQL WHERE clause.
The other thing that I was worried about was not having access to the actual SqlGeography type returned from the database. I was surprised to see that EF's DbGeography has a property called ProviderValue that returns the native type that the provider supports!
Saturday, January 01, 2011
A recap of my 2010. Here's hoping that 2011 is just as productive!
Conferences
- Codemash - Organizer
- Day of .NET in Ann Arbor - Organizer
User Groups
- Northwest Ohio .NET User Group
Training
- Azure Boot Camp - Learner (Nashville)
- Azure Boot Camp - Trainer (Cleveland)
- Azure Boot Camp - Trainer (Southfield)
Community Events
- Give Camp - Team Leader (Ann Arbor)
- Give Camp - Participant (Lansing)
Speaking Engagements
- CodeMash (Looting Design Ideas from WoW)
- West Michigan .NET User Group (Add Spatial to .NET Apps)
- Central Ohio Day of .NET (Intro TSQL; Intro XML)
- CodeStock (SQL XML)
- DevLink (SQL XML; Looting Design Ideas from WoW)
- St. Louis Day of .NET (Looting Design Ideas from WoW; Intro TSQL; SQL Spatial)
- Fort Wayne PASS (SQL XML)
- Ann Arbor .NET Developers (Looting Design Ideas from WoW)
- Greater Lansing .NET User Group (Looting Design Ideas from WoW)
- Grand Rapids Day of .NET (Looting Design Ideas from WoW)
- Dogfood Conference (A Lap Around SQL Azure)
- IndyNDA (Looting Design Ideas from WoW)
Other fun stuff
- Windows Azure Delta Force Training (Redmond, WA)
- Tech*Ed 2010 (New Orleans, LA)
Wednesday, July 14, 2010
I recently had to work with some code that I wrote a few years ago, and stumbled upon this:
(Not that it's relevant to the rest of this blog post, but this code was responsible for deleting a Role in a custom membership provider, but due to foreign key relationships without cascading deletes, all of the User/Role assignments had to be deleted first)
Now, in all fairness to myself, there's really nothing wrong with this code. Array.ForEach<T> requires an Action<T> delegate, and this anonymous method certainly qualifies as one.
However, since the time that I wrote this particular piece of code, I started using Lambda expressions more often. To be consistent with the rest of the project's code, I should change this anonymous method into a Lambda expression.
Fortunately, CodeRush makes this a breeze! Simply put your cursor within with anonymous method itself, and then hit your
Refactor Key and choose the "Compress to Lambda Expression" refactoring:
As simple as that, the wordy "delegate" syntax of the anonymous method is compressed into the terse "=>" syntax of a Lambda expression:
delegate"/>
But, suppose that you are just learning Lambda expressions, and come across one in code that you don't fully understand. CodeRush allows you to go the other way, too, and expand a Lambda expression into an anonymous method, which may be easier to understand. Similar to above, put your cursor on the Lambda expression, hit the Refactor Key, and then choose "Expand Lambda Expression":
Anonymous methods were introduced as a way to inline functionality that otherwise had to exist in its own named method. This is really only good if that functionality is used one time. But, what if you discover that the functionality is needed in multiple places?
CodeRush provides a refactoring for anonymous methods called "Name Anonymous Method" that will extract the logic into a named method that can then be called from other code:
Activating this refactoring will first display the Target Picker (red arrow and line) so that you can select where to add the new method. Then, the new method's name can be set by editing the text in the green box that will appear.
Finally, suppose that the opposite is true: you have a named method that is only used in one place, and would prefer to just inline that code. Or, perhaps you would like to create a one-off modification to an existing method, and don't need the new functionality to be in its own named method.
CodeRush has a refactoring called "Inline Delegate" that replaces a delegate referring to a named method with an anonymous method. If there is only a single reference to that named method, then this refactoring will also delete the named method altogether.
Since I was already familiar with the anonymous method syntax, CodeRush played an instrumental part in my learning Lambda expressions because I could write an anonymous method and then have it transformed into an equivalent Lambda for me. But, once I knew Lambdas, I found that I wanted to update my older code in order to replace anonymous methods with the newer syntax, and CodeRush made it a trivial task to do this.
Happy Coding!
Tuesday, July 13, 2010
Have you ever become distracted while scrolling through code, or switched to another tab and then forgot what you were working on? It happens all the time when we try to multitask while coding. If only there was a call stack for our brains that we could just unwind to find our way back to where we started....
Turns out, there is!
CodeRush includes a feature called "Markers". These are little placeholders, or breadcrumbs, for your cursor location that can be created just before your focus changes to another part of code. When you're done with whatever task distracted you, simply hit ESC (or ALT+End) to collect the most recent marker and your cursor will be returned to exactly where it was when the marker was created.
Sometimes, CodeRush will automatically create markers for you during refactoring and template expansions. These appear as small blue triangles within the newly created code:
When you collect a marker, your cursor will be returned to that location, and an animated Locator Beacon will display on top of the cursor to draw your attention to that spot:
But, you can manually drop your own markers, too, by using ALT+Home. The manually created markers will be red in color:
Some tricks that you can do with markers:
SHIFT+ALT+Home: Drops a marker at the current cursor location and collects the most recent marker (so you can swap between the locations)
Thanks Jay Wren for the clarification.
SHIFT+ALT+PgUp: Select text between the current cursor location and the most recently created marker
SHIFT+ESC: Collect the most recent marker and also paste whatever is in the clipboard at that location
Happy Coding!
Friday, July 09, 2010
CodeRush is full of templates. Thousands of them, when you combine the basic templates with the various types that are supported by each. How does someone start to learn all of the required mnemonics in order to use CodeRush effectively?
The answer to that is a brilliant feature known as the CodeRush Tool Window:
(DevExpress menu in Visual Studio, Tool Windows -> CodeRush)
The CodeRush Tool Window provides a context-sensitive list of "next characters" in CodeRush templates that are valid at the cursor's current location. As you start to work with CodeRush, leave this window open (perhaps on a second monitor) so that you can train yourself.
In the screenshot above, I have the CodeRush Tool Window docked on the left side of my IDE. Depending on where my cursor is located, the contents of the window will change.
For example, I can create a new Auto-Implemented Property at the cursor's current location (line 49 in the screenshot). In the CodeRush Tool Window, I see that mnemonics for Auto-Implemented Properties start with "a", so I can type that character. But, after typing "a", the contents of the CodeRush window will change:
The letter "a" by itself is a valid mnemonic for a CodeRush Template, as indicated by the the green "expansion" frame at the top of the CodeRush Tool Window. If I were to hit the spacebar here, I'd get code for a new abstract class (a preview of this code appears in the expansion frame). This is not what I want, so I can look in the list to see if typing more characters results in an appropriate mnemonic.
Note: If you do accidentally expand a wrong template, just perform an Undo (Ctrl-Z) to return to the pre-expansion state of your code.
Suppose that I was trying to create an Auto-Implemented Property of type Boolean. From the contents of the window, I see that "ab" is listed as "bool", and so I only need to type the second character ("b") and then hit the space bar.
Play around by moving your cursor to various places in your code, and observing how the contents of the CodeRush Tool Window changes. Try out some of the template mnemonics that are listed to see how they work. Remember that Undo (Ctrl-Z) will get you back to where you started.
Happy Coding!
There is A LOT of repetition in coding. To accomplish certain things, you often need to include boilerplate code that is repeated throughout your program.
Take Properties, for instance. Defining Properties on a class, especially a lot of them, can be a very tedious task.
Traditionally, you have to create a member variable of the class to serve as the Property's backing store, and then create the Property itself (with a getter and setter to interact with that member variable). Lather, rinse, repeat... ad nauseum.
This type of work is where CodeRush excels by providing you with mnemonic-driven code templates. In other words, you type a few characters (the mnemonic), hit the space bar, and it will expand into a much larger snippet of code for you (saving you scores of keystrokes in the process).
The most basic template to help with writing Properties is: p
Move your cursor to somewhere within your class { } block, where defining a Property is legal syntax, and then type 'p' followed by a space. This will expand into the following:
The cursor will be placed in the first orange box, where you can change the data type from "object" to whatever type your Property needs (i.e., "string"). Notice that the Property's type is linked to the backing store's type, so changing one of them will change the other.
The trick with the orange boxes in CodeRush is that you press Enter when you finish editing their contents. This will remove the orange coloring from that element, and move the focus to the next orange box within the template. Keep repeating this until there are no more orange boxes left to edit.
Since we knew this was a string Property, we could have saved even more keystrokes by using a mnemonic that included the type: ps
The only real difference between "p" and "ps" is that the type will already be set to "string" for you in the latter. There are other mnemonic templates that expand into Property definitions for other types, like "pi" for "int" and "pd8" for "DateTime". (I'll blog about an awesome discovery feature to help you learn these in my next blog post).
But, wait! There's more!
If you already have a member variable defined (maybe you used the "vs" mnemonic, which expands into a private string variable), and wish to create a Property in your class that uses this variable as a backing store... well, CodeRush has a trick to help you accomplish that!
Simply copy that line of source code into the copy buffer as you normally would (i.e., select the line, and then hit Ctrl-C). Now, paste it where you would like the Property to be created (i.e., maybe on the line directly below). Since it's illegal to define this variable twice, CodeRush does something awesome called a Intelligent Paste. In this case, based on the contents of the copy buffer, it expands a Property template that uses your variable as the backing store!
Similarly, you can also use the "Encapsulate Field" refactoring instead of the Intelligent Paste. Move your cursor to the line declaring the variable to use as a backing store, and then hit your
Refactor Key. Use the Down Arrow key to move through the list of available refactorings, and press Enter when "Encapsulate Field" is highlighted.

Next, CodeRush will prompt you where you would like to insert the code. Use the Up and Down arrows to move through valid places where the new code can be inserted. Press Enter to select the location designated by the Target Picker (red arrow).
The result will be a new property with a getter and setter that uses your variable as a backing store.
And, finally, as C# evolved, Microsoft recognized that a lot of Properties that were being created were very simple in nature, requiring no logic in the getters and setters other than reading and writing to their backing store. A new Property syntax was added that did not require a backing store to be explicitly defined (or rather, the compiler actually creates one for you behind the scenes). This new syntax is referred to as an Auto-Implemented Property.
Similar to the "ps" template, you can use "as" to create an Auto-Implemented Property of type "string".
Note: "a" by itself will not expand into an Auto-Implemented Property of type "object" similar to what "p" by itself does.
What? You didn't know about Auto-Implemented Properties, or you want to convert all of your old-style Properties to the new Auto-Implemented Properties? There happens to be a refactoring for that in CodeRush!
Move your cursor to the Property definition that you would like to convert, and then hit your Refactor Key. Use the Down Arrow to select "Convert to Auto-implemented Property", and observe the changes that CodeRush will be making to your code (the changes will be painted on top of the code that will be changed). Press the Enter key, and voila! No more backing store!
(credit goes to
Dennis Burton for telling me about this refactoring... which goes to prove that there are way more features in this tool than you'll likely ever master)
Thanks to the magic of CodeRush, you now have many different ways to quickly create properties in your code, saving hundreds (if not thousands) of keystrokes in the process.
Happy Coding!
Do a search, and you will find that there are a few IDE Productivity Tools available for Visual Studio. As far as feature sets go, you'll also find that there is quite a bit of overlap in what these tools offer. So, deciding on one is largely a matter of personal preference.
However, by not using an IDE Productivity Tool at all, you are doing yourself a great disservice. No, really! You are wasting a lot of time by manually doing things instead of allowing software do it for you. Since time is money, and these tools are not very expensive, you will quickly make up for the cost of the program in time savings alone (I think within the first week, if not the first day).
The one tool that I stand behind is
CodeRush by
DevExpress. Some of the finest people in the industry work for Devexpress, and over the years, some of these folks have become quite good friends of mine. The company has an outstanding commitment to supporting the developer community year after year, and you will surely find them sponsoring a developer event near you. Aside from the goodwill, their product suite is outstanding, so I'm proud to continue to use and recommend their entire product line whenever I can.
Sidebar: Personally, I think that DX has a bit of a branding problem with their IDE Productivity Tools, because CodeRush includes another product called Refactor! Pro. I would rather see them not offer Rafactor! Pro by itself, and only sell CodeRush with Refactor! Pro, since in my mind they are together one product (and I would never recommend Refactor! to someone without CodeRush). As such, I often refer to features from Refactor! as belonging to CodeRush. But, I digress.
Learning any new tool can be a bit daunting at first, and CodeRush is certainly no exception. DevExpress has produced
a collection of short tutorial videos to help new users come up to speed. It should be noted, though, that some of these videos are likely useful to experienced users as well: with so much functionality packed into the product, I find that it's common for someone to master a number of features, be familiar with just as many if not more features, and yet be totally unaware of half of the things that the tool has to offer.
Since you have to start somewhere, the first feature that I recommend that someone master is mapping and using their Refactor key. This is a shortcut key that will open a CodeRush "context menu" to show available refactorings for the code where the cursor is currently located.
Personally, this is the feature that I probably use the most, so finding an appropriate key binding that is easy to hit yet does not interfere with my ability to type code or use Visual Studio key bindings was important. The default is Ctrl-backtick ( ` ), but I landed on the just backtick key to save me from needing to hold Ctrl. I've heard of a few people who have picked F1 as a way of defeating the annoying help system in VS2008 and earlier. It's all a matter of personal preference, so choose something that works for you.
To set the Refactor key shortcut, open the Options from the DevExpress menu in Visual Studio. In the tree on the left, open IDE and click on Shortcuts. After admiring the entire collection of shortcuts that are available, locate the "Refactor!" section in the list. One of the defined shortcuts will be mapped to the "Refactor" command, and by clicking on this, you can set your own key to bind it to on the right.
Trivia: Notice the Alternate Bindings that also appear in the same section. Number Pad 0 is the one preferred by Mark Miller. Dustin Campbell has two bindings mapped: the default Ctrl-`, as well as Number Pad INS.
Now that you have a Refactor key, try moving your cursor to different places in your code and pressing it.
You'll still have to learn about different refactorings that are available in the tool, but hopefully the Refactor key will become your preferred portal for accessing those refactorings (instead of using smart tags or the mouse to right-click on code).
Happy Coding!