2016-10-20 11 views
0

Entity FrameworkAutoMapperに基づいて、3層のアプリケーションを作成しています。私はDAL、BLL、プレゼンテーション層を持っています。ここではデータアクセス層のための私のエンティティです:AutoMapperは設定を書き換えますか?

ビジネスレベルの層のために
[Table("Person")] 
public class Person 
{ 
    [Key] 
    public virtual long Id { get; set; } 
    [Column("Pib")] 
    public virtual string Pib { get; set; } 
    [Column("Bd")] 
    public virtual Nullable<DateTime> BirthdaySingle { get; set; } 
    [Column("Bp")] 
    public virtual string BirthPlace { get; set; } 
    [Column("Other")] 
    public virtual string Other { get; set; } 
    public virtual ICollection<Photo> Photos { get; set; } 
} 

[Table("Photo")] 
public class Photo 
{ 
    [Key] 
    public virtual long Id { get; set; } 
    [Column("Ph")] 
    public virtual byte[] RealPhoto { get; set; } 
    public virtual Nullable<long> PersonId { get; set; } 
    [ForeignKey("PersonId")] 
    public virtual Person Person { get; set; } 
} 

public class PersonDTO 
{ 
    public virtual long Id { get; set; } 
    public virtual string Pib { get; set; } 
    public virtual Nullable<DateTime> BirthdaySingle { get; set; } 
    public virtual string BirthPlace { get; set; } 
    public virtual string Other { get; set; } 
    public virtual ICollection<PhotoDTO> Photos { get; set; } 
} 

public class PhotoDTO 
{ 
    public virtual long Id { get; set; } 
    public virtual byte[] RealPhoto { get; set; } 
    public virtual Nullable<long> PersonId { get; set; } 
    public virtual PersonDTO PersonDTO { get; set; } 
} 

とプレゼンテーション層のために:

// class for showing details 
public class PersonViewModel 
{ 
    public virtual long Id { get; set; } 
    public virtual string Pib { get; set; } 
    public virtual Nullable<DateTime> BirthdaySingle { get; set; } 
    public virtual string BirthPlace { get; set; } 
    public virtual string Other { get; set; } 
    public virtual ICollection<PhotoViewModel> Photos { get; set; } 

    public override string ToString() 
    { 
     string result = string.Empty; 

     if (!string.IsNullOrWhiteSpace(Pib)) 
      result = string.Format("\r\n{0}", Pib); 
     if (BirthdaySingle.HasValue) 
      result += string.Format("\r\n{0}", BirthdaySingle.Value.ToShortDateString()); 
     if (!string.IsNullOrWhiteSpace(BirthPlace)) 
      result += string.Format("\r\n{0}", BirthPlace); 
     if (!string.IsNullOrWhiteSpace(Other)) 
      result += string.Format("\r\n{0}", Other); 

     return result; 
    } 
} 

// class for showing list of objects 
public class PersonListViewModel 
{ 
    public class PersonShortViewModel 
    { 
     [DisplayName("#")] 
     public virtual long Id { get; set; } 
     [DisplayName("Full Name")] 
     public virtual string Pib { get; set; } 
     [DisplayName("Birth Date")] 
     [DisplayFormat(DataFormatString = "{0:dd.MM.yyyy}")] 
     public virtual Nullable<DateTime> BirthdaySingle { get; set; } 
    } 

    public IPagedList<PersonShortViewModel> Persons { get; set; } 
} 

public class PhotoViewModel 
{ 
    public virtual long Id { get; set; } 
    public virtual byte[] RealPhoto { get; set; } 
    public virtual Nullable<long> PersonId { get; set; } 
} 

だから、私はBLLでのDataServiceクラスを持っています:

public class DataService : IDataService 
{ 
    IUnitOfWork Database { get; set; } 

    /// <summary> 
    /// Инициализирует новый экземпляр класса <see cref="T:System.Object"/>. 
    /// </summary> 
    public DataService(IUnitOfWork database) 
    { 
     //AutoMapperBLLConfiguration.Configure(); 
     Database = database; 
    } 

    public bool IsConnected() 
    { 
     return Database.IsConnected(); 
    } 

