How To Reference A Property By Name Without Using String Constants

by Jon Davis 13. June 2010 02:39

I’m working on some code here where I’m loading an entity using a dictionary. I’m not using .NET 4.0 so I’m not going to get to use the dynamic keyword and its IDictionary implementation of properties reflection.

My original code looked something sort of like this:

public void Populate(IDictionary<string, object> values) 
{
foreach (var kvp in values) {
var key = kvp.Key;
var value = kvp.Value;
switch (key) 
{
case "FirstName":
this.FirstName = value;
break;
case "LastName":
this.LastName = value;
break;
// and so on and so forth
}
}
}

This looks sloppy. First of all, I don’t want to have to declare each and every property again for its setter just to support IDictionary. Secondly, what if I rename my properties? Then I’d have to go back and update this switch…case statement again. I want to use a strongly-typed property reference, so that if I rename the property I can use the IDE’s built-in refactoring functionality with which all uses of that property are automatically updated. I don’t get that benefit if I’m using string constants.

I’d come across some interesting tutorials in the past that addressed this problem, and I thought I’d give it my own go based on their insight. My revised solution is not by any means original. (Nothing I ever do is, I suppose.)

To make a long story short, I flexed some recently learned LINQ expression muscle on a member evaluator expression to obtain the strongly typed property’s name. In my implementation (not entirely shown here) I actually do need to verify the property by name because I have a bunch of setter logic that I don’t expose here, such as replacing the value being an IEnumerable<string> with logic that clears an ObservableCollection<string> and populates it with the value. Anyway, here’s the basic flow:

public override void Populate(IDictionary<string, object> values)
{
foreach (var kvp in values) {
if (key == base.ResolvePropertyName(() => this.SpecialProperty))
{
// this.SpecialProperty = value;
// do something special with setter here
}
else if (key == base.ResolvePropertyName(() => this.AnotherSpecialProperty))
{
// this.AnotherSpecialProperty = value;
// do something special with setter here
}
else 
{
base.Populate(key, value);
}
}
}
/// and in the base class ....
/// <summary>
/// Returns the name of the property. Syntax:
/// <code>
/// var propertyName = ResolvePropertyName(() =&gt; this.MyProperty)
/// </code>
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
protected string ResolvePropertyName(Expression<Func<T>> e)
{
if (e.NodeType == ExpressionType.Lambda)
{
return ResolvePropertyName(((LambdaExpression)e).Body);
}
if (e.NodeType == ExpressionType.MemberAccess)
{
return ((MemberExpression)e).Member.Name;
}
throw new InvalidOperationException("Given expression is not type MemberAccess.");
}
private static Dictionary<Type, List<PropertyInfo>> TypeProperties
= new Dictionary<Type, List<PropertyInfo>>();
/// <summary>
/// Populates a property of the current entity with the the given key/value pair.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void Populate(string property, object value)
{
var t = this.GetType();
List<PropertyInfo> pis;
if (!TypeProperties.ContainsKey(t))
{
TypeProperties[t] = t.GetProperties().ToList();
}
pis = TypeProperties[t];
var pi = pis.Find(pif => pif.Name == property);
if (pi == null) throw new KeyNotFoundException("Property does not exist: \"" + property + "\"");
pi.SetValue(this, value, null);
}

 

The Populate(key,value) method is basic .NET 2.0 reflection with some sorta-kinda basic caching.  The method above it is using LINQ expressions to extract the member access invocation as expressed in the LINQ statement.

I wrote a unit test to invoke this and stepped through it with the debugger. Seems to work fine.

After further tweaking, I migrated the base class functions to a utility class so that these functions can be used with anything, not just with objects that inherit a common base class. I also improved the reflection caching of Populate() so that the properties are properly hashed on their property names.

public static class ObjectUtility
{
/// <summary>
/// Returns the name of the property. Syntax:
/// <code>
/// var propertyName = obj.ResolvePropertyName(() =&gt; obj.MyProperty)
/// </code>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <param name="expr"></param>
/// <returns></returns>
public static string ResolvePropertyName<T>(this object obj, Expression<Func<T>> expr)
{
return ResolvePropertyName(expr);
}
/// <summary>
/// Returns the name of the property. Syntax:
/// <code>
/// var propertyName = ObjectUtility.ResolvePropertyName(() =&gt; obj.MyProperty)
/// </code>
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
public static string ResolvePropertyName(Expression e)
{
if (e.NodeType == ExpressionType.Lambda)
{
return ResolvePropertyName(((LambdaExpression)e).Body);
}
if (e.NodeType == ExpressionType.MemberAccess)
{
return ((MemberExpression)e).Member.Name;
}
throw new InvalidOperationException("Given expression is not type MemberAccess.");
}
private static Dictionary<Type, Dictionary<string, PropertyInfo>> TypeProperties
= new Dictionary<Type, Dictionary<string, PropertyInfo>>();
/// <summary>
/// Populates a property of an object with the the given key/value pair.
/// </summary>
/// <param name="obj"></param>
/// <param name="property"></param>
/// <param name="value"></param>
public static void Populate(this object obj, string property, object value)
{
var t = obj.GetType();
if (!TypeProperties.ContainsKey(t))
{
TypeProperties[t] = new Dictionary<string, PropertyInfo>();
var lst = t.GetProperties().ToList();
foreach (var item in lst)
{
if (!TypeProperties[t].ContainsKey(item.Name))
{
TypeProperties[t][item.Name] = item;
}
}
}
var pisdic = TypeProperties[t];
var pi = pisdic.ContainsKey(property) ? pisdic[property] : null;
if (pi == null) throw new KeyNotFoundException("Property does not exist: \"" + property + "\"");
pi.SetValue(obj, value, null);
}
}

 

 

