我的应用程序需要一个对象到对象映射器。我尝试了一些,但没能找到适合我需要的东西,所以我正在写自己的。目前我有一个如下所示的界面:

public interface IMapper<T, R> {
    T Map(R obj);
}

然后,我实现一个 AccountMapper,将客户映射到帐户,如下所示:

public class AccountMapper : IMapper<Account, Customer> {
    Account Map(Customer obj) {
        // mapping code
    }
}

到目前为止,这工作得很好,但是我有几个映射到同一目标实体的源实体。例如,我有一个付款和一个发票,它们都映射到 BillHistory。为了以上支持这一点,我需要制作两个单独的映射器(即BillHistoryPaymentMapper 和 BillHistoryInvoiceMapper),这很好。但是,我希望能够以稍微不同的方式实现它,如下所示。唯一的问题是我不知道这是否可能,如果可以,我不知道正确的语法。

public interface IMapper<T> {
    T Map<R>(R obj);
}

public class BillHistoryMapper : IMapper<Account> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

虽然第一个实现工作正常,但第二个实现会稍微优雅一些​​。这可能吗?如果可能的话,正确的语法是什么样的?

编辑 - - - -

我讨厌人们这样做,但当然我忘了提到一个小细节。我们在映射器和接口之间有一个抽象类,用于在所有映射器之间实现一些通用逻辑。所以我的映射器签名实际上是:

public class BillHistoryMapper : Mapper<BillHistory, Invoice> {
}

其中映射器包含:

public abstract class Mapper<T, R> : IMapper<T, R> {
    public IList<T> Map(IList<R> objList) {
        return objList.ToList<R>().ConvertAll<T>(new Converter<T, R>(Map));
    }
}
有帮助吗?

解决方案

您将不得不使用自己的接口和实现接口多次在对象上:

public class BillHistoryMapper : IMapper<Account, Invoice>, 
                                 IMapper<Account, Payment> {     
   ...
}

我会认真考虑考虑看看 AutoMapper ,而不是写你自己的。有在映射了很多细微之处,它已经解决了,更何况它已经通过大量的性能测试,漏洞修复等。

其他提示

对于您的抽象类,请考虑删除它并用扩展方法替换它。这将允许您使用 MapAll 无论您是实现接口还是使用某种继承链,都可以使用函数。

public static class MapperExtensions
{
    public static IEnumerable<TOutput> MapAll<TInput, TOutput>
        (this IMapper<TInput, TOutput> mapper, IEnumerable<TInput> input)
    {
        return input.Select(x => mapper.Map(x));
    }
}

现在,当您尝试解决上述问题时,这将变得更加容易,因为您不再需要从基类继承,您现在可以为要映射的类型实现映射接口。

public class BillHistoryMapper :
    IMapper<Invoice, BillHistory>, IMapper<Payment, BillHistory>
{
    public BillHistory Map<Invoice>(Invoice obj) {}
    public BillHistory Map<Payment>(Payment obj) {}
}

还可以考虑改变你的 IMapper 通用参数则相反(我在前面的示例中冒昧):

public interface IMapper<in TInput, out TOutput>
{
    TOutput Map(TInput input);
}

这样做的原因是它直接映射到 System.Converter<T> 委托,你可以这样做:

IMapper<ObjectA, ObjectB> myAToBMapper = new MyAToBMapper();

ObjectA[] aArray = { new ObjectA(), new ObjectA() };
ObjectB[] bArray = Array.ConvertAll<ObjectA, ObjectB>(aArray, myAToBMapper.Map);

List<ObjectA> aList = new List<ObjectA> { new ObjectA(), new ObjectA() };
List<ObjectB> bList = aList.ConvertAll<ObjectB>(myAToBMapper.Map);

// Or

var aToBConverter = new Converter<ObjectA, ObjectB>(myAToBMapper.Map);
bArray = Array.ConvertAll(aArray, aToBConverter);
bList = aList.ConvertAll(aToBConverter);

自动映射器 还建议这将使您的生活更轻松。但是,如果您想保持映射抽象并使代码与映射策略无关,那么使用上述接口在 AutoMapper 周围注入包装器是非常容易的。这也意味着您可以继续使用 MapAll 上面解释了扩展方法。

public class AutoMapperWrapper<in TInput, out TOutput> : IMapper<TInput, TOutput>
{
    public TOutput Map(TInput input)
    {
        return Mapper.Map<TOutput>(input);
    }
}

最后一句话

另请记住,您并不总是会发现您的映射策略将全面发挥作用,因此不要试图与您的域作斗争并强迫它适应您的映射策略。一个特殊的例子是您可能必须将两个输入项映射到一个输入项。显然,您可以使其符合您的策略,但您可能会发现它变得混乱。在这个特定的例子中,将其视为合并。

您第二示例将只有很少的改变工作:

// you have to include the R type in the declaration of the Mapper interface
public interface IMapper<T, R> {
    T Map<R>(R obj);
}

// You have to specify both IMapper implementations in the declaration
public class BillHistoryMapper : IMapper<Account, Invoice>, IMapper<Account, Payment> {
    public BillHistory Map<Invoice>(Invoice obj) {
        // mapping code
    }
    public BillHistory Map<Payment>(Payment obj) {
        // mapping code
    }
}

我不知道如果这实际上获得您是否在现有模式东西,虽然。

如果有的要从映射,然后我会用在接口定义声明输入和输出类型的第一种方法的类型数量有限。然后映射器可以实现接口为它支持,所以你BillHistoryMapper将被声明为每个输入类型:

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top