    public IQueryable<PersonDTO> GetPersons() 
    { 
     Mapper.CreateMap<Person, PersonDTO>().ForMember(ph => ph.Photos, opt => opt.Ignore()); 
     return Database.Persons.GetAll().ProjectTo<PersonDTO>(); 
    } 

    public PersonDTO GetPerson(long id) 
    { 
     var person = Database.Persons.GetById(id); 
     if (person == null) 
     { 
      throw new ValidationException("Об'єкт не знайдено.", ""); 
     } 

     Mapper.CreateMap<Photo, PhotoDTO>(); 
     Mapper.CreateMap<Person, PersonDTO>().ForMember(pe => pe.Photos, opt => opt.MapFrom(p => p.Photos)); 
     return Mapper.Map<PersonDTO>(person); 
    } 

    public IEnumerable<PersonDTO> GetPersonsBy(Expression<Func<PersonDTO, bool>> predicate) 
    { 
     if (predicate == null) 
     { 
      throw new ValidationException("Відсутня умова пошуку.", ""); 
     } 

     Mapper.CreateMap<PersonDTO, Person>().ForMember(person => person.CbdId, opt => opt.Ignore()); 
     return 
      Mapper.Map<IEnumerable<PersonDTO>>(
       Database.Persons.GetByCondition(predicate.RemapForType<PersonDTO, Person, bool>())); 
    } 

    public PhotoDTO GetPhoto(long id) 
    { 
     var photo = Database.Photos.GetById(id); 
     if (photo == null) 
     { 
      throw new ValidationException("Зображення не знайдено.", ""); 
     } 

     return Mapper.Map<PhotoDTO>(photo); 
    } 

    public IEnumerable<PhotoDTO> GetPhotosBy(Expression<Func<PhotoDTO, bool>> predicate) 
    { 
     if (predicate == null) 
     { 
      throw new ValidationException("Відсутня умова пошуку.", ""); 
     } 
     Expression<Func<Photo, bool>> mappedSelector = Mapper.Map<Expression<Func<Photo, bool>>>(predicate); 
     return Mapper.Map<IEnumerable<PhotoDTO>>(Database.Photos.GetByCondition(mappedSelector)); 
    } 

    public void Dispose() 
    { 
     Database.Dispose(); 
    } 
} 

私は、プレゼンテーション層はBLLについて知る必要はありませんだと思うので、私は

public static class AutoMapperBLLConfiguration 
{ 
    public static void Configure() 
    { 
     Mapper.Initialize(configuration => 

       /*configuration.AddProfile(new PhotoIgnoreProfile()); 
       configuration.AddProfile(new PhotoIncludeProfile());*/ 
       /*configuration.AddProfile(new PhotoProfile()); 
       configuration.AddProfile(new PersonProfile());*/ 
       GetConfiguration(Mapper.Configuration) 
      ); 
     Mapper.AssertConfigurationIsValid(); 
    } 

    private static void GetConfiguration(IConfiguration configuration) 
    { 
     var profiles = 
      typeof(PhotoProfile).Assembly.GetTypes().Where(type => typeof(Profile).IsAssignableFrom(type)); 
     foreach (Type profile in profiles) 
     { 
      configuration.AddProfile(Activator.CreateInstance(profile) as Profile); 
     } 
    } 
} 

public class PersonProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<Person, PersonDTO>(); 
     Mapper.CreateMap<PersonDTO, Person>().ForMember(person => person.CbdId, opt => opt.Ignore()); 
    } 
} 

/*public class PhotoIgnoreProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<Person, PersonDTO>().ForMember(ph => ph.Photos, opt => opt.Ignore()); 
    } 
} 

public class PhotoIncludeProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<Person, PersonDTO>().ForMember(pe => pe.Photos, opt => opt.MapFrom(p => p.Photos)); 
    } 
}*/ 

public class PhotoProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<Photo, PhotoDTO>().ForMember(dto => dto.PersonDTO, opt => opt.MapFrom(photo => photo.Person)); 
     Mapper.CreateMap<PhotoDTO, Photo>().ForMember(photo => photo.Person, opt => opt.MapFrom(dto => dto.PersonDTO)); 
    } 
} 

