0

私のアプリには、DDD 方式で開発されたきれいなドメイン レイヤーがあります。ドメインの開発時には、データベースはまったく考慮されていませんでした。プロパティ名は意味があり、すべて大文字ではなく、私のアプリケーションに関連しています。

現在、既存の EF DbContext からプルするリポジトリを実装しています。DbContext は、設計が不十分な Oracle データベースに (基本的に) 適合するように開発されました。

理想的には、次のようなリポジトリを実装したいと思います。

public interface IRepository {
    IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity;
}

Tは私のドメイン エンティティです。しかし、Find私のレポジトリの私のメソッドの中で、私はしなければなりません...

  1. どういうわけか式を変換して DbContext で動作するようにします

    これを行う方法はまだわかりません。

  2. DbContext を照会する

    式が「マッピング」されると、これは簡単です

  3. どういうわけか私のドメインオブジェクトにマップします

    AutoMapper を使用するか、独自のマッパーを実装できると確信しています。

  4. データベースへのトリップをまだ行っていない IQueryable を返します。

    # の 1 - 3 ですべての干渉が行われた後、これが可能かどうかはわかりません

では、この問題は過去にどのように解決されたのでしょうか。ここに再利用可能なパターンはありますか?

4

1 に答える 1

0

まあ、あなたはすでに正しい方向に進んでいます。あなたが望むことを実装するだけです:)

1.findメソッドに式を渡しているので、Where句でその式を使用するだけです

2.DbContextから正しいDbSetを取得してクエリを実行する必要があるだけです。DbContextには、特定のタイプのDbContextを取得するメソッドがあり、それを使用すると、次のようにクエリできます

public IQueryable<T> Find<T>(Expression<Func<T, bool>> query) where T : IMyDomainEntity
{
   var dbSet = context.Set<T>();
   return dbSet.Where(query);
}

3.ドメインオブジェクトがEFによってデータベースにマップされたものではない場合、DbContextクラスのDBにあるものに対してマッピングをカスタマイズする必要があります(そのためのオートマッパーは必要ありません)ので、このようなものがありますあなたの DbContext クラスで

public class MyContext : DbContext 
{
   ...
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                .Map(a => a.ToTable("DB_USERS"))
                .Property(a => a.Email).HasColumnName("MAIL");

            base.OnModelCreating(modelBuilder);
        }
}

DB のテーブル DB_USERS からクラス User にマップするには、フィールドなどに異なる名前を付けます。これに関する記事は次のとおりです。

http://www.codeproject.com/Articles/165720/Using-the-Code-First-Model-Configuration-Classes

DbContext クラスを変更したくない/変更できない場合は、属性を使用してプロパティを正しいテーブル列にマップすることもできます

http://msdn.microsoft.com/en-us/data/gg193958

または、DB にマップされたエンティティの別のセットを持ち、automapper を使用してそれらをドメイン オブジェクトに変換することもできますが、失うものはありません。クエリを具体化してドメイン モデルに自動マップする必要があるため、4 つのベロスが必要です。

4.特別なことをする必要はありません。EFが面倒を見てくれます

更新: DbContext にアクセスできないソリューション (完全に汎用的なバージョンではありませんが、機能します)

アイデアは、各ドメイン クラスのリポジトリのマッピング部分を作成して、すべてが正しくバインドされるようにすることです。Userドメイン モデルとDBUserテーブル モデルを続けます。

public class User : IDomainModelEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

public class DBUser
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int USER_ID { get; set; }

    [Required]
    [MaxLength(150)]
    public string USER_NAME { get; set; }

    [Required]
    [MaxLength(260)]
    public string USER_MAIL { get; set; }
}

次に、マッピングされた基本的な GetAll クエリを実装するドメイン クラスごとに、抽象的なリポジトリと具体的なリポジトリを作成します。

public abstract class Repository<T>  where T : IDomainModelEntity
{
    protected readonly DbContext _context;

    public Repository(DbContext context)
    {
        _context = context;
    }

    public abstract IQueryable<T> GetAll();

    public IQueryable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return GetAll().Where(predicate);
    }

}

public class UserRepository : Repository<User>
{
    public UserRepository(DbContext context)
        : base(context)
    {
    }

    public override IQueryable<User> GetAll()
    {
        return _context.Set<DBUser>()
            .Select(u => new User
                {
                    Id = u.USER_ID,
                    Name = u.USER_NAME,
                    Email = u.USER_MAIL
                });
    }
}

今それを使用するには、リポジトリでfindまたはget allを呼び出すだけです...

        using (var context = new CompanyDbContext())
        {
            var repo = new UserRepository(context);
            var list = repo.Find(a=>a.Id >= 2).ToList();

            list.ForEach(a => Console.WriteLine("Id: {0}, Name {1}, email {2}", a.Id, a.Name, a.Email));
        }

使用する必要がある各ドメイン クラスのリポジトリを渡す必要があるため、完全に汎用的ではありませんが、許容できる妥協点である可能性があります。

お役に立てれば

于 2012-06-28T02:19:37.527 に答える