.NET [PL] Architecture Aurora Backend Entity Framework Get Noticed 2016 Issues

Seedowanie bazy w Entity Framework 7

Łojezu, zabierałem się za ten wpis jak za odśnieżanie, ale w końcu jest. Wpisik będzie łatwy, szybki i przyjemny, a będzie o seedowaniu bazy danych w Entity Framework 7.

Ci, którzy czytali pierwszy wpis dotyczący konfiguracji EF7 wiedzą, że na dzień dzisiejszy nie istnieje elegancki sposób na dodanie przykładowych danych do naszej bazy podczas jej tworzenia. Tu dowód, żeby nie było, że ściemniam 😀

proof

Na szczęście Pan Rowan podpowiada nam gdzie należy obecnie umieścić kod – w klasie Startup. Problem polega jednak na tym, że posiadając aplikację wielowarstwową nie widzimy kontekstu z warstwy zawierającej kontrolery (u mnie nazwanej Web). Wobec tego, zadziały podobnie jak to miało miejsce z konfiguracją Autofac. W każdym projekcie utworzymy folder o nazwie AppBuild, a w nim umieścimy klasę statyczną, również nazwaną AppBuild. Będzie ona zawierać metodę On<Nazwa warstwy>Build, której zadaniem będzie:

  • wywołanie metody OnBuild z warstwy wyższej
  • wykonanie metod konfiguracyjnych z danej warstwy, niezbędnych do działania aplikacji (np. rejestracja map w AutoMapperze)

Dzięki temu będziemy w stanie skonfigurować całą aplikację podczas jej uruchamiania, bez dodawania niepożądanych referencji. Cały łańcuch wygląda następująco:

 


public IServiceProvider ConfigureServices(IServiceCollection services)
    {   
        //kongiguracja kontenera Autofac        
        Container = container; //tu przypisywany zostaje kontener Autofac do property Container
 
        return container.Resolve<IServiceProvider>();         
    }

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.OnDomainProxyBuild(Container);
        //dalsza konfiguracja
    }


public static class AppBuild
    {
        public static void OnDomainProxyBuild(this IApplicationBuilder app, IContainer container)
        {
            app.OnDomainBuild(container); // tu kontener jest przekazywany wgłąb metod konfiguracyjnych
 
            ToDomainObjectMappings.RegisterMaps();
            ToDtoMappings.RegisterMaps();
        }
    }



public static class AppBuild
    {
        public static void OnDomainBuild(this IApplicationBuilder app, IContainer container)
        {
            app.OnDataAccessBuild(container);
            ToEntityMappings.RegisterMaps();
            
        }
    }



public static class AppBuild
    {
        public static void OnDataAccessBuild(this IApplicationBuilder app, IContainer container)
        {
           var userManager = container.Resolve<UserManager<UserEntity>>();
           var roleManger = container.Resolve<RoleManager<IdentityRole>>(); 
           var context = container.Resolve<AuroraContext>();
           new AuroraContextSeed().Seed(context, userManager, roleManger).Wait();
        }
    }

Mam nadzieję, że teraz jest już jasne co miałem na myśli 😀 Jak widać do metod OnBuild został przekazany kontener Autofac, a to dlatego, że dostarczy nam on implementację klas, których użyjemy przy seedowaniu naszej bazy. W naszym przypadku jest to kontekst bazy danych oraz menedżery ASP.NET Identity. Przejdźmy do samej metody Seed:

 


public class AuroraContextSeed
    {
        public async Task Seed(AuroraContext context, UserManager<UserEntity> userManager, RoleManager<IdentityRole> roleManager)
        {
            var wasDatabseCreationEnsured = await context.Database.EnsureCreatedAsync();
            //*************SEED***************//
 
            if (wasDatabseCreationEnsured)
            {
                await roleManager.CreateAsync(new IdentityRole { Name = RoleNames.Admin });
 
                var adminUser = await CreateUser(new UserEntity { UserName = "admin", Email = "admin@aurora.com" }, userManager);
                var adminRole = await roleManager.FindByNameAsync(RoleNames.Admin);
 
                context.UserRoles.Add(new IdentityUserRole<string> { UserId = adminUser.Id, RoleId = adminRole.Id });
                await context.SaveChangesAsync();
            }
        }
 
        private async Task<UserEntity> CreateUser(UserEntity user, UserManager<UserEntity> userManager)
        {
            await userManager.CreateAsync(user, "test123");
            return user; 
        }
    }

Póki co nie robi on za wiele. Jej zadaniem jest:

  • Utworzenie bazy danych metodą EnsureCreatedAsync. Jeżeli baza danych istnieje, żadna akcja nie zostanie podjęta, a zwrócone zostanie false.
  • Sprawdzenie czy baza danych została utworzona. Jeżeli tak – seedujemy.
  • Dodanie poprzez RoleManager-a roli administratora.
  • Dodanie poprzez UserManager-a nowego użytkownika
  • Przypisanie nowo utworzonemu użytkownikowi roli administratora.
  • Zapisanie zmian.

 

Oczywiście jak tylko, architektura zostanie ustabilizowana i przejdziemy do implementacji funkcjonalności systemu, nasz Seed będzie się stopniowo rozrastał. Jeszcze mała ciekawostka, od tej wersji ASP.NET Identity menedżery nie dostarczają synchronicznych metod, a jedynie async 🙂 Mnie to akurat cieszy, bo jestem fanem asynców, ale wiem, że nie każdy podziela moje zdanie.

Myślę, że tym wpisem zamykam póki co temat bazodanowy w Aurorze 🙂 Przy okazji, jeśli ktoś z Was może polecić mi jakąś bibliotekę, która wspiera kaskady soft deletowe to byłbym wdzięczny za informacje w komentarzach. Na dziś to tyle.

Do widzenia !