2 Corrections: LINQ *Can* Use POCO/SQL, and VB.NET *Doesn't* Suck

by Jon Davis 8. January 2009 21:17

Just a couple observations here.

1. LINQ *can* work with POCO/SQL.

A few (two or three?) posts ago, I complained that LINQ-to-SQL doesn't really "speak" SQL / ADO.NET, you're forced to use generated classes and a designer to work with the provider. Thus, if you want to avoid the designer and generated classes, you're forced to using plain old ADO.NET.

I am VERY happy to say that I need to go do more homework. LINQ-to-SQL does have POCO support, and in fact when reading this blog entry I realized that a week's worth of work on my pet project is garbage because it is exactly to detail what this blog seems to show that LINQ already designed it.

http://linqinaction.net/blogs/jwooley/archive/2008/06/11/linq-to-sql-s-support-for-poco.aspx 

Why does this matter? Because I believe that a business object should be database / provider / ORM agnostic. It should NEVER implement database code within itself, and it shouldn't even inherit an ORM library object. I would be willing to tack on attributes/markers that don't do anything except provide mapping cues for an external ORM.

But even attributes can be avoided, and in the above-linked article's case, avoiding them is suggested. (I don't agree with that, though, because some members are database-bound, sometimes they are not, sometimes they are named differently from the database table columns, etc.)  

2. VB.NET Actually Doesn't Suck

While I think the syntax of VB.NET is painful to look at, to say nothing of writing, I must say that I greatly admire the features that set it apart from C#. Before v2008 I didn't think they were enough. ("My"?! Come on! Even the Windows Vista team was smart enough to eliminate "My" verbiage from Windows. You're going the wrong way!)

But at a .NET users group meeting I attended a couple months ago, I noticed the profound value of v2008's integrated XML support. I'm not talking about just Dim xx = <myxml>..</myxml>. That in itself is incredibly impressive, no doubt. But what impresses me more is how you can bury such XML expressions in LINQ statements. Add on top of that the ASP-style templating that is supported with it; you can do this: Dim xx = <myxml><%=myXmlValue%></myxml> And even this: 

Dim xx = <myxml>
<% For Each x In y %>
    <x><%=x.stuff%></x>
<% Next %>
</myxml> 

I haven't validated that but that's what I understand it to offer. That demonstration doesn't say much until you see it in action... 

The blog link I mentioned above (under #1) has at the bottom of it a "crosspost" link, the recent article of which reminded me of this VB.NET functionality and made me realize that this really is something worth blogging about. And if I have to work with VB.NET in the future, so long as it is with .NET 3.5 / Visual Studio 2008, I shouldn't pass it up anymore. 

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Software Development | VB.NET

LINQ Didn't Replace SQL Queries

by Jon Davis 3. January 2009 04:17

Just observing the obvious here, but about three and a half or so years ago when I heard about C-Omega, which later became LINQ, which then later became a part of C# 3.0, I got the impression that LINQ would perform SQL querying on the fly with integrated SQL-like syntax directly inline with C#, with strong typing and everything. In other words, I thought the language would inherently know ADO.NET/SQL semantics.

It does, but well, no, it doesn't. LINQ isn't an inline SQL translator at all. It is only syntactical sugar for a set of interfaces to a neutral provider to different sources of data. Once the provider is set, LINQ is still not translating SQL inline.

LINQ-to-SQL, LINQ-to-Entities, LINQ-to-NHibernate, LINQ-to-YoMamasDB, all of these use ORM templating and database boilerplating with class-specific generated code to match up the generated SQL.

I'm not against ORM, but it's still too much for smaller (read: tiny) quick-and-dirty hacks. In these cases LINQ (-to-anything) would be overkill galore in the database context. I do say this as a complaint: I still have to use plain old ADO.NET for quick-and-dirty SQL invocation additions, there's no way around it without making it not-so-quick-and-dirty.

Meanwhile, LINQ-to-Objects and LINQ-to-XML are legit. No boilerplating / generated code there. Very sweet.

Tags: , , ,

C#

LINQ-to-SQL On The Web Client

by Jon Davis 4. December 2008 09:29

This is really old news, but something I didn't realize until last night at a Silverlight users group meeting.

Silverlight 2.0 brought us LINQ-to-Objects and LINQ-to-XML on the client. Bravo, yay, etc., but obviously LINQ-to-SQL was out of the question because you can't make an Internet-based T-SQL connection (i.e. over port 1433), for obvious reasons (*cough* security).

But Service Pack 1 for Visual Studio 2008, which introduced ADO.NET Data Services, also brought in along with it transparent LINQ-to-SQL support over WCF. This to me is bizzare. I haven't tried it, I only heard it mentioned. Frankly I'm worried about security, still, as it sounds a lot like RDO (Remote Data Objects) from back in the ASP Classic / ActiveX days, and which turned out to be a huge security disaster. Nonetheless, I'm still curious about how this might work securely and how it might make the workflow of a modern-age developer much, MUCH more pleasant than manually wiring up WCF for data synchronization to begin with.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

C# | Web Development

Quick Method Of Sorting On Multiple Properties

by Jon Davis 24. June 2008 07:37

I’ve started replacing my custom List<T> sorts of

 

ret.Sort(new Comparison(delegate(MyClass o1, MyClass o2) { return o1.City.CompareTo(o2.City); } ) );

 

.., which doesn't seem to support multiple property sorts when I run Sort() multiple times (and I don're care to glean from the many multi-property sort samples on the web that have tens of lines of method execution), with ..

