从源到目标的自定义转换的 AutoMapper 问题
AutoMapper problem with custom convert from source to destination
我正在使用 AutoMapper 将我的数据库模型映射到我的 api 模型。但我对自定义映射有疑问。我将尝试解释我的问题:
所以我有这样的 db 和 api 模型:
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class DbModel
{
public string Type { get; set; }
public string Settings { get; set; }
}
通常 DbModel
Settings
属性 是 ApiModel
的序列化版本。所以我想在创建 maping:
时通过自定义转换来实现
Startup.cs:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies())
映射器配置文件class:
internal class DbToApiMapping : Profile
{
public DbToApiMapping()
{
CreateMap<ApiModel, DbModel>()
.ConvertUsing((source, dest, context) => new DbModel
{
Type = context.Items["Type"].ToString(),
Settings = JsonConvert.SerializeObject(source)
});
CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
}
}
第一张地图我是这样使用的:
var settings = modelMapper.Map<DbModel>(req.Settings, opt => opt.Items["Type"] = "Setpoint");
对于第二张地图,我是这样使用的:
var ss = modelMapper.Map<ApiModel>(settings.Settings);
尝试映射时出现的错误如下:
Message:
AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
Object -> ApiModel
System.Object -> CommonLibrary.Models.ApiModel
我确定我做错了什么...但我不太明白应该看什么。对于第二个映射,我尝试使用 .ConvertUsing()
方法,但错误是相同的。
谁能帮忙解决这个问题。
提前致谢
朱利安
---编辑---
正如我在没有 DI 的情况下尝试的评论中所建议的那样。这是代码:
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration( cfg =>
{
cfg.CreateMap<ApiModel, DbModel>()
.ConvertUsing((source, dest, context) => new DbModel
{
Type = context.Items["Type"].ToString(),
Settings = JsonConvert.SerializeObject(source)
});
cfg.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
});
var modelMapper = config.CreateMapper();
var apiObj = new ApiModel
{
Colors = new List<ApiSubModel>
{
new ApiSubModel
{
SomeProp = "Test",
SubModel = new ApiSubSubModel
{
IntProp = 3,
StringProp = "Alabala"
}
}
}
};
DbModel dbRes = modelMapper.Map<DbModel>(apiObj, opt => opt.Items["Type"] = "Setpoint");
var dbObj = new DbModel
{
Type = "Setpoint",
Settings = "{\"Colors\":[{\"SomeProp\":\"Test\",\"SubModel\":{\"IntProp\":3,\"StringProp\":\"Alabala\"}}]}"
};
var apiRes = modelMapper.Map<ApiModel>(dbObj);
Console.WriteLine(dbRes.Settings);
Console.WriteLine(apiRes.Colors[0].SomeProp);
Console.WriteLine(apiRes.Colors[0].SubModel.StringProp);
Console.ReadLine();
}
}
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class DbModel
{
public string Type { get; set; }
public string Settings { get; set; }
}
public class ApiSubModel
{
public string SomeProp { get; set; }
public ApiSubSubModel SubModel { get; set; }
}
public class ApiSubSubModel
{
public int IntProp { get; set; }
public string StringProp { get; set; }
}
它正在工作,但有一些奇怪的事情,当我想调试程序并在 var apiRes = modelMapper.Map<ApiModel>(dbObj);
之后放置一个断点并尝试调试 apiRes
的值时,它说 apiRes error CS0103: The name 'apiRes' does not exist in the current context
我稍微调整了你的代码,现在它可以工作了(虽然我不得不使用我自己的 JSON 和我自己的 SubApiModel)但是如果你不确定你可以在评论中问我
我的模特
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class ApiSubModel
{
public string Name { get; set; }
}
public class DbModel
{
public DbModel(string type, string settings)
{
Type = type;
Settings = settings;
}
public string Type { get; set; }
public string Settings { get; set; }
}
和我的JSON
{
Colors:
[
{
"Name": "AMunim"
}
]
}
这是我的映射配置:
.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
这基本上反序列化了整个 JSON,因为它有一个 属性 Color,这是一个 ApiSubModels 列表,我可以简单地将整个字符串转换为对象(ApiModel 类型)。
这是我完整的测试代码
using AutoMapper;
using Newtonsoft.Json;
using StackAnswers;
using StackAnswers.Automapper;
using System.Numerics;
DbModel dbModel = new DbModel("very important type", "{Colors: [{\"Name\": \"AMunim\"}]}");
MapperConfiguration config = new(cfg =>
{
cfg.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
});
Mapper mapper = new(config);
ApiModel apiModel = mapper.Map<DbModel, ApiModel>(dbModel);
Console.WriteLine(apiModel.Colors.Count);
Console.WriteLine(apiModel.Colors.FirstOrDefault()?.Name);
Console.Read();
和输出:
编辑
您可以单独注册您的 profiles/Mappings 并强制 DI 使用它,即注册
var config = new MapperConfiguration(c => {
//profile
c.AddProfile<DbToApiMapping>();
//mappings
c.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
});
//now register this instance
services.AddSingleton<IMapper>(s => config.CreateMapper());
现在,当您请求此服务时,您将获得在您的 ctor 中应用配置的实例
public class BadClass
{
private readonly IMapper _mapper;
BadClass(IMapper mapper)
{
_mapper = mapper;
}
public void HandleFunction()
{
//here you can use this
ApiModel apiModel = _mapper.Map<DbModel, ApiModel>(dbModel);
}
}
我设法找到了我的错误,我不得不承认这是一个愚蠢的错误,但花了我很多时间来弄明白。问题出在 var ss = modelMapper.Map<ApiModel>(settings.Settings);
行
看我有这样的 Profile
:
CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
它期望源是一个 DbModel
对象,但实际上我传递了一个 属性 这个对象,它实际上是一个字符串。而且我没有定义那种映射,这就是我收到错误的原因。
正确的用法必须是:var ss = modelMapper.Map<ApiModel>(settings);
非常感谢您的所有建议!
此致,
朱利安
我正在使用 AutoMapper 将我的数据库模型映射到我的 api 模型。但我对自定义映射有疑问。我将尝试解释我的问题:
所以我有这样的 db 和 api 模型:
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class DbModel
{
public string Type { get; set; }
public string Settings { get; set; }
}
通常 DbModel
Settings
属性 是 ApiModel
的序列化版本。所以我想在创建 maping:
Startup.cs:
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies())
映射器配置文件class:
internal class DbToApiMapping : Profile
{
public DbToApiMapping()
{
CreateMap<ApiModel, DbModel>()
.ConvertUsing((source, dest, context) => new DbModel
{
Type = context.Items["Type"].ToString(),
Settings = JsonConvert.SerializeObject(source)
});
CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
}
}
第一张地图我是这样使用的:
var settings = modelMapper.Map<DbModel>(req.Settings, opt => opt.Items["Type"] = "Setpoint");
对于第二张地图,我是这样使用的:
var ss = modelMapper.Map<ApiModel>(settings.Settings);
尝试映射时出现的错误如下:
Message:
AutoMapper.AutoMapperMappingException : Missing type map configuration or unsupported mapping.
Mapping types:
Object -> ApiModel
System.Object -> CommonLibrary.Models.ApiModel
我确定我做错了什么...但我不太明白应该看什么。对于第二个映射,我尝试使用 .ConvertUsing()
方法,但错误是相同的。
谁能帮忙解决这个问题。
提前致谢
朱利安
---编辑--- 正如我在没有 DI 的情况下尝试的评论中所建议的那样。这是代码:
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration( cfg =>
{
cfg.CreateMap<ApiModel, DbModel>()
.ConvertUsing((source, dest, context) => new DbModel
{
Type = context.Items["Type"].ToString(),
Settings = JsonConvert.SerializeObject(source)
});
cfg.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
});
var modelMapper = config.CreateMapper();
var apiObj = new ApiModel
{
Colors = new List<ApiSubModel>
{
new ApiSubModel
{
SomeProp = "Test",
SubModel = new ApiSubSubModel
{
IntProp = 3,
StringProp = "Alabala"
}
}
}
};
DbModel dbRes = modelMapper.Map<DbModel>(apiObj, opt => opt.Items["Type"] = "Setpoint");
var dbObj = new DbModel
{
Type = "Setpoint",
Settings = "{\"Colors\":[{\"SomeProp\":\"Test\",\"SubModel\":{\"IntProp\":3,\"StringProp\":\"Alabala\"}}]}"
};
var apiRes = modelMapper.Map<ApiModel>(dbObj);
Console.WriteLine(dbRes.Settings);
Console.WriteLine(apiRes.Colors[0].SomeProp);
Console.WriteLine(apiRes.Colors[0].SubModel.StringProp);
Console.ReadLine();
}
}
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class DbModel
{
public string Type { get; set; }
public string Settings { get; set; }
}
public class ApiSubModel
{
public string SomeProp { get; set; }
public ApiSubSubModel SubModel { get; set; }
}
public class ApiSubSubModel
{
public int IntProp { get; set; }
public string StringProp { get; set; }
}
它正在工作,但有一些奇怪的事情,当我想调试程序并在 var apiRes = modelMapper.Map<ApiModel>(dbObj);
之后放置一个断点并尝试调试 apiRes
的值时,它说 apiRes error CS0103: The name 'apiRes' does not exist in the current context
我稍微调整了你的代码,现在它可以工作了(虽然我不得不使用我自己的 JSON 和我自己的 SubApiModel)但是如果你不确定你可以在评论中问我
我的模特
public class ApiModel
{
public List<ApiSubModel> Colors { get; set; }
}
public class ApiSubModel
{
public string Name { get; set; }
}
public class DbModel
{
public DbModel(string type, string settings)
{
Type = type;
Settings = settings;
}
public string Type { get; set; }
public string Settings { get; set; }
}
和我的JSON
{
Colors:
[
{
"Name": "AMunim"
}
]
}
这是我的映射配置:
.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
这基本上反序列化了整个 JSON,因为它有一个 属性 Color,这是一个 ApiSubModels 列表,我可以简单地将整个字符串转换为对象(ApiModel 类型)。
这是我完整的测试代码
using AutoMapper;
using Newtonsoft.Json;
using StackAnswers;
using StackAnswers.Automapper;
using System.Numerics;
DbModel dbModel = new DbModel("very important type", "{Colors: [{\"Name\": \"AMunim\"}]}");
MapperConfiguration config = new(cfg =>
{
cfg.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
});
Mapper mapper = new(config);
ApiModel apiModel = mapper.Map<DbModel, ApiModel>(dbModel);
Console.WriteLine(apiModel.Colors.Count);
Console.WriteLine(apiModel.Colors.FirstOrDefault()?.Name);
Console.Read();
和输出:
编辑 您可以单独注册您的 profiles/Mappings 并强制 DI 使用它,即注册
var config = new MapperConfiguration(c => {
//profile
c.AddProfile<DbToApiMapping>();
//mappings
c.CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return res;
});
});
//now register this instance
services.AddSingleton<IMapper>(s => config.CreateMapper());
现在,当您请求此服务时,您将获得在您的 ctor 中应用配置的实例
public class BadClass
{
private readonly IMapper _mapper;
BadClass(IMapper mapper)
{
_mapper = mapper;
}
public void HandleFunction()
{
//here you can use this
ApiModel apiModel = _mapper.Map<DbModel, ApiModel>(dbModel);
}
}
我设法找到了我的错误,我不得不承认这是一个愚蠢的错误,但花了我很多时间来弄明白。问题出在 var ss = modelMapper.Map<ApiModel>(settings.Settings);
行
看我有这样的 Profile
:
CreateMap<DbModel, ApiModel>()
.ConstructUsing((source, context) =>
{
var res = JsonConvert.DeserializeObject<ApiModel>(source.Settings);
return new ApiModel
{
Colors = res.Colors
};
});
它期望源是一个 DbModel
对象,但实际上我传递了一个 属性 这个对象,它实际上是一个字符串。而且我没有定义那种映射,这就是我收到错误的原因。
正确的用法必须是:var ss = modelMapper.Map<ApiModel>(settings);
非常感谢您的所有建议!
此致, 朱利安