とプレゼンテーション層のために... AutoMapperのための個別の構成を作成することにしました:

public static class AutoMapperPLConfiguration 
{ 
    public static void Configure() 
    { 
     Mapper.Initialize(configuration => 

       //configuration.AddProfile(new PersonViewProfile()); 
       //configuration.AddProfile(new PhotoViewProfile()); 
       GetConfiguration(Mapper.Configuration) 
      ); 
     Mapper.AssertConfigurationIsValid(); 
    } 

    private static void GetConfiguration(IConfiguration configuration) 
    { 
     // we use order by because we need photo mapping to be the first 
     var profiles = 
      typeof(PhotoViewProfile).Assembly.GetTypes().Where(type => typeof(Profile).IsAssignableFrom(type)).OrderByDescending(type => type.Name); 
     foreach (Type profile in profiles) 
     { 
      configuration.AddProfile(Activator.CreateInstance(profile) as Profile); 
     } 
    } 
} 

public class PersonViewProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<PersonDTO, PersonViewModel>() 
       .ForMember(model => model.Photos, opt => opt.MapFrom(dto => dto.Photos)); 
     Mapper.CreateMap<PersonViewModel, PersonDTO>() 
       .ForMember(dto => dto.Photos, opt => opt.MapFrom(model => model.Photos)); 
    } 
} 

public class PersonShortViewProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<PersonDTO, PersonListViewModel.PersonShortViewModel>().IgnoreAllNonExisting(); 
     Mapper.CreateMap<IPagedList<PersonDTO>, IPagedList<PersonListViewModel.PersonShortViewModel>>() 
       .AfterMap((s, d) => 
         Mapper 
          .Map<IEnumerable<PersonDTO>, IEnumerable<PersonListViewModel.PersonShortViewModel>>(s, d)) 
       .ConvertUsing<PagedListConverter<PersonDTO, PersonListViewModel.PersonShortViewModel>>(); 
     Mapper.CreateMap<PersonDTO, PersonListViewModel.PersonShortViewModel>().IgnoreAllNonExisting(); 
    } 
} 
public class PhotoViewProfile : Profile 
{ 
    protected override void Configure() 
    { 
     Mapper.CreateMap<PhotoDTO, PhotoViewModel>().ForSourceMember(dto => dto.PersonDTO, opt => opt.Ignore()); 
     Mapper.CreateMap<PhotoViewModel, PhotoDTO>().ForMember(dto => dto.PersonDTO, opt => opt.Ignore()); 
    } 
} 

また、私が使用してこのような拡張子を持っていますExpressionsPagedListへの変換:

public class PagedListConverter<TIn, TOut> : ITypeConverter<IPagedList<TIn>, IPagedList<TOut>> 
{ 
    /// <summary> 
    /// Performs conversion from source to destination type 
    /// </summary> 
    /// <param name="context">Resolution context</param> 
    /// <returns> 
    /// Destination object 
    /// </returns> 
    public IPagedList<TOut> Convert(ResolutionContext context) 
    { 

     var source = (IPagedList<TIn>) context.SourceValue; 
     var mapped = Mapper.Map<IList<TOut>>(source); 
     return new StaticPagedList<TOut>(mapped,source.GetMetaData()); 
    } 
} 
    /// <summary> 
/// An <see cref="ExpressionVisitor"/> implementation which uses <see href="http://automapper.org">AutoMapper</see> to remap property access from elements of type <typeparamref name="TSource"/> to elements of type <typeparamref name="TDestination"/>. 
/// </summary> 
/// <typeparam name="TSource">The type of the source element.</typeparam> 
/// <typeparam name="TDestination">The type of the destination element.</typeparam> 
public class AutoMapVisitor<TSource, TDestination> : ExpressionVisitor 
{ 
    private readonly ParameterExpression _newParameter; 
    private readonly TypeMap _typeMap = Mapper.FindTypeMapFor<TSource, TDestination>(); 

