简体   繁体   English

Expression.Convert:'System.Int64'类型的对象无法转换为'System.Int32'类型

[英]Expression.Convert: Object of type 'System.Int64' cannot be converted to type 'System.Int32'

I asked a question yesterday here about reading properties from an anonymous object and writing them to private fields of a class. 我昨天在这里问了一个关于从匿名对象中读取属性并将它们写入类的私有字段的问题。 The problem solved. 问题解决了。 Here is the short story: 这是一个短篇小说:

I have some data in json format. 我有一些json格式的数据。 I deserialize them to ExpandoObject , and pass them as IDictionary<string, object> to method. 我将它们反序列化为ExpandoObject ,并将它们作为IDictionary<string, object>传递给方法。 It works fine, except Int32 properties. 它工作正常,除了Int32属性。 It seems they change to Int64 , where? 看来他们改成了Int64 ,在哪里? I don't know. 我不知道。

Here is the method again: 这是方法:

    private Func<IDictionary<string, object>, dynamic> MakeCreator(
        Type type, Expression ctor,
        IEnumerable<PropertyToFieldMapper> maps) {

        var list = new List<Expression>();
        var vList = new List<ParameterExpression>();

        // creating new target
        var targetVariable = Expression.Variable(type, "targetVariable");
        vList.Add(targetVariable);
        list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));

        // accessing source
        var sourceType = typeof(IDictionary<string, object>);
        var sourceParameter = Expression.Parameter(sourceType, "sourceParameter");

        // calling source ContainsKey(string) method
        var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) });

        var accessSourceIndexerProp = sourceType.GetProperty("Item");
        var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod();

        // itrate over writers and add their Call to block
        var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument");
        vList.Add(containsKeyMethodArgument);
        foreach (var map in maps) {
            list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name)));
            var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo,
                                                        new Expression[] { containsKeyMethodArgument });

            // creating writer
            var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo,
                                              new Expression[] { containsKeyMethodArgument });
            var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
            var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo,
                new Expression[] {
                                     Expression.Convert(targetVariable, typeof(object)),
                                     Expression.Convert(sourceValue, typeof(object))
                                 });
            Console.WriteLine(Expression.Lambda(setterCall));
            list.Add(Expression.IfThen(containsKeyMethodCall, setterCall));
        }
        list.Add(targetVariable);

        var block = Expression.Block(vList, list);

        var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(
            block, new[] { sourceParameter }
            );

        return lambda.Compile();
    }

If we have this 如果我们有这个

public class Person {
    public int Age { get; set; }
    public string Name { get; set; }
}

class, and use this object class,并使用此对象

var data = new { Name = "Amiry", Age = 20 };

to initialize an instance of Person using above method, this error occurs: 要使用上面的方法初始化Person的实例,会发生以下错误:

Object of type 'System.Int64' cannot be converted to type 'System.Int32'. “System.Int64”类型的对象无法转换为“System.Int32”类型。

But if we change Age property to: 但是,如果我们将Age属性更改为:

public long Age { get; set; }

every thing looks fine and method works perfectly. 每件事看起来都很好,方法也很完美。 I completely confused about why this happens. 我完全混淆了为什么会这样。 Do you have any idea? 你有什么主意吗?

So your input Dictionary contains long (based on discussion in comments). 所以你的输入Dictionary包含long (基于评论中的讨论)。

The easiest fix is to add Convert.ChangeType before SetValue . 最简单的解决方法是在SetValue之前添加Convert.ChangeType
(passing in sourceValue and Constant(map.Field.FieldType) ) (传入sourceValueConstant(map.Field.FieldType)
However, this may have an unintended consequence of allowing string -> int conversion. 但是,这可能会导致允许string -> int转换的意外后果。

Alternative is to add your own ConvertType method, where you decide how types are converted. 另一种方法是添加您自己的ConvertType方法,您可以在其中决定如何转换类型。

The expression is correct. 表达是正确的。 The problem is Json.NET . 问题是Json.NET It converts all numeric values (in anonymous conversions) to Int64 . 它将所有数值(匿名转换)转换为Int64 So, I just need a custom convertor: 所以,我只需要一个自定义转换器:

public class JsonIntegerConverter : JsonConverter {

    public override bool CanConvert(Type objectType) {
        return objectType == typeof(IDictionary<string, object>);
    }

    public override bool CanWrite {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var result = new Dictionary<string, object>();
        reader.Read();

        while (reader.TokenType == JsonToken.PropertyName) {
            var propertyName = (string)reader.Value;
            reader.Read();
            object value;
            if (reader.TokenType == JsonToken.Integer) {
                var temp = Convert.ToInt64(reader.Value);
                if (temp <= Byte.MaxValue && temp >= Byte.MinValue)
                    value = Convert.ToByte(reader.Value);
                else if (temp >= Int16.MinValue && temp <= Int16.MaxValue)
                    value = Convert.ToInt16(reader.Value);
                else if (temp >= Int32.MinValue && temp <= Int32.MaxValue)
                    value = Convert.ToInt32(reader.Value);
                else
                    value = temp;
            } else
                value = serializer.Deserialize(reader);
            result.Add(propertyName, value);
            reader.Read();
        }

        return result;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        throw new NotSupportedException();
    }
}

This is a concrete implementation, and absolutely can be implemented more extended and useful. 这是一个具体的实现,绝对可以实现更加扩展和有用。 But it just solve my current problem. 但它只是解决了我目前的问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 用于转换数值的表达式:System.InvalidCastException:&gt; 无法将“System.Int32”类型的 object 转换为“System.Int64”类型 - Expression to convert numeric values: System.InvalidCastException: > Unable to cast object of type 'System.Int32' to type 'System.Int64' C#:无法将“System.Int64”类型的对象转换为“System.Int32”类型 - C#: Unable to cast object of type 'System.Int64' to type 'System.Int32' 无法将 System.Int64 类型的 object 转换为 System.Int32 类型 - Unable to cast object of type System.Int64 to type System.Int32 Entityframework 核心无法将类型为“System.Int32”的 object 转换为类型“System.Int64” - Entityframework Core Unable to cast object of type 'System.Int32' to type 'System.Int64' 无法将类型为“ System.Int64”的对象转换为类型为“ System.Int32”的对象 - Unable to cast object of type 'System.Int64' to type 'System.Int32' EF Core RemoveRange System.InvalidCastException:无法将“System.Int32”类型的 object 转换为“System.Int64”类型 - EF Core RemoveRange System.InvalidCastException : Unable to cast object of type 'System.Int32' to type 'System.Int64' 从物化的“System.Int32”类型到“System.Int64”类型的指定强制转换无效 - The specified cast from a materialized 'System.Int32' type to the 'System.Int64' type is not valid 类型&#39;System.Int16&#39;的对象不能转换为类型&#39;System.Nullable`1 [System.Int32] - Object of type 'System.Int16' cannot be converted to type 'System.Nullable`1[System.Int32] “System.Int32”类型的对象无法转换为“System.UInt32&”类型 - Object of type 'System.Int32' cannot be converted to type 'System.UInt32&' “System.Int64”类型的表达式不能用于返回类型“System.Object” - Expression of type 'System.Int64' cannot be used for return type 'System.Object'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM