13

アプリケーションでオブジェクトからオブジェクトへのマッパーが必要です。いくつか試してみましたが、自分のニーズに合ったものが見つからなかったので、自分で書いています。現在、私は以下のようなインターフェースを持っています:

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

次に、顧客を次のようにアカウントにマップするAccountMapperを実装します。

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

これは今のところ問題なく機能しますが、同じ宛先エンティティにマップするソースエンティティがいくつかあります。たとえば、BillHistoryにマップするPaymentとInvoiceがあります。上記をサポートするには、2つの別々のマッパー(つまり、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
    }
}

最初の実装は正常に機能しますが、2番目の実装は少しエレガントになります。これは可能ですか?もしそうなら、正しい構文はどのようになりますか?

編集 - - - -

私は人々がこれをするのは嫌いですが、もちろん私は少し詳細に言及するのを忘れました。マッパーとインターフェースの間に抽象クラスがあり、すべてのマッパーに共通のロジックを実装します。したがって、私のマッパー署名は実際には次のとおりです。

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

Mapperに含まれる場所:

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));
    }
}
4

4 に答える 4

4

最初のインターフェースを使用して、オブジェクトにインターフェースを複数回実装する必要があります。

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

自分で書くのではなく、 AutoMapperを見てみることを真剣に検討したいと思います。マッピングには、多くのパフォーマンステストやバグ修正などを経ていることは言うまでもなく、すでに解決されている微妙な違いがたくさんあります。

于 2010-05-13T19:27:24.153 に答える
1

抽象クラスに関しては、それを取り除き、拡張メソッドに置き換えることを検討してください。これにより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も提案されており、これによりあなたの生活が楽になります。ただし、マッピングの抽象化を維持し、コードをマッピング戦略に依存させたくない場合は、上記のインターフェイスを使用してAutoMapperのラッパーを挿入するのは非常に簡単です。MapAllまた、上記で説明した拡張方法を引き続き使用できることも意味します。

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

最後の言葉

また、マッピング戦略が全面的に機能するとは限らないことにも注意してください。ドメインと戦って、マッピング戦略に強制的に適合させようとしないでください。1つの特定の例は、2つの入力項目から1つにマップする必要がある場合があります。あなたは明らかにこれをあなたの戦略に適合させることができますが、あなたはそれが厄介になるかもしれません。この特定の例では、それをマージと見なします。

于 2010-06-29T00:39:41.823 に答える
0

2番目の例は、いくつかの変更のみで機能します。

// 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
    }
}

ただし、これによって既存のパターンよりも実際に何かが得られるかどうかはわかりません。

于 2010-05-13T19:23:03.080 に答える
0

マップするタイプの数が限られている場合は、インターフェイス定義で入力タイプと出力タイプを宣言する最初の方法を使用します。次に、マッパーはサポートする入力タイプごとにインターフェースを実装できるため、次のようBillHistoryMapperに宣言されます。

public class BillHistoryMapper : IMapper<BillHistory, Invoice>, IMapper<BillHistory, Payment>
{
   ...
}
于 2010-05-13T19:25:43.240 に答える