使用implicit关键字时出现InvalidCastException

时间:2012-12-27 08:32:51

标签: c# .net

我有一个'模型',我正在尝试将查询结果映射到,但由于某种原因它仍然失败。以下是一些更多信息:

此处发生异常(除了查询的选择部分之外的所有部分在现实生活中都不同):

var query = @"
        SELECT 
            Id,
            PublicationDate,  
            Title,  
            IntroText,  
            BodyText,  
            IsReadByTarget,
            IsRequired
        FROM Notifications
        WHERE 
            CategoryId = @categoryId
        ";
var parameters = new Dictionary<string, object> {
    { "@categoryId", AppSettings.NotificationCategoryId },
};

var notifications = SqlHelper.GetList<Notification>(_connectionString, query, parameters);

SqlHelper是一个小助手类,可以完成所有映射。通知是我要映射到的模型。这就是它的样子:

public class Notification
{
    public string Id { get; set; }

    public Time PublicationDate { get; set; }

    public string Title { get; set; }
    public string IntroText { get; set; }
    public string BodyText { get; set; }

    public string ActiveText
    {
        get
        {
            return string.IsNullOrEmpty(IntroText) ? BodyText : IntroText;
        }
    }

    public Notifiable Target { get; set; }
    public bool IsReadByTarget { get; set; }
    public bool IsRequired { get; set; }
}

时间也是一个自定义类。它基本上保持日期+时间(就像日期时间,但更小)。它仅用于通信而非计算或其他:

public class Time
{
    public int Year { get; set; }
    public int Month { get; set; }
    public int Day { get; set; }
    public int Hour { get; set; }
    public int Minute { get; set; }
    public int Second { get; set; }

    public Time()
        : this(DateTime.Now)
    {
    }
    public Time(DateTime time)
    {
        Year = time.Year;
        Month = time.Month;
        Day = time.Day;
        Hour = time.Hour;
        Minute = time.Minute;
        Second = time.Second;
    }

    public static implicit operator DateTime(Time time)
    {
        return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
    }
    public static implicit operator Time(DateTime dateTime)
    {
        return new Time(dateTime);
    }
}

所以这也是魔术开始的地方。如您所见,它应该默默地从DateTime转换为Time,从Time转换为DateTime。这在正常情况下工作正常。所以做一些像......

Time myTime = DateTime.Now;

......工作正常。

但就我而言,我得到了:

  

从'System.DateTime'到'MyNamespace.Time'的无效演员。

public static List<T> GetList<T>(string connectionString, string query, Dictionary<string, object> parameters) where T : class, new()
{
    var data = new List<T>();

    using (var conn = new SqlConnection(connectionString))
    {
        conn.Open();
        using (var command = conn.CreateCommand())
        {
            command.CommandText = query;

            if (parameters != null)
            {
                foreach (var parameter in parameters)
                {
                    command.Parameters.AddWithValue(parameter.Key, parameter.Value);
                }
            }

            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    var item = Read<T>(reader);
                    data.Add(item);
                }
            }
        }
    }

    return data;
}

public static T Read<T>(SqlDataReader reader) where T : new()
{
    var item = new T();
    var properties = typeof(T).GetProperties();
    foreach (var propertyInfo in properties)
    {
        if (!reader.HasColumn(propertyInfo.Name)) continue;
        var ordinal = reader.GetOrdinal(propertyInfo.Name);

        if (reader.IsDBNull(ordinal)) continue;
        propertyInfo.SetValue(item, Convert.ChangeType(reader[ordinal], propertyInfo.PropertyType), null);
    }

    return item;
}

基本上,将DateTime列映射到Time对象时将其映射到DateTime对象时运行正常。 任何有关为什么会发生这种情况以及合理修复的帮助都会受到赞赏。

我知道我可以创建一个新模型,它使用DateTime代替Time并映射到该模型,然后将该模型映射到带有Time的模型,但这不是一个合理的修复。

1 个答案:

答案 0 :(得分:4)

我建议您创建自定义转化词典:

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>>
    Mappings = new Dictionary<Tuple<Type, Type>, Func<object, object>>
{
    { Tuple.Create(typeof(DateTime), typeof(Time)), x => (Time)(DateTime) x },
    // Any other conversions...
};

然后:

object originalValue = reader[ordinal];
Func<object, object> converter;
if (!Mappings.TryGetValue(Tuple.Create(originalValue.GetType(), 
                                       propertyInfo.PropertyType),
                          out converter)
{
    // Fall back to Convert.ChangeType
    converter = x => Convert.ChangeType(x, propertyInfo.PropertyType);
}
object targetValue = converter(originalValue);
propertyInfo.SetValue(item, targetValue, null);