    /// <summary> 
    /// Initialises a new instance of the <see cref="AutoMapVisitor{TSource, TDestination}"/> class. 
    /// </summary> 
    /// <param name="newParameter">The new <see cref="ParameterExpression"/> to access.</param> 
    public AutoMapVisitor(ParameterExpression newParameter) 
    { 
     Contract.Requires(newParameter != null); 

     _newParameter = newParameter; 
     Contract.Assume(_typeMap != null); 
    } 

    [ContractInvariantMethod] 
    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")] 
    private void ObjectInvariant() 
    { 
     Contract.Invariant(_typeMap != null); 
     Contract.Invariant(_newParameter != null); 
    } 

    /// <summary> 
    /// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>. 
    /// </summary> 
    /// <returns> 
    /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression. 
    /// </returns> 
    /// <param name="node">The expression to visit.</param> 
    protected override Expression VisitMember(MemberExpression node) 
    { 
     var propertyMaps = _typeMap.GetPropertyMaps(); 
     Contract.Assume(propertyMaps != null); 

     // Find any mapping for this member 
     var propertyMap = propertyMaps.SingleOrDefault(map => map.SourceMember == node.Member); 
     if (propertyMap == null) 
     { 
      return base.VisitMember(node); 
     } 

     var destinationProperty = propertyMap.DestinationProperty; 
     Contract.Assume(destinationProperty != null); 

     var destinationMember = destinationProperty.MemberInfo; 
     Contract.Assume(destinationMember != null); 

     // Check the new member is a property too 
     var property = destinationMember as PropertyInfo; 
     if (property == null) 
     { 
      return base.VisitMember(node);  
     } 

     // Access the new property 
     var newPropertyAccess = Expression.Property(_newParameter, property); 
     return base.VisitMember(newPropertyAccess); 
    } 
} 

/// <summary> 
/// A class which contains extension methods for <see cref="Expression"/> and <see cref="Expression{TDelegate}"/> instances. 
/// </summary> 
public static class ExpressionExtensions 
{ 
    /// <summary> 
    /// Remaps all property access from type <typeparamref name="TSource"/> to <typeparamref name="TDestination"/> in <paramref name="expression"/>. 
    /// </summary> 
    /// <typeparam name="TSource">The type of the source element.</typeparam> 
    /// <typeparam name="TDestination">The type of the destination element.</typeparam> 
    /// <typeparam name="TResult">The type of the result from the lambda expression.</typeparam> 
    /// <param name="expression">The <see cref="Expression{TDelegate}"/> to remap the property access in.</param> 
    /// <returns>An <see cref="Expression{TDelegate}"/> equivalent to <paramref name="expression"/>, but applying to elements of type <typeparamref name="TDestination"/> instead of <typeparamref name="TSource"/>.</returns> 
    public static Expression<Func<TDestination, TResult>> RemapForType<TSource, TDestination, TResult>(
     this Expression<Func<TSource, TResult>> expression) 
    { 
     Contract.Requires(expression != null); 
     Contract.Ensures(Contract.Result<Expression<Func<TDestination, TResult>>>() != null); 

     var newParameter = Expression.Parameter(typeof (TDestination)); 
     Contract.Assume(newParameter != null); 

     var visitor = new AutoMapVisitor<TSource, TDestination>(newParameter); 
     var remappedBody = visitor.Visit(expression.Body); 
     if (remappedBody == null) 
     { 
      throw new InvalidOperationException("Unable to remap expression"); 
     } 
     return Expression.Lambda<Func<TDestination, TResult>>(remappedBody, newParameter); 
    } 
} 

はこのようにそれを使用します。

//... 
PersonListViewModel list = new PersonListViewModel(); 
      Mapper.CreateMap<PersonDTO, PersonListViewModel.PersonShortViewModel>().IgnoreAllNonExisting(); 
      if (predicate == null) 
      { 
       //Mapper.CreateMap<PersonDTO, PersonListViewModel.PersonShortViewModel>(); 
       list.Persons = 
        Mapper.Map<IPagedList<PersonListViewModel.PersonShortViewModel>>(
         DataService.GetPersons() 
            .OrderBy(person => person.Pib) 
            .ToPagedList(pageIndex, pagingSettings.PageSize)); 
      } 
      else 
      { 
       list.Persons = 
        Mapper.Map<IPagedList<PersonListViewModel.PersonShortViewModel>>(
         DataService.GetPersonsBy(predicate.RemapForType<PersonViewModel, PersonDTO, bool>()) 
            .OrderBy(person => person.Pib) 
            .ToPagedList(pageIndex, pagingSettings.PageSize)); 
      } 
