tags:

views:

1555

answers:

4

I have a text file with placeholders such as:

Thank you for your order [OrderNo]. Your order will be shipped to: [Name] [Street1] [Street2] etc

The placeholders are the field names in the database surrounded by brackets.

I want to retrieve a single record from the database like:

var order = (from o in testContext.OrderTables where o.OrderID == id select o).Single();

Then I'd like to be able to loop through the order object and get the field name, and it's value to fill in the placeholder.

Is this possible?

+1  A: 

You might be able to do it with .GetType() and then use reflection to get at the individual properties and their values.

Orion Adrian
+1  A: 

You can't use LINQ directly to iterate through the properties of an object since there isn't an enumeration of the properties of the object available on the object. You can, however, use Reflection to get at the properties of an object. LINQ may come in handy when looking through these to find the appropriate object for your placeholder.

 public Dictionary<string,object>
     ValuesForPlaceHolders( object obj,
                            IEnumerable<string> placeHolders )
 {
     var map = new Dictionary<string,object>();
     if (obj != null)
     {

         var properties = obj.GetType().GetProperties();
         foreach (string placeHolder in placeHolders)
         {
             var property = properties.Where( p => p.Name == placeHolder )
                                      .SingleOrDefault();
             if (property != null)
             {
                 map.Add( placeHolder, property.GetValue( obj, null ) );
             }
         }
     }
     return map;
 }

EDIT: The above sample was written to illustrate how LINQ could be used. My preference actually is using a helper method to get values by name. Note that the method below assumes that the property exists, but could easily be modified to return null when the property doesn't exist. In my usage my calls to it are verified by unit tests so I don't bother with the extra check.

public static class TypeHelper
{
    public static object GetPropertyValue( object obj, string name )
    {
        return obj == null ? null : obj.GetType()
                                       .GetProperty( name )
                                       .GetValue( obj, null );
    }
}
tvanfosson
Unfortunately There are 45 columns in this table. I assume the performance would not be too great using reflection here (don't know much about it). Should I just type out the fields individually ie plcHolder.Replace("[OrderNo]", order.OrderNo) instead?
Using my sample you only use reflection once to get the properties and put them in a map. From then on you would reference them from the map by name to get the value. I think I would prefer this solution when there are lots of columns and type them out by hand if there were only a few.
tvanfosson
Note that I would probably do something simpler. My original example was just to demonstrate LINQ in the scenario. I've updated with code that I actually use for this.
tvanfosson
+2  A: 
Will
A: 

How about this:


void Main()
{
   var text = "Thank you for your order [OrderNo]. Your order will be shipped to: [Name] [Street1] [Street2]";

   var order = new Order() {OrderNo = 1, Name="NameA", Street1 = "Address1A", Street2="Addres2A"};

   var splitted = text.Split(']').ToList();

   var orderType = (new Order()).GetType();

   var newText = text;
   splitted.ForEach(p => 
    { 
       string field = p.Substring(p.IndexOf('[') + 1);
       if (!string.IsNullOrEmpty(field))
       {
           var result = orderType.GetProperty(field).GetValue(order, null);
           //var method = orderType.GetMethod("get_"+field);
           //var result = method.Invoke(o, null);
           newText = newText.Replace("[" + field + "]", result.ToString());
       }
    });
   Console.WriteLine(newText);
}


public class Order
{
    public Order()
    {}

    public int OrderNo {get; set;}
    public string Name {get; set;}
    public string Street1 {get; set;}
    public string Street2 {get; set;}
}

I'm not sure if I like it, but it does work. :-)

Klinger
Klinger this looks like it will work great. How can I modify this to have just a single order - instead of List<Order> I'm just looping through one record (it's actually an application not an order) which is why I only need one.
Sam, I have edited the answer but I am not sure if that's what you are asking for.
Klinger