Asp.Net
April 5, 2019

Проект "Отпуск сотрудников". Часть 1, база данных.


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

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

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


Сначала сделаем серверную часть проекта. В решении создадим новый проект "Веб-приложение ASP.NET Core". Далее выберем шаблон "API".

Создание моделей

У нас будет три таблицы: "Сотрудники", "Цвета" и "Отпуски". У сотрудника (employee) есть идентификатор, имя и идентификатор цвета, которым будет окрашиваться его отпуски в сводной таблице. У цвета (color) имеются идентификатор и числовой номер, по которому можно будет воссоздать структуру Color, предназначенную для раскраски чего-либо. Специально не стал хранить название цвета в столбце типа string, ведь использование int позволяет занимать таблице меньше места в памяти. Таблицы "Цвета" и "Сотрудники" связаны отношением "один-ко-многим". У отпуска (vacation) есть обязательный идентификатор, дата начала отпуска, его продолжительность и ключ работника, которому "принадлежит" отпуск. Сотрудники и отпуски связаны отношением "один-ко-многим". Нулевым может быть только столбец с именем человека, остальные обязательны к заполнению.

Для создания базы данных и таблиц в ней будем использовать Entity Framework Core (далее просто EF). Подход выберем "code first", то есть по нашим классам, объявленным в C#, EF создаст базу данных вместе с необходимыми таблицами и связями. Для моделей сделаем отдельную папку Models.

Класс сотрудника:

public class Employee
{
    public int EmployeeId { get; set; }
    public string Name { get; set; }        
    public List<Vacation> Vacations { get; set; }
    public int ColorId { get; set; }
    [JsonIgnore]
    public Color Color { get; set; }
}

Класс цвета:

public class Color
{
    public int ColorId { get; set; }
    public int ColorNumber { get; set; }
    public List<Employee> Employees { get; set; } 
}

Класс отпуска:

public class Vacation
{
    public int VacationId { get; set; }
    public DateTime Start { get; set; }
    public int Duration { get; set; }        
    public int EmployeeId { get; set; }
    [JsonIgnore]
    public Employee Employee { get; set; }
}

Использование свойства Vacations в классе Employee и свойств EmployeeId и Employee в классе Vacation сообщает EF, что между соответствующими таблицами необходимо создать отношение "один-ко-многим". Аналогичным образом создаётся и связь между Color и Employee.


Для создания самих таблиц используется класс контекста, где они формируются на основании свойств:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }

    public DbSet<Employee> Employees { get; set; }
    public DbSet<Vacation> Vacations { get; set; }
    public DbSet<Color> Colors { get; set; }
}

Строку подключения базы данных следует указать в файле appsettings.json. Если такого файла не в проекте, то его можно создать вручную.

{  
  "ConnectionStrings": {
    "vacationConnection": "Server=(localdb)\\mssqllocaldb;Database=vacationsstore;
      Trusted_Connection=True"
  }
} 

Здесь "vacationsstore" - имя базы данных, которое можно задать любым.


Создание класса контекста с нужной строкой подключения следует зарегистрировать в провайдере сервисов в методе ConfigureServices() класса Startup. Сам метод выглядит следующим образом:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. 
    // Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        string connection = Configuration.GetConnectionString("vacationConnection");
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(connection));
        services.AddSingleton<IEmployeeRepository, EFEmployeeRepository>();
        services.AddMvc();            
    }

    // This method gets called by the runtime. 
    // Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseStatusCodePages();
        app.UseDeveloperExceptionPage();
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();

        SeedData.EnsurePopulated(app);
    }
}  

Также создадим класс с методом, который будет сразу заполнять нашу базу данных начальными значениями.

public static class SeedData
{
    public static void EnsurePopulated(IApplicationBuilder app)
    {
        ApplicationDbContext context = 
            app.ApplicationServices.GetRequiredService<ApplicationDbContext>();
        context.Database.Migrate();
        if (!context.Employees.Any())
        {
            context.Colors.AddRange(
                new Color { ColorNumber = -16777216 },  // чёрный
                new Color { ColorNumber = -16744448 },  // зелёный
                new Color { ColorNumber = -16776961 },  // синий
                new Color { ColorNumber = -65536 },     // красный
                new Color { ColorNumber = -256 },       // жёлтый
                new Color { ColorNumber = -8388480 },   // фиолетовый
                new Color { ColorNumber = -23296 },     // оранжевый
                new Color { ColorNumber = -8355712 },   // серый
                new Color { ColorNumber = -16711936 },  // лайм
                new Color { ColorNumber = -16181 }      // розовый
                );
            context.SaveChanges();

            context.Employees.AddRange(
                new Employee { Name = "Pyotr", 
                    Color = context.Colors.Skip(0).FirstOrDefault() },
                new Employee { Name = "Sergey", 
                    Color = context.Colors.Skip(1).FirstOrDefault() },
                new Employee { Name = "Vasiliy", 
                    Color = context.Colors.Skip(2).FirstOrDefault() }
                );
            context.SaveChanges();

            context.Vacations.AddRange(
                new Vacation { Start = new DateTime(2019, 5, 6), Duration = 7, 
                    Employee = context.Employees.Skip(0).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 3, 11), Duration = 7, 
                    Employee = context.Employees.Skip(0).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 1), Duration = 28, 
                    Employee = context.Employees.Skip(1).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 4, 30), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 12), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() },
                new Vacation { Start = new DateTime(2019, 5, 25), Duration = 7, 
                    Employee = context.Employees.Skip(2).FirstOrDefault() }
                );
            context.SaveChanges();
        }
    }
}

В первой строчке метода мы получаем класс контекста из провайдера сервисов. Затем производим все необходимые миграции. Если база данных с именем из строки подключения отсутствует, то она создаётся. При пустой таблице Employees мы заполняем все таблицы данными. Метод EnsurePopulated() следует вызывать в методе Configure() класса Startup. В результате данные таблиц будут такими:


И, наконец, в методе CreateWebHostBuilder() класса Program сконфигурируем провайдер сервисов:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseDefaultServiceProvider(options => options.ValidateScopes = false);
}

Таким образом, с помощью Entity Framework Core мы создали базу данных и заполнили её таблицы минимальной информацией. В следующей части определим класс репозитория и класс контроллера.


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

......... >> К части 2 >>