Table of Contents
Narzędzia ORM są szeroko stosowane do wykonywania operacji na bazie danych. W tym artykule opiszę jak w prosty sposób użyć narzędzia NHibernate w projekcie ASP.NET Core. Przykłady zostały stworzone w .NET 5, aczkolwiek wszystko co tutaj opisuję ma zastosowanie do starszych wersji .NET, a także do najnowszej .NET 6. Zakładam, że znasz NHibernate. Jeśli nie, to tutaj znajdziesz podstawowe informacje.
Wstęp
Na początku było ADO.NET, czyli warstwa dostępu do bazy danych wykorzystująca bezpośrednie połączenie oraz zapytania SQL. Korzystanie z tego typu podejścia w komercyjnych aplikacjach nie było idealnym rozwiązaniem, gdyż zapytania były pisane w dialekcie SQL zgodnym z konkretnym silnikiem bazy danych. Przejście na inną bazę wymagałoby przepisania większości zapytań. Dodatkowo programista za każdym razem musiał mapować dane zwracane przez zapytania na klasy. Aby rozwiązać te i wiele innych problemów, świat stworzył narzędzia ORM (Object–relational mapping).
Na platformie .NET rekomendowanym ORM jest Entity Framework, jednak ja od wielu lat korzystam z bardziej dojrzałego narzędzia NHibernate. Oryginalnie powstał on w Javie i w międzyczasie został przeniesiony także do .NET. Posiada bardzo wiele użytecznych ficzerów i moim skromnym zdaniem nadal jest lepszy od Entity Framework. Dlatego, rozpoczynając nowy projekt, zawsze opieram go o NHibernate i za każdym razem widzę, jak wiele rzeczy mi dostarcza.
Tworzymy nowy projekt
Będzie to aplikacja ASP.NET Core wraz z aplikacją SPA napisaną w Blazor. W tym artykule skupimy się przede wszystkim na ASP.NET Core, gdyż tylko tam jest wykorzystywany NHibernate. Jako IDE korzystam z Visual Studio 2019. Wersja Community jest bezpłatna dla małych zespołów, więc idealnie pasuje do SoloProgramisty.
Źródła znajdziesz tutaj:
GitHub: https://github.com/robocik/AspNetCoreWithNHibernate
Zaczynamy zatem:
- Stwórz w VS 2019 nowy projekt Blazor WebAssembly App
Zwróć uwagę na wybrane opcje:
- ASP.NET Core hosted – spowoduje dodanie do solucji projektu aplikacji ASP.NET Core.
- Authentication Type – Individual Accounts, dzięki czemu od razu dostajemy bazowy kod logowania i tworzenia użytkowników. VS korzysta z Entity Frameworka do operacji bazodanowych, a my chcemy NHibernate, dlatego część kodu usuniemy w dalszych krokach
- Nasz projekt wygląda tak:
Widzmy 3 projekty:
- NoteBookApp.Client – aplikacja Blazor
- NoteBookApp.Server – aplikacja ASP.NET Core, która obecnie pełni rolę WebApi dla aplikacji klienckiej
- NoteBookApp.Shared – biblioteka zawierająca wspólne elementy pomiędzy serwerem, a klientem (np definicję obiektów DTO)
- Na początek dodajmy nowy projekt NoteBookApp.Logic (Class Library), który będzie zawierał logikę biznesową (Opcjonalnie).
Jeśli chcesz wiedzieć dlaczego warto wydzielać logikę biznesową, kliknij tutaj.
Usunięcie EntityFramework
Stworzony projekt domyślnie używa EntityFramework (EF), więc następnym krokiem jest usunięcie wszystkich komponentów z nim związanych.
- Usuwamy katalog Data
W tym folderze znajdują się klasy związane z tworzeniem bazy danych oraz dostępem do niej z EF. NHibernate z nich nie korzysta.
- Usuwamy pakiety EntityFrameworka. Najprościej zaznaczyć w Solution Explorer projekt NoteBookApp.Server i usunąć wszystkie pozycje z EntityFrameworkCore
W moim przypadku są to:
- Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
Gdy już usuniemy powyższe wpisy, nasz projekt powinien wyglądać mniej więcej tak:
Możesz też usunąć te referencje klikając prawym myszy na projekcie NoteBookApp.Server i wybierając Manage NuGet Packages…
- Na koniec usuwamy pozostałości po EF z klasy
Startup
Wszystkie instrukcje, które na powyższym zrzucie ekranu są na czerwono, usuwamy (pamietaj też o usunięciu wszystkich instrukcji using
związanych z EF na początku pliku). Ostatecznie nasza klasa Startup
powinna wyglądać mniej więcej tak:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NoteBookApp.Server.Models;
using System.Linq;
namespace NoteBookApp.Server
{
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.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
}
}
}
- Jeśli powyższe kroki wykonałeś prawidłowo, projekt powienien się skompilować. Wciśnij Ctrl+Shift+B, aby to sprawdzić (lub wybierz Build solution z menu Build)
Dodanie NHibernate do projektu
W tym momencie możemy już dodać NHibernate do naszego projektu.
- Dodaj do projektu NoteBookApp.Server następujące pakiety:
- NHibernate – właściwy pakiet naszego ORM
- NHibernate.AspNetCore.Identity – implementacja ASP.NET Identity za pomocą NHibernate
- NHibernate.NetCore – zawiera metody ułatwiające konfigurację NHibernate
- Microsoft.Data.SqlClient – pakiet umożliwiający łączenie się z Sql Server
Natomiast do projektu NoteBookApp.Logic dodaj
- NHibernate
- NHibernate.AspNetCore.Identity
Aby to zrobić, kliknij prawym na solucji w oknie Solution Explorer i wybierz Manage NuGet Packages for Solution…
W oknie NuGet – Solution znajdujemy wymagane pakiety i instalujemy w odpowiednich projektach
- (Opcjonalnie) Przenieś katalog Model z NoteBookApp.Server do projektu NoteBookApp.Logic wraz ze zmianą przestrzeni nazw.
W naszej testowej aplikacji klasy modelowe i mappingi będę umieszczać w tym projekcie, jednak nic nie stoi na przeszkodzie, aby pozostawić te rzeczy w NoteBookApp.Server.
- Domyślnie klasa
ApplicationUser
dziedziczy zMicrosoft.AspNetCore.Identity.IdentityUser
, która jest pozostałością po implementacji EntityFrameworka. W przypadku NHibernate, musimy zastąpić ją przezNHibernate.AspNetCore.Identity.IdentityUser
.
using NHibernate.AspNetCore.Identity;
namespace NoteBookApp.Logic.Domain
{
public class ApplicationUser : IdentityUser
{
}
}
- Dodajemy mapping dla klasy
ApplicationUser
using NHibernate.Mapping.ByCode.Conformist;
using NoteBookApp.Logic.Models;
namespace NoteBookApp.Logic.Mappings
{
public class ApplicationUserMapping : JoinedSubclassMapping<ApplicationUser>
{
public ApplicationUserMapping()
{
}
}
}
Obecnie nasza klasa ApplicationUser
nie ma żadnych właściwości (prócz tych, które dziedziczy z IdentityUser
).
- Teraz czas na konfigurację NHibernate. W tym celu w projekcie NoteBookApp.Server tworzymy katalog Infrastructure, a w nim klasę
NHibernateSqlServerInstaller
:
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using NHibernate.AspNetCore.Identity;
using NHibernate.Cfg;
using NHibernate.Connection;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Mapping.ByCode;
using NHibernate.NetCore;
using NoteBookApp.Logic.Domain;
using NoteBookApp.Logic.Mappings;
namespace NoteBookApp.Server.Infrastructure
{
public static class NHibernateSqlServerInstaller
{
public static IServiceCollection AddNHibernateSqlServer(this IServiceCollection services, string cnString)
{
var cfg = new Configuration();
cfg.DataBaseIntegration(db =>
{
db.Dialect<MsSql2012Dialect>();
db.Driver<MicrosoftDataSqlClientDriver>();
db.ConnectionProvider<DriverConnectionProvider>();
db.LogSqlInConsole = true;
db.ConnectionString = cnString;
db.Timeout = 30;/*seconds*/
db.SchemaAction = SchemaAutoAction.Validate;
});
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddDefaultTokenProviders()
.AddHibernateStores();
cfg.Cache(c => c.UseQueryCache = false);
var mapping = new ModelMapper();
mapping.AddMappings(typeof(ApplicationUserMapping).Assembly.GetTypes());
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserMappingMsSql));
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserLoginMappingMsSql));
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserTokenMappingMsSql));
var mappingDocument = mapping.CompileMappingForAllExplicitlyAddedEntities();
cfg.AddMapping(mappingDocument);
services.AddHibernate(cfg);
#if DEBUG
cfg.BuildSessionFactory();
#endif
return services;
}
}
}
W tej klasie znajduje się cała konfiguracja NHibernate. Jeśli nie wiesz jak korzystać z NHibernate, to tutaj znajdziesz tutorial opisujący podstawy. Poniżej, klika ciekawszych fragmentów:
Konfiguracja ASP.NET Identity
Korzysta z pakietu NHibernate.AspNetCore.Identity.
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddDefaultTokenProviders()
.AddHibernateStores();
Usprawnione debugowanie
#if DEBUG
cfg.BuildSessionFactory();
#endif
Powyższy kod jest odpalany tylko w trybie DEBUG
, gdyż ma nam ułatwić wyłapywanie błędów (np. brakujących kolumn w bazie danych itp). Standardowo cfg.BuildSessionFactory
jest wywoływana później i z tego względu, gdy ta metoda rzuci wyjątek, to mamy utrudnione zadanie, aby go złapać i zobaczyć co jest nie tak (w przypadku braków w bazie danych, obiekt wyjątku pokazuje czego brakuje).
Gdy metodę odpalimy ręcznie, to nasze IDE bez problemu pokaże nam ewentualne wyjątki.
Dodawanie mappingów dla ASP.NET Identity
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserMappingMsSql));
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserLoginMappingMsSql));
mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserTokenMappingMsSql));
W tym miejscu konfigurujemy obiekty ASP.NET Identity (pochodzące z NHibernate.AspNetCore.Identity) w NHibernacie.
- Ostatnim krokiem jest wywołanie naszej konfiguracji w klasie
Startup
. Poniższy kod dodaj na początek metodyConfigureServices
:
var connectionString = Configuration.GetValue<string>("DefaultConnection");
services.AddNHibernateSqlServer(connectionString);
Connection String do naszej bazy znajduje się w plik appsettings.json
{
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=NotesDb;Trusted_Connection=True;MultipleActiveResultSets=true",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"IdentityServer": {
"Clients": {
"NoteBookApp.Client": {
"Profile": "IdentityServerSPA"
}
}
},
"AllowedHosts": "*"
}
W tym momencie masz prawidłowo skonfigurowanego NHibernate w aplikacji ASP.NET Core.
Konfiguracja IdentityServer (opcjonalnie)
Jeśli Twoja aplikacja ASP.NET Core współpracuje z aplikacją SPA napisaną w Blazor (jak w naszym przykładzie), pozostaje nam jeszcze skonfigurować bibliotekę IdentityServer, która odpowiada za proces uwierzytelniania użytkowników.
Dodaj poniższy kod do metody ConfigureServices
w klasie Startup
services.AddIdentityServer()
.AddAspNetIdentity<ApplicationUser>()
.AddIdentityResources()
.AddApiResources()
.AddClients()
.AddDeveloperSigningCredential();
W tym kodzie prócz standardowych ustawień, wskazujemy naszą nową klasę ApplicationUser
. Dzięki temu, IdentityServer będzie z niej korzystał (i NHibernate) podczas logowania.
Tworzenie bazy danych
Mamy już napisany kod, ale nie mamy przygotowanej bazy danych (z tabelami dla ASP.NET Identity). Czas to zmienić.
- Stwórz pustą bazę w Sql Server. Możesz również skorzystać z innego silnika bazodanowego, np. MySql, Postgres, SqLite)
- Aby dodać tabelę dla ASP.NET Identity, możesz przejść na stronę projektu NHibernate.AspNetCore.Identity:
Znajdziesz tam skrypty tworzące odpowiednie tabele dla wybranych silników bazodanowych. Uruchom skrypt 00_aspnet_core_identity.sql na swojej bazie danych.
- Następnie musisz stworzyć tabelę dla
ApplicationUser
. Tutaj jest przykładowy skrypt:
CREATE TABLE ApplicationUser
(
ApplicationUser_Key nvarchar(32) NOT NULL Primary key,
constraint FK_ApplicationUser_AspNetUsers foreign key (ApplicationUser_Key) references AspNetUsers,
)
- Po tej operacji baza powinna wyglądać podobnie do mojej
Teraz powinieneś być w stanie uruchomić testową aplikację, stworzyć nowe konto i zalogować się.
Podsumowanie
Dodanie NHibernate do projektu ASP.NET Core jest dość prostą operacją, jednak wymaga trochę pracy. W kolejnych artykułach powiem Ci, dlaczego lubię NHibernate i co jeszcze można z nim ciekawego zrobić.
Napisz proszę z jakiego ORM korzystasz w swoich projektach i jakie to narzędzie ma plusy.
Użycie NHibernate w aplikacji ASP.NET Core – SoloProgramista
Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl
Hej Romek,
kolejny fajny artykuł 😉
Mnie interesuje jak będziesz ogarniał aktualizacje/migracje bazy danych w swoim projekcie korzystając z NHibernate 🙂
W przypadku aplikacji SaaS aktualizacje bazy nie są dużym problemem, gdyż jest tylko kilka instancji bazy danych, które trzeba zaktualizować. Dodatkowo nie ma sytuacji, gdzie różni klienci mają różne wersje programu, a co za tym idzie w danej chwili istnieją różne wersje bazy.
W moim przypadku mam następujące instancje bazy danych:
– Produkcyjna (Azure)
– Testowa (Azure)
– Lokalna (PC)
– Lokalna (Laptop)
W momencie, gdy zmieniam aplikację, a co za tym idzie schemat bazy danych, od razu przygotowuję skrypt SQL do aktualizacji obecnej bazy danych i od razu aktualizuję bazę na komputerze, na którym pracuję.
W trakcie dewelopmentu danego ficzera, aktualizuję ten skrypt. Gdy ficzer jest gotowy i uploaduje nową wersję aplikacji do środowiska testowego Azure, biorę mój skrypt i odpalam go na bazie testowej.
W momencie gdy chcę wrzucić nowy ficzer na produkcję, to odpalam mój skrypt na produkcyjnej bazie (w tym momencie skrypt był przetestowany na trzech pozostałych bazach).
Obecnie to podejście jest dla mnie wystarczające. W przypadku, gdy będzie powodowało zbyt wiele problemów, to pomyślę nad automatyzacją tego procesu
[…] Jeśli chciałbyś dowiedzieć się jak użyć NHibernate w aplikacji ASP.NET Core, to zapraszam Cię do tego artykułu. […]
d-change.net
Użycie NHibernate w aplikacji ASP.NET Core – SoloProgramista