使用 LINQ 和自动映射程序将属性序列化为 DTO 中的复杂类型

本文关键字:DTO 类型 复杂 序列化 属性 LINQ 映射程序 使用 | 更新日期: 2023-09-27 18:35:57

我很难弄清楚一些看似"简单"的问题。我正在使用Microsoft Azure移动应用程序.Net后端,MSSQL数据库,实体框架代码优先和AutoMapper。所以我有以下对象:

public class Route
{
    public string Id { get; set; }
    [...] //some other properties
    public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
    public string Id { get; set; }
    [...]
    public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
    [...] //only strings, ints,... 
}

所以我想做的是:在数据库中将 GoogleRoute 保存为序列化字符串,因为它由许多属性组成,我不需要它们在不同的列中 - 我只想在路由实体的一列中将其作为序列化字符串。当 Route 对象投影到 DtoRoute 对象时,我希望 GoogleRoute 被序列化,反之亦然。

因为我正在使用 LINQ/queryables,所以我只能使用一些自动映射器映射选项(请参阅自动映射器维基)。没有这些,我无法让它工作。

面临的问题/我尝试过:我无法在映射(使用 MapFrom 或 ConstructProjectionUsing)上将字符串序列化/反序列化为 DtoGoogleRoute,因为 LINQ 显然无法将 JsonConvert.Serialize/Deserialize 方法转换为 SQL 语句。

我尝试在路由对象中使用 DtoGoogleRoute 属性,在 DtoRoute 对象中使用字符串属性,其中 getter/setter 执行(反)序列化。这在自定义 API 控制器中几乎完美运行,但由于 OData 查询筛选器,Azure 移动应用 .Net 后端再次在表控制器中使用,只有序列化的字符串属性返回到客户端(因为 OData/LINQ 不知道其他属性)。

另一种选择是使用实体框架从DtoGoogleRoute中创建一个复杂类型 - 这很好用,但不适用于AutoMapper,因为AutoMapper无法处理复杂类型。

目前,我正在使用自定义 API 控制器,这有效。但最好使用表控制器,因为它们支持脱机同步。

我无法想象这么简单的事情(至少我认为这是一件简单的事情)做不到或很难做到。但也许问题在于涉及的所有组件(tablecontroller,OData,LINQ,EF,AutoMapper)。

如果有人能帮忙,我真的很感激。

[编辑]:我认为它与普通的 api 控制器而不是表控制器一起工作的事实与 OData 有关。我尝试将相同的代码放在表控制器方法和 API 控制器方法中。调用 API 控制器方法时,我可以在服务器上看到它只是调用此函数并将所有正确的属性返回给客户端(使用 Fiddler 检查)。但是当调用tablecontroller方法时,tablecontroller方法将URL"重写"为OData URL ->我认为这是因为某些EnableQuery或其他OData属性。因为在这里(虽然不是AutoMapper,但它看起来像是Microsoft的类似项目)它说EnableQuery属性被调用两次 - 也是在请求离开服务器时。我认为它削减了GoogleRoute属性,因为它不知道OData元数据中的此属性或类似的东西。

使用 LINQ 和自动映射程序将属性序列化为 DTO 中的复杂类型

你可以这样实现它 -

internal class RouteToDtoConverter : TypeConverter<Route, DtoRoute>
{
    protected override DtoRoute ConvertCore(Route source)
    {
        return new DtoRoute
        {
            Id = source.Id,
            GoogleRoute = JsonConvert.DeserializeObject<DtoGoogleRoute>(source.SerializedGoogleRoute)
        };
    }
}
internal class DtoToRouteConverter : TypeConverter<DtoRoute, Route>
{
    protected override Route ConvertCore(DtoRoute source)
    {
        return new Route
        {
            Id = source.Id,
            SerializedGoogleRoute = JsonConvert.SerializeObject(source.GoogleRoute)
        };
    }
}
public class Route
{
    public string Id { get; set; }
    public string SerializedGoogleRoute { get; set; }
}
public class DtoRoute
{
    public string Id { get; set; }
    public DtoGoogleRoute GoogleRoute { get; set; }
}
public class DtoGoogleRoute
{
    public int MyProperty { get; set; }
    public int MyProperty2 { get; set; }
}

AutoMapper.Mapper.CreateMap<Route, DtoRoute>()
            .ConvertUsing(new RouteToDtoConverter());
AutoMapper.Mapper.CreateMap<DtoRoute, Route>()
            .ConvertUsing(new DtoToRouteConverter());
var res = Mapper.Map<DtoRoute>(new Route
        {
            Id = "101",
            SerializedGoogleRoute = "{'MyProperty':'90','MyProperty2':'09'}"
        });
var org = Mapper.Map<Route>(res);  //pass