ret = new List<MyClass>(from r in ret orderby r.State, r.City select r);

Works like a charm. I'm slowly learning to dig LINQ-to-Objects ;)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Software Development | C#

EntitySpaces and Silverlight Demo - Part 2

by Jon Davis 23. May 2008 20:57

Mike Griffin at EntitySpaces has posted Part 2 of my EntitySpaces + Silverlight tutorial.

http://www.entityspaces.net/blog/2008/05/24/EntitySpaces+And+Silverlight+Demo+Part+2.aspx  kick it on DotNetKicks.com

It barely touches on the purest basics of filtering and updating data with Silverlight, EntitySpaces, WCF, and LINQ.

Not a whole lot to it, there's SO MUCH more that ES (particularly), Silverlight 2, WCF, and LINQ have to offer, but it's good quick-and-dirty tutorial knowledge nonetheless.

And no, I haven't been slaving away at this second part of the tutorial all this time. I finished it a long while back, its posting was delayed for various reasons not related to the demo/tutorial. I might still yet get into some advanced tutorials but we'll see. 

Currently rated 1.0 by 1 people

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Software Development | Web Development

LINQPad - One Of .NET's Best Kept Secrets

by Jon Davis 18. March 2008 14:36

http://www.linqpad.net/

This thing is awesome. :)

LINQPad supports everything in C# 3.0 and Framework 3.5:

  • LINQ to SQL
  • LINQ to Objects
  • LINQ to XML

LINQPad is also a great way to learn LINQ: it comes preloaded with 200 examples from my book, C# 3.0 in a Nutshell.  There's no better way to experience the coolness of LINQ and functional programming.

And LINQPad is more than just a LINQ query tool: it's a code snippet IDE. Instantly execute any C# 3 or VB 9 expression or statement block!

Best of all, LINQPad is free and needs no installation: just download and run.  The executable is under 2MB and is self-updating.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Cool Tools

LINQ-to-JSON

by Jon Davis 13. February 2008 10:16

A few days ago I posted a link / observation of LINQ-to-Javascript a.k.a. JSLINQ.

Ajaxian just pointed out a new project called LINQ-to-JSON that is more LINQ-like. One would use JSLINQ for working with any type of arrays in real Javascript. The LINQ-to-JSON solution looks like a truer LINQ coding experience; however, I can't tell if it's actually for Javascript or JScript.NET, the latter of which is not web browser Javascript and only runs in .NET. I'll check it out further but wanted to raise my eyebrows first. :P

http://james.newtonking.com/archive/2008/02/11/linq-to-json-beta.aspx

Currently rated 1.6 by 16 people

  • Currently 1.625/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Open Source | Software Development | Web Development

LINQ-to-Lucene

by Jon Davis 24. January 2008 15:57

I haven't tried this yet, but as a Lucene.NET user I find this to be quite compelling.

http://www.codeplex.com/linqtolucene

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Software Development

LINQ-to-Javascript

by Jon Davis 24. January 2008 15:28

I jumped onto codeplex.com for a random summary of the latest projects and stumbled across LINQ-TO-Javascript (JSLINQ). Pretty nifty idea.

Personally I'd like to see JSLINQ to be a jQuery overload -- that is, take the JSLINQ object definition and merge it with jQuery's object (at runtime) -- so that jQuery extensions can automagically work with JSLINQ objects. I suggested the idea here.

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Open Source | Cool Tools | Web Development


 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  May 2018  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar