Создание сайта-блога. Часть 1.


!!НАШ БЛОГ ПЕРЕЕХАЛ!!

Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда.

Наш новый сайт maddevelop.ru


Решение "Blog" состоит из двух проектов: "Domain" и "WebUI". В первом хранятся классы сущностей нашего сайта и интерфейс взаимодействия с базой данных. Второй проект реализует взаимодействие пользователя с сущностями.

Сущности

Для работы с Entity Framework используется подход Code-First . Созданы три класса: Category, Post и Tag. У категорий есть идентификатор, название и коллекция постов с конкретной категорией.

 public class Category
    {
        public int Id { get; set; }         // Идентификатор
        public string Name { get; set; }    // Название категории
        public ICollection<Post> Posts { get; set; } = new List<Post>(); // Коллекция постов в этой категории
    }

У тега также есть идентификатор, название и коллекция постов, подписанных данным тегом.

public class Tag
    {
        public int Id { get; set; }         // Идентификатор
        public string Name { get; set; }    // Название тега
        public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
    }

Пост описывается идентификатором, заголовком, автором, идентификатором категории, категорией, датой создания, небольшим вступлением, основным текстом и коллекцией тегов. Категория и пост связаны отношением "один-ко-многим": пост принадлежит одной категории, каждой категории принадлежат несколько постов. Тег и категория связаны отношением "многие-ко-многим". Каждое свойство тега описывается атрибутом в квадратных кавычках. Об этом будет позже.

public class Post
    {
        [HiddenInput(DisplayValue = false)]
        public int Id { get; set; }             // Id

        [Display(Name = "Заголовок")]
        public string Title { get; set; }       // Заголовок

        [Display(Name = "Автор")]
        public string Author { get; set; }      // Автор поста

        [HiddenInput(DisplayValue = false)]
        public int? CategoryId { get; set; }    // Идентификатор категории

        [Display(Name = "Категория")]
        public Category Category { get; set; }  // Категория

        [Display(Name = "Дата")]
        public DateTime Date { get; set; }      // Дата

        [Display(Name = "Вступление")]
        public string ShortText { get; set; }   // Краткое описание

        [Display(Name = "Текст")]
        public string Text { get; set; }        // Основной текст

        [Display(Name = "Теги")]
        public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();   // Коллекция тегов в данном посте.
    }

Далее создаём контекст данных, в котором свойствами являются коллекции сущностей.

public class EFDbContext : DbContext
    {
        public DbSet<Post> Posts { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Tag> Tags { get; set; }

        public EFDbContext() : base("DefaultConnection") { }        
    }

Созданная Entity Framework база данных хранится в папке App-Data проекта WebUI. Для этого задана следующая строка подключения:

 <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(localdb)\MSSQLLocalDB;
         AttachDbFilename='|DataDirectory|\Blog.mdf';Integrated Security=True;
         MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
 </connectionStrings>


Хранилище постов

В решении используется инверсия управления для написания слабо связанного кода. Поэтому хранилище постов реализуется через интерфейс IPostRepository.

public interface IPostRepository
    {
        IEnumerable<Post> Posts { get; }
        IEnumerable<Tag> Tags { get; }
        IEnumerable<Category> Categories { get; }

        void SavePost(Post post);
        Post DeletePost(int postId);
    }

Хранилище постов должно иметь свойства с коллекциями постов, тегов и категорий. Для сохранения поста и удаления написаны методы SavePost и DeletePost. О них речь пойдёт при описании админ-панели.

Интерфейс IPostRepository реализуется классом EFBlogRepository:

public class EFBlogRepository : IPostRepository
    {
        EFDbContext context = new EFDbContext();

        public IEnumerable<Post> Posts
        {
            get => context.Posts.Include(z => z.Category);
        }

        public IEnumerable<Tag> Tags
        {
            get => context.Tags;
        }

        public IEnumerable<Category> Categories
        {
            get => context.Categories;
        }

        public void SavePost(Post post)
        {
            //релизация будет написана позже
        }

        public Post DeletePost(int postId)
        {
            //релизация будет написана позже
        }
    }

Методы интерфейса реализуем при создании админ-панели. В описании класса хранилища следует обратить внимание на свойство Posts. Для отображения названий категорий на сайте при показе постов следует прикреплять в списку постов список категорий. Это обеспечивает использование метода Include() при извлечении постов из базы данных.

Инъекция зависимостей

Для того, чтобы класс контроллера не создавал экземпляр класса хранилища постов, в решении реализована инъекция зависимостей с помощью контейнера Ninject. В классе WebUI.NinjectWebCommon есть метод RegisterServices.

private static void RegisterServices(IKernel kernel)
        {
            DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
        }   

С помощью него создаётся класс NinjectDependencyResolver, в котором указывается, что при создании интерфейса IPostRepository следует создавать класс EFBlogRepository (данная процедура реализуется в методе AddBindings) :

public class NinjectDependencyResolver : IDependencyResolver
    {
        private IKernel kernel;

        public NinjectDependencyResolver(IKernel kernelParam)
        {
            kernel = kernelParam;
            AddBindings();
        }

        public object GetService(Type serviceType)
        {
            return kernel.TryGet(serviceType);
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return kernel.GetAll(serviceType);
        }

        private void AddBindings()
        {
            // Здесь размещаются привязки
            kernel.Bind<IPostRepository>().To<EFBlogRepository>();
        }
    }

Таким образом, создаются слабо связанные друг с другом части программы.

Сайт-блог создан на основе статьи

Professorweb.ru, создание интернет-магазина компьютерных игр



Ещё больше интересной информации на нашем Telegram канале.