//... 

これはすべてではありませんが、他のコードはまったく問題ではないようです。しかし、それがあれば、私にそれを求めてください。私はここにそれを追加します。

そしてここでマッパー設定をインスタンス化:

internal static class Program 
{ 
    /// <summary> 
    /// Main point. 
    /// </summary> 
    [STAThread] 
    private static void Main() 
    { 
     AutoMapperBLLConfiguration.Configure(); 
     AutoMapperPLConfiguration.Configure(); 
     // etc. 
    } 
} 

私の主な質問は:なぜAutoMapperは、設定を書き換えるように思える、それをインスタンス化した後、私はまだ、各操作の前にMapper.CreateMap<>を行う必要があるため?同様のエンティティに対して異なる設定を1か所で作成するにはどうすればよいですか?例えば。今ではこのエラーを示しています

Missing type map configuration or unsupported mapping. 

Mapping types: 
Person -> PersonDTO 
Reestr.DAL.Entities.Person -> Reestr.BLL.DTO.PersonDTO 

Destination path: 
IEnumerable`1[0] 

Source value: 
System.Data.Entity.DynamicProxies.Person_81BD716087EE14CF5E255587795725BC7C06DC2382A1A8EBF33C29A04F551C34 

どのように私は(あなたはそれがDataServiceコンストラクタでの行をコメントした見たように)AutoMapperは異なる層betweeen構成を作成分けることができますか? そして私は初心者であり、私のプログラムをベストプラクティスにしたいので、いくつかのアーキテクチャーロジックで私を助けてください/私はこの問題を3日間戦っています...ありがとう!

+0

これは大きなコードです。どこかのサンプルプロジェクトにアクセスできますか? – Kalitsov

+0

初期化を呼び出すたびに、設定を上書きします。一度呼び出すと、関連するすべてのプロファイルが含まれます。レイヤー間を移動するオブジェクトをマップするためのAutomapperでは、両側を意識する必要があります。 – stuartd

+0

@stuartdプレゼンテーション層はビジネス層のロジックについて知る必要はありません。もちろん、私はすべてのオートマトンロジックをプレゼンテーションレイヤーに入れることができますが、間違った方法ではありませんか? – Dmitry

答えて

0

AutoMapperで初期化すると、設定がリセットされます。 AppDomainごとにAutoMapperを1つずつ設定し、アプリケーションの起動時に1つのInitializeコールを設定する必要があります。私は設定を別々に保つために、あなたのCreateMap呼び出しのためにそれらを使うことができるようにProfileインスタンスを使います。私は層がお互いを「知っていない」ということが何を意味するのか分かりませんが、アプリはプロファイルを読み込むためにすべてのアセンブリを知る必要があります。あなたのアプリからすべてのプロファイルを追加するだけで、Initializeを呼び出してください。

また、AutoMapper 5にアップグレードすると、タイプのアセンブリからプロファイルを読み込む方法があります。

+0

私はあなたを理解しています。そして、あなたが見るように私はプロファイルを通して初期化を使用します。残念ながら、私のプロジェクトはオンにする必要があります。 NET Framework 4.0では、automapper 4.1.1しか使用できません。ビジネス層もそれをマッピングの設定とプレゼンテーション層を初期化する必要があります私はこれについて知っていると思う必要はありません。 – Dmitry

+0

レイヤーについて「知っている」ことが何を意味するかはまだ分かりません。すべてが1つのアプリケーションなので、すべてが同じAppDomainにあります。スキャンの背後にあるコードは本当に簡単です。コピーして貼り付けることができます。それは私がそれを組み立てる前に行ったことです。 –

+0

よろしくお願い致します。それは良いアイデアだ、私は数日前に見つけたかったようだ。私はあなたの解決策を試してみる) – Dmitry

関連する問題