3

私は最近、Kazi Manzur Kigg MVC の実装 (Kazi rocks) を調べていて、DRY/SOC の原則を破っているように見えるいくつかのコードに気付きました。懸念事項を分離するためのリファクタリングの可能性について、皆さんの考えをぜひお聞かせください。

Kigg は、各リポジトリ クラスにAddとメソッドの両方を実装します (:各具体的な実装によってオーバーロードできる仮想メソッドがあります。)RemoveBaseRepository

Kigg.Repository.LinqToSql.CategoryRepositoryおよび両方の実装は、子エンティティを削除するために、メソッドをKigg.Repository.LinqToSql.StoryRepository介して削除をカスケードします。Remove(: カテゴリにはストーリーとの親関係 (1 対多) があるため、ストーリーからオブジェクト グラフまで同じ子関係を共有します)ダイアグラムを参照してください。問題のコードは、両方のリポジトリが互いの子エンティティを削除する方法です。

カテゴリリポジトリ

namespace Kigg.Repository.LinqToSql.CategoryRepository{
    //using statements omitted
    public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
    {
    //code omitted

    public override void Remove(ICategory entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        Category category = (Category) entity;

        Database.DeleteAll(Database.StoryViewDataSource.Where(v => v.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.CommentSubscribtionDataSource.Where(cs => cs.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.CommentDataSource.Where(c => c.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.VoteDataSource.Where(v => v.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.MarkAsSpamDataSource.Where(sp => sp.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.StoryTagDataSource.Where(st => st.Story.CategoryId == category.Id));
        Database.DeleteAll(Database.StoryDataSource.Where(s => s.CategoryId == category.Id));

        base.Remove(category);
    }
}
}

ストーリーリポジトリ

namespace Kigg.Repository.LinqToSql
{

    //using statements omitted


    public class StoryRepository : BaseRepository<IStory, Story>, IStoryRepository
    {

    //code omitted


    public override void Remove(IStory entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        Story story = (Story) entity;

        Database.DeleteAll(Database.StoryViewDataSource.Where(sv => sv.StoryId == story.Id));
        Database.DeleteAll(Database.CommentSubscribtionDataSource.Where(cs => cs.StoryId == story.Id));
        Database.DeleteAll(Database.CommentDataSource.Where(c => c.StoryId == story.Id));
        Database.DeleteAll(Database.VoteDataSource.Where(v => v.StoryId == story.Id));
        Database.DeleteAll(Database.MarkAsSpamDataSource.Where(sp => sp.StoryId == story.Id));
        Database.DeleteAll(Database.StoryTagDataSource.Where(st => st.StoryId == story.Id));

        base.Remove(story);
    }
    }

}

より良い設計では でメソッドをCategoryRepository呼び出し、それによってストーリーの子オブジェクトの削除に関する懸念をそれが属する場所に委譲すると仮定するのは正しいでしょうか? メンテナンスの観点から、ストーリーの子に追加するには、と の両方に呼び出しを追加する必要があります。RemoveStoryRepositoryStoryRepositoryDeleteAllCategoryRepositoryStoryRepository

より良い実装は何でしょうか?

CategoryRepositoryを直接使用するにはリファクタリングする必要がありますStoryRepositoryか?: CategoryRepository (リファクタリング)

namespace Kigg.Repository.LinqToSql.CategoryRepository{
    //using statements omitted
    public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
    {
    //code omitted

    public override void Remove(ICategory entity)
    {
        Check.Argument.IsNotNull(entity, "entity");

        Category category = (Category) entity;

        // refactor - start
        StoryRepository _storyRepository = new StoryRepository( Database );
        category.Stories.ForEach( story => _storyRepository.Remove( story ) );
        // refactor - end

        base.Remove(category);
    }
}
}

このリファクタリングにより、CategoryRepositoryで削除ロジックを再利用できるようになり、コンストラクターに指定された引数によって参照されるStoryRepository同じ LinqToSql も再利用する必要があります。しかし、単体テストになると、においがし始めます。DataContextDatabaseStoryRepository

より良いリファクタリングには、IoC (Kigg は Unity を Ioc コンテナーとして使用) を使用して、 のPerWebRequestスコープ付きインスタンスをIStoryRepositoryCategoryRepositoryコンストラクターに挿入することが含まれますか?

CategoryRepository (リファクタリング テイク 2)

namespace Kigg.Repository.LinqToSql.CategoryRepository{
    //using statements omitted
    public class CategoryRepository : BaseRepository<ICategory, Category>, ICategoryRepository
    {


    private readonly IStoryRepository _storyRepository;

    public CategoryRepository(IDatabase database, IStoryRepository storyRepository) : base(database)
    {
        Check.Argument.IsNotNull(storyRepository, "storyRepository");
        _storyRepository = storyRepository;
    }

    public CategoryRepository(IDatabaseFactory factory, IStoryRepository storyRepository) : base(factory)
    {
        Check.Argument.IsNotNull(storyRepository, "storyRepository");
        _storyRepository = storyRepository;
    }

    //code omitted

    public override void Remove(ICategory entity)
    {
        {
        Check.Argument.IsNotNull(entity, "entity");

        Category category = (Category) entity;

        // refactor - start
        category.Stories.ForEach( story => _storyRepository.Remove( story ) );
        // refactor - end

        base.Remove(category);
    }
}
}

この 2 番目のリファクタリングにより、ユニット テスト中に Unity Ioc を介してのインスタンスをIStoryRepository注入できるようになりました。CategoryRepositoryもちろん、このリファクタリングを各リポジトリ クラスに拡張して、それらが自分の子供の責任を果たすことができるようにする必要があります。

みんなの感想は?

4

2 に答える 2

1

L2S に関する無知 (私は NHibernate を使用しています) で申し訳ありませんが、カスケードは自動的に処理されませんか?

于 2009-07-17T02:44:08.660 に答える
1

JBland、LingToSql は、データベースで定義されているようにカスケードを処理します。ただし、SQL Server では複数のカスケード パスを使用できません。

Kigg データベースにはカスケードの削除または更新ルールが定義されていませんが、リポジトリの実装が必要な削除を実行する場合は必要ありません。

于 2009-07-17T09:56:03.817 に答える