remi bourgarel
remi bourgarel

Reputation: 9389

Inheritance and Dapper.net

I decided to use Dapper.net because it seems to be doing only what I want : mapping, I don't need anything fancy , I'm just bored to handle the mapping between my datareader and my object.

My problem :

Let's say I have these class :

class Foo{
int ID;
string Name;
}
class Bar : Foo{
string FavoriteMoovie;
}

And these tables :

Foo 
- ID
- Name

Bar
- FooID
- FavoriteMoovie

So I'd like to know how I can select my Foo and Bars in the same query ?

My only idea so far is

I can't use the overload of the method "Query" because there are just here for mapping relationships.

Upvotes: 3

Views: 4426

Answers (2)

FreeClimb
FreeClimb

Reputation: 706

in the case when one table per hierarchy TPH, it is easy to get all concrete childs Class, especially if the BaseClass is abstract

abstract class BaseValue
{
    public BaseValue()
        : this(0, 0, string.Empty)
    {
    }

    public BaseValue(int id, double value, string dimension)
    {
        Id = id;
        TypeName = GetType().Name;
        Dimension = dimension;
        _value = value;
    }

    protected double _value;

    public double RawValue
    {
        get { return _value;  }
    }

    public int Id { get; protected set; }

    public string TypeName { get; protected set; }

    public string Dimension { get; set; }

}

abstract class BaseValue<T> : BaseValue
    where T : struct
{
    public BaseValue()
        : this(0, default(T), string.Empty)
    { }

    public BaseValue(int id, T value, string dimension)
        : base(id, 0, dimension)
    {
        Value = value;
    }

    public T Value
    {
        get
        {
            if (typeof(T) == typeof(float))
                return (dynamic)(float)_value;
            else if (typeof(T) == typeof(bool))
                return (dynamic)(bool)(_value > 0 ? true : false);
            else if (typeof(T) == typeof(int))
                return (dynamic)(int)_value;
            else if (typeof(T) == typeof(TimeSpan))
                return (dynamic)TimeSpan.FromSeconds(_value);
            else if (typeof(T) == typeof(DateTime))
                return (dynamic)new DateTime((long)_value);
            else
                return (dynamic)_value;
        }
        set
        {
            if (typeof(T) == typeof(float))
                _value = (float)(object)value;
            else if (typeof(T) == typeof(bool))
                _value = (bool)(object)value ? 1 : 0;
            else if (typeof(T) == typeof(int))
                _value = (int)(object)value;
            else if (typeof(T) == typeof(TimeSpan))
                _value = ((TimeSpan)(object)value).TotalSeconds;
            else if (typeof(T) == typeof(DateTime))
                _value = ((DateTime)(object)value).Ticks;
            else
                _value = (double)(object)value;
        }
    }
}

class IntValue : BaseValue<int>
{
    public IntValue()
        : this(0, 0, string.Empty)
    { }

    public IntValue(int id, int value, string dimension)
        : base(id, value, dimension)
    { }
}

class BoolValue : BaseValue<bool>
{
    public BoolValue()
        : this(0, false, string.Empty)
    { }

    public BoolValue(int id, bool value, string dimension)
        : base(id, value, dimension)
    { }
}

class FloatValue : BaseValue<float>
{
    public FloatValue()
        : this(0, 0, string.Empty)
    { }

    public FloatValue(int id, float value, string dimension)
        : base(id, value, dimension)
    { }
}

class TimeSpanValue : BaseValue<TimeSpan>
{
    public TimeSpanValue()
        : this(0, TimeSpan.MinValue, string.Empty)
    { }

    public TimeSpanValue(int id, TimeSpan value, string dimension)
        : base(id, value, dimension)
    { }
}

class DateTimeValue : BaseValue<DateTime>
{
    public DateTimeValue()
        : this(0, DateTime.Now, string.Empty)
    { }

    public DateTimeValue(int id, DateTime value, string dimension)
        : base(id, value, dimension)
    { }
}

and Repository select from TPH table:

class Repository
{
    public IEnumerable<BaseValue> GetAll()
    {
        IEnumerable<BaseValue> values = null;

        using (SqlConnection cn = new SqlConnection(""))
        {
            cn.Open();

            const string query = "SELECT Id, TypeName, RawValue, Dimension FROM Values";

            values = cn.Query<BaseValue, object, BaseValue>(query,
                (value, ignore) =>
                {
                    BaseValue child = null;

                    if (value.TypeName == "IntValue")
                        child = new IntValue(value.Id, (int)value.RawValue, value.Dimension);
                    else if (value.TypeName == "BoolValue")
                        child = new BoolValue(value.Id, value.RawValue > 0, value.Dimension);
                    else if (value.TypeName == "FloatValue")
                        child = new FloatValue(value.Id, (float)value.RawValue, value.Dimension);
                    else if (value.TypeName == "TimeSpanValue")
                        child = new TimeSpanValue(value.Id, TimeSpan.FromSeconds(value.RawValue), value.Dimension);
                    else if (value.TypeName == "DateTimeValue")
                        child = new DateTimeValue(value.Id, new DateTime((long)value.RawValue), value.Dimension);

                    return child;
                });

            cn.Close();
        }

        return values;
    }
}

Upvotes: 1

Sam Saffron
Sam Saffron

Reputation: 131112

Very hacky, but this probably works.

db.Query<Bar, object, Foo>("select * from Foo left join Bar on FooID = ID",
 (bar,ignore) => 
   bar.FavoriteMoovie == null ? bar : new Foo{ID = bar.ID, Name = bar.Name});

Disadvantage is that it creates a couple of intermediate objects that it could do without.

Other options are to simply select Bar objects and then use the same trick to filter out the Foos or select a dynamic and then convert to the correct class.

Personally I would not query twice just to handle this, unless you are allowing for NULL FavoriteMoovie, in which case you have no choice but to either complicate the query or select twice.

Upvotes: 2

Related Questions