首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自动-投影映射规则的重用

自动-投影映射规则的重用
EN

Stack Overflow用户
提问于 2017-05-20 13:05:53
回答 2查看 2.6K关注 0票数 1

给定2个来源实体:

代码语言:javascript
复制
class SourceA
{
   public string Info1 { get; set; }
   public string Info2 { get; set; }
}

class SourceB
{
   public A A { get; set; }

   public string OptionalExtraInfo { get; set; }
}

还有一个目的地:

代码语言:javascript
复制
class Dest
{
   public string ModifiedInfo1 { get; set; }
   public string ModifiedInfo2 { get; set; }

   public string ModifiedOptionalExtraInfo { get; set; }
}

我想让下面的代码与EF6一起工作:

代码语言:javascript
复制
var destsFromA = dbContext.SourcesA.ProjectTo<Dest>().ToArray();
var destsFromB = dbContext.SourcesB.ProjectTo<Dest>().ToArray();

因此,我定义了Automapper.net映射:

  • SourceA => Dest
  • SourceB => Dest

有关于如何将Info1投影到ModifiedInfo1中的自定义规则,以及Info2=>ModifiedInfo2:

代码语言:javascript
复制
CreateMap<SourceA, Dest>()
    .ForMember(x => ModifiedInfo1, opt => opt.MapFrom(src => src.Info1 + " something else-1")
    .ForMember(x => ModifiedInfo2, opt => opt.MapFrom(src => src.Info1 + " something else-2")
    .ForMember(x => ModifiedOptionalExtraInfo, opt => opt.Ignore());

CreateMap<SourceB, Dest>()
    .ForMember(x => ModifiedInfo1, opt => opt.MapFrom(src => src.A.Info1 + " something else-1")
    .ForMember(x => ModifiedInfo2, opt => opt.MapFrom(src => src.A.Info2 + " something else-2")
    .ForMember(x => ModifiedOptionalExtraInfo, opt => opt.MapFrom(src => src.OptionalExtraInfo + " something else-3"));

如何在第二次映射中重用ModifiedInfo1、ModifiedInfo2的映射规则,因为它们与第一种情况相同?

UPDATE --在我的特定情况下--我了解了如何以自然的方式重用SourceA => Dest映射。

首先,我添加了反向引用(导航属性) SourceA.B,因为这些实体实际上处于一对零或一的关系中,EF必须知道这一点。

然后,我在我的应用程序代码中更改了聚合根,它变成:

代码语言:javascript
复制
var destsFromA = dbContext.SourcesA.ProjectTo<Dest>().ToArray();
var destsFromB = dbContext.SourcesB.Select(x => x.A).ProjectTo<Dest>().ToArray();

所以我只需要处理唯一的SourceA => Dest映射

最后,我更改了映射本身:

代码语言:javascript
复制
CreateMap<SourceA, Dest>()
    .ForMember(x => ModifiedInfo1, opt => opt.MapFrom(src => src.Info1 + " something else-1")
    .ForMember(x => ModifiedInfo2, opt => opt.MapFrom(src => src.Info1 + " something else-2")
    .ForMember(x => ModifiedOptionalExtraInfo, opt => opt.MapFrom(src => src.B ? src.B.OptionalExtraInfo + " something else-3" : null);

由于这是一个问题的解决方案,而不是原来问题的答案,我接受了Ilya 的答案,认为他的答案是正确的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-20 16:06:06

带有表达式的参数化映射:

代码语言:javascript
复制
opt.MapFrom(expression)

.ForMember(x => x.Foo, expression)

使用ReSharper提取这些表达式变量很容易,因此可以如下所示:

代码语言:javascript
复制
Expression<Func<SourceA, string>> expression = src => src.Info1 + " something else-1";
var func = expression.Compile();

cfg.CreateMap<SourceA, Dest>()
    .ForMember(x => x.ModifiedInfo1, 
        opt => opt.MapFrom(expression));

cfg.CreateMap<SourceB, Dest>()
    .ForMember(x => x.ModifiedInfo1,
        opt => opt.MapFrom(src => func(src.A)));

更新:在LINQ转换的情况下,解决方案变得更加复杂。expression.Compile()不能工作,应该创建一个新的表达式:

代码语言:javascript
复制
Expression<Func<SourceA, string>> expression = src => src.Info1 + "foo";

//it should contain `src => src.A.Info1 + "foo"`
var newExpression = ConvertExpression(expression);

基于ExpressionVisitor的基本实现

代码语言:javascript
复制
private static Expression<Func<SourceB, string>> 
    ConvertExpression(Expression<Func<SourceA, string>> expression)
{
    var newParam = Expression.Parameter(typeof(SourceB), "src");

    var newExpression = Expression.Lambda<Func<SourceB, string>>(
        new ReplaceVisitor().Modify(expression.Body, newParam), newParam);

    return newExpression;
}    

class ReplaceVisitor : ExpressionVisitor
{
    private ParameterExpression parameter;

    public Expression Modify(Expression expression, ParameterExpression parameter)
    {
        this.parameter = parameter;

        return Visit(expression);
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        return Expression.Lambda<Func<SourceB, bool>>(
            Visit(node.Body), 
            Expression.Parameter(typeof(SourceB)));
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Type == typeof(SourceA))
        {
            return Expression.Property(parameter, nameof(SourceB.A));
        }

        throw new InvalidOperationException();
    }
}   
票数 4
EN

Stack Overflow用户

发布于 2017-05-21 07:20:05

一个快速而简单的解决方案是使用中间类。

首先是所使用的类,然后在投递中使用

代码语言:javascript
复制
public class SourceA 
{
    public string A { get; set; }
}

public class SourceB
{
    public string B { get; set; }
}

public class Dest
{
    public string ValueFromSourceA { get; set; }

    public string ValueFromSourceB { get; set; }
}

这里说的是中间阶层:

代码语言:javascript
复制
public class Intermediate
{
    public SourceA SourceA { get; set; } = new SourceA();

    public SourceB SourceB { get; set; } = new SourceB();
}

现在让我们开始用Automapper把零件粘在一起。

定义配置文件类

代码语言:javascript
复制
public class DestinationProfile : Profile
{
    public DestinationProfile()
    {
        this.CreateMap<Intermediate, Dest>()
            .ForMember(destination => destination.ValueFromSourceA, 
                       opt => opt.MapFrom(src => src.SourceA.A))
            .ForMember(destination => destination.ValueFromSourceB, 
                       opt => opt.MapFrom(src => src.SourceB.B));
    }
}

public class IntermediateProfile : Profile
{
    public IntermediateProfile()
    {
        this.CreateMap<Intermediate, Dest>()
            .ForMember(destination => destination.ValueFromSourceA, map => map.MapFrom(src => src.SourceA.A))
            .ForMember(destination => destination.ValueFromSourceB, map => map.MapFrom(src => src.SourceB.B));

    // ----- TODO: Create mapping for source classes.
    }
}

这是我们在上面标记的地图的重担。您可以使用Automapper的IValueResolver接口来定义值映射。所以在我们的例子中,解析器看起来像

代码语言:javascript
复制
public class SourceAResolver : IValueResolver<SourceA, Intermediate, SourceA>
{
    public SourceA Resolve(SourceA source, Intermediate destination, SourceA destMember, ResolutionContext context)
    {
        return source;
    }
}

public class SourceBResolver : IValueResolver<SourceB, Intermediate, SourceB>
{
    public SourceB Resolve(SourceB source, Intermediate destination, SourceB destMember, ResolutionContext context)
    {
        return source;
    }
}

现在,我们可以替换todo语句。

代码语言:javascript
复制
        this.CreateMap<SourceA, Intermediate>()
            .ForMember(destination => destination.SourceA, map => map.ResolveUsing<SourceAResolver>());
        this.CreateMap<SourceB, Intermediate>()
            .ForMember(destination => destination.SourceB, map => map.ResolveUsing<SourceBResolver>());

最后,我们将配置文件类注册到Automapper

代码语言:javascript
复制
public static class AutomapperProfile
{
    public static void Configure()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.AddProfile<DestinationProfile>();
            cfg.AddProfile<IntermediateProfile>();
        }); 
    }
}

用下面的代码片段启动控制台有助于测试我们的内容

代码语言:javascript
复制
        AutomapperProfile.Configure();
        var a = new SourceA {A = "Value A"};

        var b = new SourceB() {B = "Value B"};

        var intermediate = new Intermediate() {SourceA = a, SourceB = b};

        var destination = AutoMapper.Mapper.Map<Dest>(intermediate);

        Console.WriteLine(destination.ValueFromSourceA);

        Console.Read();

完成了!

注意:所提供的代码片段只是为了演示“中间”类的用法/含义--还没有实现返回到源类的方式。

(玩得开心:)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44086234

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档