Является ли приложение Kigg MVC СУХИМ? Можем ли мы настроить хранилище - PullRequest
3 голосов
/ 16 июля 2009

Недавно я взглянул на реализацию Kazi Manzur Kigg MVC (Kazi пород) и заметил некоторый код, который, казалось, побеждал принцип DRY / SOC. Я хотел бы, чтобы у всех были мысли о возможном рефакторе для разделения проблем.

Kigg реализует методы Add и Remove в каждом классе репозитория ( Примечание : BaseRepository имеет виртуальные методы, которые могут быть перегружены каждой конкретной реализацией.)

Реализации для Kigg.Repository.LinqToSql.CategoryRepository и Kigg.Repository.LinqToSql.StoryRepository каскадного удаления с помощью их методов Remove для удаления дочерних объектов. ( Примечание : Категория имеет родительское отношение (один ко многим) к Story, поэтому они имеют одинаковые дочерние отношения от Story до графа объектов) см. Диаграмму . Оскорбительный код - это способ, которым оба хранилища удаляют друг друга дочерние объекты:

CategoryRepository </p> <pre><code>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); } } }

StoryRepository </p> <pre><code>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 будет вызываться метод Remove для StoryRepository, что делегирует заботу об удалении дочернего объекта Story StoryRepository, к которому он принадлежит? С точки зрения техобслуживания любые добавления к дочерним элементам истории потребуют добавления DeleteAll вызовов как к CategoryRepository, так и к StoryRepository.

.

Какая реализация будет лучше?

Следует ли изменить рефакторинг CategoryRepository для непосредственного использования StoryRepository: Категория Репозиторий (рефакторинг) </p> <pre><code>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 DataContext, на который ссылается аргумент Database, данный конструктору StoryRepository. Но когда дело доходит до юнит-тестирования, у него начинает появляться запах.

Будет ли лучший рефакторинг включать использование IoC (Kigg использует Unity в качестве контейнера Ioc) для внедрения экземпляра PerWebRequest scoped IStoryRepository в конструктор CategoryRepository?

Категория Репозиторий (рефакторинг 2) </p> <pre><code>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); } } }

С помощью этого второго рефакторинга мы теперь можем внедрить экземпляр IStoryRepository в CategoryRepository во время модульного тестирования и через Unity Ioc. Конечно, мы должны были бы распространить этот рефакторинг на каждый класс репозитория, чтобы они могли заботиться о своих детях.

Что думают все?

Ответы [ 2 ]

1 голос
/ 17 июля 2009

JBland, LingToSql будет обрабатывать каскады, как определено в базе данных. Хотя SQL Server не позволяет несколько каскадных путей .

В базе данных Kigg не определены каскадные правила удаления или обновления, но они не нужны, если реализация репозитория выполняет необходимые удаления.

1 голос
/ 17 июля 2009

Извините за мое невежество на L2S (я ​​использую NHibernate), но разве он не обрабатывает каскады автоматически?

...