2

AutoMapper(5.2)を使用して、次のEF Core(1.1)シナリオをマップする方法があるかどうかを調べました。ソースクラスは、テーブルごとの継承がまだサポートされておらず、既存のデータベースに対して作業しているため、継承を使用しません。AutoMapperを使用して、ソース継承なしで宛先継承をマップできますか?

EFコアPOCOS:

public class Farmer 
{ 
    [Key] 
    public int FarmerId { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    //changed entities 
    public virtual ICollection<Chicken> ChangedChickens { get; set; } 
    public virtual ICollection<Cow> ChangedCows { get; set; } 
} 

public class Chicken 
{ 
    [Key] 
    public int ChickenId { get; set; } 
    public bool IsRooster { get; set; } 
    //common change props 
    public int LastChangeByFarmerId { get; set; } 
    public DateTime LastChangeTimestamp { get; set; } 
    [ForeignKey("LastChangeBy")] 
    public virtual Farmer LastChangeFarmer { get; set; } 
} 

public class Cow 
{ 
    [Key] 
    public int CowId { get; set; } 
    public string Name { get; set; } 
    //common change props 
    public int LastChangeByFarmerId { get; set; } 
    public DateTime LastChangeTimestamp { get; set; } 
    [ForeignKey("LastChangeBy")] 
    public virtual Farmer LastChangeFarmer { get; set; }   
} 

私は私のデータ転送クラスの変更のプロパティの基本クラスを使用したい:

のDTO

public abstract class FarmerChangeDtoBase 
{ 
    public int LastChangeBy { get; set; } 
    public DateTime LastChangeTime { get; set; } 
    public string ChangingFarmerFirstName { get; set; } 
    public string ChangingFarmerLastName { get; set; } 
    public string ChangingFarmerFullName => $"{ChangingFarmerFirstName} {ChangingFarmerLastName}"; 
} 

public class ChickenDto : FarmerChangeDtoBase 
{ 
    public int ChickenId { get; set; } 
    public bool IsRooster { get; set; } 
} 

public class CowDto : FarmerChangeDtoBase 
{ 
    public int CowId { get; set; } 
    public string Name { get; set; } 
} 

I拡張メソッドを書いてLastChangeByとを得る10の値はリフレクションを使用して理想的ではないかもしれませんが、私は農家名のネストされたプロパティを取得する方法を理解できません。ここでは拡張メソッドです:

public static IMappingExpression<TSource, TDest> MapChangeFarmer<TSource, TDest>(
    this IMappingExpression<TSource, TDest> mappingExpression) 
    where TDest : FarmerChangeDtoBase 
{ 
    return mappingExpression.ForMember(d => d.LastChangeBy, 
      opt => opt.MapFrom(s => 
       (int) s.GetType().GetProperty("LastChangeByFarmerId").GetValue(s))) 
     .ForMember(d => d.LastChangeTime, 
      opt => opt.MapFrom(s => 
       (DateTime) s.GetType().GetProperty("LastChangeTimestamp").GetValue(s))); 
    //what/how can I map the name properties??? 
} 

は、私はむしろFarmerChangeDtoBaseから継承するすべての単一のDTOのためにそれを書くことよりも、拡張メソッドでは、ネストされたプロパティLastChangeFarmer.FirstNameLastChangeFarmer.LastNameをマッピングすることができる方法はありますか?

+0

はサイドノートのだろうが、私は一般的な性質を保持台POCOクラスを持つのできないように何が表示されない - それはあなたが 'DbContext'は、単に継承含まれている場合はEFの継承とは何の関係もありませんエンティティ。 –

+0

@IvanStoev - "継承は、階層別テーブル(TPH)パターンを使用してマップされます。TPHは、単一のテーブルを使用して階層内のすべての型のデータを格納します。行は表す。 –

+0

これはあなたのコンテキストに 'DbSet 'があるときです。 'DbSet 'と 'DbSet 'を別々にすると、すべてが上手くいく(継承がない場合と同じように)。 –

答えて

1

ソース継承を使わずにデスティネーション継承をマップするのではなく、ソース継承が問題の原因を解決してください。

EF(コア)継承は、単一多形抽象エンティティセットを中心としたエンティティの継承をモデル化し、単一のテーブル(TPH)または複数のテーブル(TPTまたはTPC)にマッピングする概念です。しかし、EF(Core)は、EF継承を使用せずにPOCOクラス継承を使用することを禁止していません。 - あなたの場合のような共通のエンティティプロパティを含む基本クラスを使用することは絶対に問題ありません。例えば

、サンプルモデルはChickenCowエンティティは、次の表を生成します。

migrationBuilder.CreateTable(
    name: "Chicken", 
    columns: table => new 
    { 
     ChickenId = table.Column<int>(nullable: false) 
      .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 
     IsRooster = table.Column<bool>(nullable: false), 
     LastChangeByFarmerId = table.Column<int>(nullable: false), 
     LastChangeTimestamp = table.Column<DateTime>(nullable: false) 
    }, 
    constraints: table => 
    { 
     table.PrimaryKey("PK_Chicken", x => x.ChickenId); 
     table.ForeignKey(
      name: "FK_Chicken_Farmer_LastChangeByFarmerId", 
      column: x => x.LastChangeByFarmerId, 
      principalTable: "Farmer", 
      principalColumn: "FarmerId", 
      onDelete: ReferentialAction.Cascade); 
    }); 

migrationBuilder.CreateTable(
    name: "Cow", 
    columns: table => new 
    { 
     CowId = table.Column<int>(nullable: false) 
      .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 
     LastChangeByFarmerId = table.Column<int>(nullable: false), 
     LastChangeTimestamp = table.Column<DateTime>(nullable: false), 
     Name = table.Column<string>(nullable: true) 
    }, 
    constraints: table => 
    { 
     table.PrimaryKey("PK_Cow", x => x.CowId); 
     table.ForeignKey(
      name: "FK_Cow_Farmer_LastChangeByFarmerId", 
      column: x => x.LastChangeByFarmerId, 
      principalTable: "Farmer", 
      principalColumn: "FarmerId", 
      onDelete: ReferentialAction.Cascade); 
    }); 

私たちは、共通のプロパティを持つ基本クラスを抽出し、それからChickenCow継承をさせた場合:

public abstract class FarmerChangeBase 
{ 
    public int LastChangeByFarmerId { get; set; } 
    public DateTime LastChangeTimestamp { get; set; } 
    [ForeignKey("LastChangeByFarmerId")] 
    public virtual Farmer LastChangeFarmer { get; set; } 
} 

public class Chicken : FarmerChangeBase 
{ 
    [Key] 
    public int ChickenId { get; set; } 
    public bool IsRooster { get; set; } 
} 

public class Cow : FarmerChangeBase 
{ 
    [Key] 
    public int CowId { get; set; } 
    public string Name { get; set; } 
} 

結果の移行(したがってマッピング)は、継承を使用しない以前のものとまったく同じです。

あなたはマッピング方法が原因適切なジェネリック型制約のために簡単ですが、それを行うたら:

public static IMappingExpression<TSource, TDest> MapChangeFarmer<TSource, TDest>(
    this IMappingExpression<TSource, TDest> mappingExpression) 
    where TSource : FarmerChangeBase 
    where TDest : FarmerChangeDtoBase 
{ 
    return mappingExpression 
     .ForMember(d => d.LastChangeBy, opt => opt.MapFrom(s => s.LastChangeByFarmerId)) 
     .ForMember(d => d.ChangingFarmerFirstName, opt => opt.MapFrom(s => s.LastChangeFarmer.FirstName)) 
     .ForMember(d => d.ChangingFarmerLastName, opt => opt.MapFrom(s => s.LastChangeFarmer.LastName)) 
     .ForMember(d => d.LastChangeTime, opt => opt.MapFrom(s => s.LastChangeTimestamp)); 
} 

P.S.それは、これを使用することができます

public static void MapFromPath<TSource, TDestination, TMember>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, string memberPath) 
{ 
    var source = Expression.Parameter(typeof(TSource), "s"); 
    var member = memberPath.Split('.').Aggregate(
     (Expression)source, Expression.PropertyOrField); 
    var selector = Expression.Lambda<Func<TSource, TMember>>(member, source); 
    opt.MapFrom(selector); 
} 

はあなたがソースの継承を使用することはできませんいくつかの理由によって場合に、元の質問に答えるために、次のカスタム拡張メソッドは、プロパティパスを含む文字列から動的に必要な表現を構築します方法:

public static IMappingExpression<TSource, TDest> MapChangeFarmer<TSource, TDest>(
    this IMappingExpression<TSource, TDest> mappingExpression) 
    where TDest : FarmerChangeDtoBase 
{ 
    return mappingExpression 
     .ForMember(d => d.LastChangeBy, opt => opt.MapFromPath("LastChangeByFarmerId")) 
     .ForMember(d => d.ChangingFarmerFirstName, opt => opt.MapFromPath("LastChangeFarmer.FirstName")) 
     .ForMember(d => d.ChangingFarmerLastName, opt => opt.MapFromPath("LastChangeFarmer.LastName")) 
     .ForMember(d => d.LastChangeTime, opt => opt.MapFromPath("LastChangeTimestamp")); 
} 
関連する問題