3

What is the cleanest way to map a string column to a Uri property using Dapper?

Here's the cleanest I've been able to come up with so far (using the ITypeMap functionality):

Query:

SELECT * FROM TableWithAStringAddressColumn

POCO:

public class MyPoco
{   
    [ColumnSetter("DapperAddress")]
    public Uri Address { get; set; }
    private string DapperAddress { set { this.Address = new Uri(value); } }
}

Extensions:

partial class SqlMapper
{
    public static void InitializeTypeMaps()
    {
        SqlMapper.SetTypeMap(
            typeof(MyPoco),
            new CustomPropertyTypeMap(typeof(MyPoco), SqlMapper.CustomSetterMapper));

        // call out every other class that needs this kind of mapping
    }

    public static Func<Type, string, PropertyInfo> CustomSetterMapper =
        (type, columnName) =>
        {
            PropertyInfo prop = type
                .GetProperties()
                .FirstOrDefault(p => string.Equals(columnName, p.Name, StringComparison.OrdinalIgnoreCase));

            if (prop != null)
            {
                // find out if we need to use a different setter
                ColumnSetterAttribute setterAttribute = prop.GetCustomAttributes(false).OfType<ColumnSetterAttribute>().LastOrDefault();
                if (setterAttribute != null)
                {
                    PropertyInfo setterProp = type
                        .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .FirstOrDefault(p => string.Equals(setterAttribute.Setter, p.Name, StringComparison.OrdinalIgnoreCase));
                    if (setterProp == null)
                    {
                        throw new InvalidOperationException(string.Format("Setter property misconfigured (Property={0}, Setter={1})", prop.Name, setterAttribute.Setter));
                    }
                    else
                    {
                        prop = setterProp;
                    }
                }
            }
            return prop;
        };
}

Custom Attribute:

public class ColumnSetterAttribute : Attribute
{
    public string Setter { get; set; }

    public ColumnSetterAttribute(string setter)
    {
        this.Setter = setter;
    }
}

[edit] I'm looking for a solution I can use without needing to call out all columns in all my queries (I'd like to find a solution where I can use SELECT *).

Community
  • 1
  • 1
Clay
  • 10,885
  • 5
  • 47
  • 44

1 Answers1

2

Seems like a lot of work...

Wouldn't this be ok?

public class MyPoco
{
    private string _uriMapper;

    public Uri SomeUri
    {
        get { return new Uri(_uriMapper); }
    }

    public string Mapper { set { _uriMapper = value; } }
}

Edit:

public class UriContainer
{
    private string _uriMapper;

    public string UriMapper { set { _uriMapper = value; } }

    public int Id { get; set; }

    public Uri SomeUri { get {return new Uri(_uriMapper);} }
}



public class DbTests
{
    [Test]
    public void Can_Get_A_Uri()
    {
        using (var c = new SqlConnection("hello"))
        {
            c.Open();

            var uri = c.Query<UriContainer>("select *, someuri as urimapper from uris where id = 3").Single();

            Console.WriteLine(uri.SomeUri);
        }
    }
}
Greg Smith
  • 2,449
  • 2
  • 24
  • 37
  • You are right. That would work if I call out all my columns in my queries and alias the one(s) that are special. `SELECT Column1, Column2, SomeUri AS Mapper FROM TableName` However, I'm looking for a solution where I don't have to call out all columns in all my queries. – Clay Dec 28 '12 at 17:45
  • OK, and naming the mapping property directly after the column is not acceptable? Also, you don't need to call out all the columns, only the ones you want to alias - this works: "select *, someUri as mapper from table" – Greg Smith Dec 28 '12 at 18:03
  • Correct, I'd like to use the same name for my property (of type Uri) as I use in the table. It seems contrived to have to rename one side or the other, just to satisfy Dapper. Unfortunately, the `SELECT *, SomeUri AS Mapper` trick doesn't work if you use the same name for the Uri property as the SQL column. – Clay Dec 28 '12 at 21:44
  • Actually, the `SELECT *, SomeUri AS Mapper` trick does work if the Uri property is just a getter `Uri SomeUri { get { ... } }`. It doesn't work if you make `SomeUri` an automatic property `public Uri SomeUri { get; set; }`. – Clay Dec 28 '12 at 22:05
  • I'm pretty sure we're on the same page - I updated my answer with the exact code I used to test it - works for me exactly as expected. – Greg Smith Dec 28 '12 at 22:42