Tryb Live Demo w aplikacji ASP.NET Core i Blazor

Programowanie

W tym artykule opisuję, jak zaimplementować tryb Live Demo w aplikacji ASP.NET Core i Blazor. Zaproponowane rozwiązanie jest oparte o NHibernate, jednak w przypadku EntityFrameworka koncepcja pozostaje bez zmian, a jedynie należałoby podmienić kod związany z samym dostępem do bazy danych.

Wstęp

Na moim blogu zacząłem cykl Z Pamiętnika SaaSa, w którym opisuję własne podejście do tworzenia aplikacji w modelu SaaS. W ramach tego cyklu powstaje aplikacja NotesApp, w której prezentuję najważniejsze techniki. Ostatnio dodałem wysyłanie/odbieranie plików z Azure Blob Storage i właśnie do wersji aplikacji z tego artykułu dodamy tryb Live Demo.

Kod źródłowy: https://github.com/robocik/NotesApp_V3_WithDemo

Tryb Live Demo

Jednym z problemów, przed którym stoi twórca aplikacji SaaS, jest zachęcenie osoby, które odwiedziły stronę aplikacji, aby założyły konto i zaczęły korzystać z programu. Często użytkownik widząc, że musi zarejestrować konto w nieznanej aplikacji, szybko rezygnuje. Co więcej, nie wie, czy wogóle warto podawać swojego maila.

Załóżmy, że użytkownik założył konto, żeby potestować Twój program, to zastanów się, co zobaczy na początku. Nawet jeśli Twoja aplikacja ma wiele ciekawych ficzerów, rysuje piękne wykresy oraz prezentuje zaawansowane roczne zestawienia, to jednak na początku program jest pusty. Nie ma danych, nie rysuje wykresów, nie wyświetla rocznych zestawień.

Jednym z rozwiązań, które polecam, jest zaimplementowanie trybu Live Demo. Polega on na tym, że aplikacja ma pewne konto w systemie, na które każdy może sie zalogować. To konto jest wypełnione danymi, co sprawia, że od razu użytkownik ma dostęp do większości ficzerów aplikacji i może dostrzec jej potencjał. Sam może zapoznać się z jej możliwościami, pododawać nowe rzeczy i usunąć istniejące – po prostu może się pobawić Twoim programem.

Założenia

Nasza aplikacja obecnie ma następujące elementy, które musimy uwzględnić implementując tryb Live Demo:

  • Dodawanie i usuwanie notatek (baza Sql Server)
  • Dodawanie i usuwanie plików (Azure Blob Storage)
  • Tworzenie konta (baza Sql Server)
  • Wysyłanie powiadomień (email)

Tryb Live Demo nie jest normalnym trybem działania aplikacji. Głównym założeniem jest to, że zmiany wprowadzone przez użytkownika podczas poznawania programu, nie mają wpływu na innych. Musimy zaimplementować pewnego rodzaju piaskownicę (sandbox), w której użytkownik będzie mógł zrobić wszystko co przyjdzie mu do głowy i nie zakłóci pracy pozostałych userów.

Drugim wymaganiem jest przygotowanie startowego zestawu danych dla nowego użytkownika. Gdy ktoś wchodzi na naszą stronę i odpala tryb Live Demo, chcemy aby zobaczył system wypełniony specjalnie przygotowanymi danymi, które najlepiej pokażą możliwości programu.

Trzecim wymaganiem jest sprawne usuwanie starych danych. Tryb Live Demo jest tymczasowy. Człowiek wchodzi do aplikacji, poklika, popatrzy i wychodzi. W tym czasie mógł dodać wiele nowych notatek do bazy, wysłać dużą ilość plików itp. Nie chcemy, aby jakiekolwiek dane stworzone podczas sesji Demo zajmowały miejsce na naszym serwerze. Musimy mieć skuteczny sposób na usuwanie wszystkich danych.

Tylko jak to wszystko zrobić, aby się nie narobić?

Na początek – upraszczamy

Zanim przystąpimy do implementacji trybu Live Demo, warto jest zastanowić się tak naprawdę, które ficzery w naszym programie chcemy, aby były dostępne w tym trybie, a które nie ma potrzeby w ogóle prezentowania użytkownikowi. W końcu soloprogramista dąży do robienia tylko tego, co jest faktycznie potrzebne.

W naszym przykładzie głównymi ficzerami, które muszą być dostępne w trybie to dodawanie i usuwanie notatek. To jest trzon naszej aplikacji i to z powodu notatek ludzie wchodzą na naszą stronę i myślą o używaniu programu.

A co z wysyłaniem plików? Z jednej strony chcemy pokazać użytkownikowi, że nasz program umożliwia przechowywanie plików, a z drugiej implementacja piaskownicy dla tej funkcjonalności zajęłaby sporo czasu. Moim zdaniem nie ma sensu implementować wysyłania/odbierania plików w trybie Live Demo, a przynajmniej faktycznego wysyłania. W moim programie easyRenti tryb Live Demo daje użytkownikowi wrażenie wysyłania plików, jednak fizycznie pliku nie wysyła. Czyli gdy użytkownik w trybie Demo chce dodać plik, program tworzy wpis w bazie danych o nowym pliku, ale już nie wysyła jego zawartości. Dzięki takiemu rozwiązaniu, korzystając z trybu testowego, użytkownik po „wysłaniu” pliku będzie go widział w aplikacji. Jednak gdy spróbuje go pobrać (otworzyć), dostanie informację, że w trybie Demo wysyłanie plików nie działa. Jest duża szansa, że testując program, nie dojdzie on do pobrania pliku – wrzucił go tam tylko do testów i tryb Demo pokazał, że taki ficzer działa. Gdyby jednak chciał go ściągnać, to dostanie informację, że w normalnym trybie plik właśnie by został ściągnięty, jednak w trybie Demo widzi tylko tą wiadomość.

A co z tworzeniem nowego konta? Tutaj także uważam, że nie ma takiej potrzeby, a nawet mogłoby to być zniechęcające dla potencjalnego użytkownika. Jaki jest sens pokazywać mu, że nasz program umożliwia stworzenie konta? Nie jest to nic niezwykłego i 99% stron i programów w Internecie tak właśnie działa. Co więcej, istnieje ryzyko, że nasz user musząc najpierw stworzyć konto w trybie Live Demo, zniechęciłby się i tyle. W związku z tym, jak pisałem wcześniej, tryb Live Demo powinien mieć już konto utworzone, na które trzeba się tylko zalogować.

Pamiętaj, że tryb demo nie musi mieć zaimplementowanych 100% wszystkich funkcjonalności programu. On ma tylko przekonać użytkownika, że warto jest zacząć korzystać z Twojej aplikacji.

Jak natomiast zaimplementować wysyłanie emaili czy innych powiadomień? Oczywiście nie implementować w ogóle. Pomijając sytuację, że wysyłanie emaili to jest główny ficzer Twojego systemu i bez tego nie ma on sensu, to w całej reszcie aplikacji, powiadomienia są tylko dodatkiem, który uważam, może być pominięty w Live Demo.

Jak zaimplementować tryb Live Demo?

Dzięki powyższej analizie, udało się uprościć nasze wymagania do niezbędnego minimum. To co musimy zaimplementować to piaskownica dla operacji bazodanowych, z łatwym przygotowywaniem danych dla nowego użytkownika oraz z czyszczeniem bazy na zakończenie jego testów. Pytanie tylko jak to w miarę prosto zrobić?

To co może na początku przyjść do głowy to korzystanie ze standardowej bazy danych (np. Sql Server) i przygotowanie kodu, który w pewnym momencie usuwa rekordy z bazy danych należące do trybu Demo. Jednak pomijając fakt, że jest to dość pracochłonne to większym problemem jest zaimplementowanie wielu tych samych użytkowników. Jak wspomniałem tryb Demo polega na tym, że jest utworzone konto w programie (np. DemoUser) i można się na niego zalogować. Wtedy przed naszymi oczami pojawiają się przygotowane wcześniej dane. Pamiętaj jednak o tym, że w jednym czasie może kilka osób korzystać z trybu Demo, więc jak zapewnić w jednej bazie oddzielny zestaw danych dla tego testowego konta? Można to oczywiście jakoś zaimplementować,, jednak polecam Ci o wiele prostszy sposób..

NHibernate + SqLite = Live Demo 🙂

Czytając moje wcześniejsze artykuły mogłeś zauważyć, że jestem dużym fanem ORM NHibernate. Do tej pory NH był wykorzystany w standardowy sposób, czy jako warstwa dostępu do bazy danych. Jednak można go wykorzystać w bardziej zaawansowany sposób. Dzięki wykorzystaniu możliwości NH do zmiany bazy danych na Sqlite, implementacja trybu Live Demo jest bardzo prosta. Sqlite jest to baza danych w pliku na dysku, dlatego bardzo łatwo możemy ją usunąć, więc temat czyszczenia danych będzie załatwiony praktycznie z automatu.

Schemat działania jest prosty:

  1. Użytkownik uruchamia tryb Demo. Aplikacja wyświetla mu okno logowania, z testową nazwą usera.
  2. Po kliknięciu przycisku Login, tworzymy nową bazę plikową SqLite, a następnie wypełniamy bazę początkowymi danymi.
  3. Ponieważ chcemy, aby nasza baza była przypisana do bieżącej sesji użytkownika, to naturalnym rozwiązaniem jest włączenie Sesji w ASP.NET Core
  4. Po skończonej sesji musimy usnąć plik bazy danych dla tego użytkownika

Baza per user = architektura Multitenant

Ok, wszystko fajnie, baza w pliku, ale w jaki sposób zmusimy NHibernate, aby korzystał z różnych baz dla różnych userów w tym samym czasie? Co innego podać connection string do pliku na dysku w appsettings.json, a co innego podmieniać bazę w runtime.

Tutaj przychodzi z pomocą koncepcja Multitenant z różnymi bazami danych. Podstawowe informacje o tej architekturze znajdziesz m.in. tutaj. Jednak nas w tym momencie nie interesuje implementowanie tej architektury ze względów wydajnościowych, czy bezpieczeństwa, a jedynie dla trybu Live Demo. Zwróć uwagę, że właśnie ten tryb idealnie pasuje do architektury Multitenant z wieloma bazami danych.

A teraz wisienka na torcie – moje niedawne odkrycie. NHibernate posiada wsparcie dla architektury multitenant i podmieniania baz danych w runtimie, w związku z tym, implementacja będzie bardzo prosta.

Przejdźmy zatem do kodu

W naszym projekcie NHibernate jest konfigurowany w klasie NHibernateSqlServerInstaller. To jest podstawowa konfiguracja korzystająca z bazy Sql Server. My musimy stworzyć konfigurację dla trybu Live Demo.

public static class NHibernateSqLiteInstaller
{
    public static IServiceCollection AddDemoNHibernate(this IServiceCollection services)
    {
        var cfg = new Configuration();
        cfg.DataBaseIntegration(db =>
        {
            db.Dialect<SQLiteDialect>();
            db.Driver<SQLite20Driver>();
            db.ConnectionProvider<DriverConnectionProvider>();
            db.MultiTenancyConnectionProvider<MultiTenantConnectionProvider>();
            db.MultiTenancy = MultiTenancyStrategy.Database;
            db.LogSqlInConsole = true;
            db.LogFormattedSql = true;
            db.ConnectionString = @"Data Source='wwwroot\Temp\TestDb.db';Version=3;New=True;Compress=True;";
        });

        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.IdentityUserMappingSqlite));
        mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserLoginMappingSqlite));
        mapping.AddMapping(typeof(NHibernate.AspNetCore.Identity.Mappings.IdentityUserTokenMappingSqlite));


        var mappingDocument = mapping.CompileMappingForAllExplicitlyAddedEntities();
        cfg.AddMapping(mappingDocument);
        services.AddHibernate(cfg);

        services.AddScoped(typeof(IUserStore<ApplicationUser>), typeof(UsersService));

        services.AddScoped<ISession>(provider =>
        {
            var nhSession = CreateNhSession(provider, cfg);

            return nhSession;
        });
        return services;
    }

    private static ISession CreateNhSession(IServiceProvider provider, Configuration cfg)
    {
        var httpContextAccessor = provider.GetService<IHttpContextAccessor>();
        var tempId = httpContextAccessor!.HttpContext!.Session.GetString("TenantId");
        Guid tenantId = Guid.Empty;
        if (tempId != null)
        {
            tenantId = new Guid(tempId);
        }

        var nhSession = provider.GetService<ISessionFactory>()!.WithOptions()
            .Tenant(new TenantConfiguration(tenantId.ToString())).OpenSession();

        var file = new FileInfo(@$"wwwroot\Temp\{tenantId}.db");
        if (!file.Exists || file.Length == 0)
        {
            FillDatabase(cfg, nhSession);
        }

        return nhSession;
    }

    private static void FillDatabase(Configuration cfg, ISession? nhSession)
    {
        SchemaExport exp = new SchemaExport(cfg);
        exp.Execute(true, true, false, nhSession!.Connection, null);

        using (var trans = nhSession.BeginTransaction())
        {
            var demo = new DemoDataCreator(nhSession);
            demo.Create();
            trans.Commit();
        }
    }
}

Ta konfiguracja jest podobna do tej standardowej, z tym że korzystamy z bazy SqLite, a także włączamy tryb Multitenant oraz rejestrujemy provider tenanta:

db.MultiTenancy = MultiTenancyStrategy.Database;
db.MultiTenancyConnectionProvider<MultiTenantConnectionProvider>();

W ogólnym zarysie, provider tenanta instruuje NH, którą konkretnie bazę (connection string) należy użyć w bieżącym requeście. Wykorzystuje się do tego jakiś identyfikator, który aplikacja używa do wybrania właściwej bazy danych. W przypadku architektury multitenant, często ten identyfikator jest przesyłany w nagłówku requesta lub w adresie strony, np:

https://renti.easywsdl.com/companyX/houses

lub

https://renti.easywsdl.com/houses?company=companyX

W powyższym przypadku companyX jest naszym identyfikatorem tenanta.

Gdy stosujemy tą architekturę do trybu Demo, tutaj można wybrać inny sposób przechowywania identyfikatora. Ja polecam włączyć w ASP.NET stan sesji i to w nim trzymać losowy identyfikator Guid. Stan sesji jest utrzymywany tak długo jak użytkownik ma otwartą przeglądarkę. Gdy ją zamknie, to sesja się kończy (w dużym uproszczeniu). Mamy zatem mechanizm idealnie pasujący do naszych wymagań – dane, które użytkownik wprowadzi podczas trybu Live Demo, powinny być dostępne tak długo, jak jest otwarta przeglądarka. Gdy zostanie zamknięta, a użytkownik odpali tryb Demo następnego dnia, to dostanie nowy (początkowy) zestaw danych.

Zatem nasz tenant provider wygląda tak

class MultiTenantConnectionProvider : AbstractMultiTenancyConnectionProvider
{
    protected override string GetTenantConnectionString(TenantConfiguration tenantConfiguration, ISessionFactoryImplementor sessionFactory)
    {
        var connectionString = $@"Data Source='wwwroot\Temp\{tenantConfiguration.TenantIdentifier}.db';Version=3;New=True;Compress=True;";
        return connectionString;
    }
}

Identyfikator tenanta jest nazwą pliku Sqlita. Gdy tworzymy sesję NH (metoda CreateNhSession), najpierw sprawdzamy w sesji ASP.NET, czy jest wygenerowany identyfikator tenanta i jeśli nie, to go generujemy (na starcie sesji ASP.NET):

var httpContextAccessor = provider.GetService<IHttpContextAccessor>();
var tempId = httpContextAccessor!.HttpContext!.Session.GetString("CompanyId");
Guid companyId=Guid.Empty;
if (tempId != null)
{
   companyId = new Guid(tempId);
}

Następnie przekazujemy go do NH. Sprawdzamy też, czy na dysku istnieje plik z taką nazwą i jeśli nie, to tworzymy nową bazę danych i wypełniamy ją początkowymi danymi:

var nhSession = provider.GetService<ISessionFactory>()!.WithOptions()
                .Tenant(new TenantConfiguration(companyId.ToString())).OpenSession();

var file = new FileInfo(@$"wwwroot\Temp\{companyId}.db");
if (!file.Exists || file.Length == 0)
{
    FillDatabase(cfg, nhSession, companyId);
}

return nhSession;

Teraz naszą konfigurację dla trybu Live Demo musimy zarejestrować. Ja polecam następujące rozwiązanie, które z powodzeniem zastosowałem. Opiszę je na podstawie aplikacji easyrenti.

Mianowicie stawiamy dwie niezależne instancje naszej aplikacji:

https://renti.easywsdl.com – właściwa instancja (produkcyjna z bazą Sql Server)

https://demo.easywsdl.com/ – instancja dla trybu live demo (z bazą Sqlite).

Na startowej stronie instancji głównej (https://renti.easywsdl.com) dajemy link przekierowujący do instancji Live Demo.

Konfigurację NH rejestrujemy w zależności od ustawienia w pliku appsettings.json

protected virtual void ConfigureNHibernate(IServiceCollection services, AppSettings settings)
{
    if (settings.IsDemo)
    {
        services.AddDemoNHibernate();
        services.AddSingleton<ISessionStore, DistributedSessionStoreWithStart>();
        services.AddSession(options => {
                    options.Cookie.IsEssential = true;
                    options.IdleTimeout = TimeSpan.FromMinutes(30);
                    options.Cookie.Name = ".MyApplication";
        });
     }
     else
     {
        var connectionString = Configuration.GetValue<string>("DefaultConnection");
        services.AddNHibernateSqlServer(connectionString);
     }
}

Dodatkowo w pliku Startup.cs włączamy obsługę sesji ASP.NET Core (tylko dla instancji Live Demo)

if (settings?.IsDemo == true)
{
     app.UseSession();
}

Wysyłanie plików w trybie Live Demo

Jak już wspominałem, wysyłanie i pobieranie plików będzie zaimplementowane tylko częściowo. Aplikacja wykona większość czynności, jednak pominie faktyczne wysyłanie i pobieranie plików.

Dzięki temu, że w naszej aplikacji wydzieliliśmy specjalny interface IFileSystemProvider, którego implementacja odpowiada za komunikację z Azure Blob Storage, to najprościej jest dla trybu Demo stworzyć nową implementację, która praktycznie nie robi nic:

public class LiveDemoFileSystemProvider: IFileSystemProvider
{
    public Task<FileMetaInfo> Upload(Stream stream, FileIdentifier data)
    {
        var info = new FileMetaInfo(data.File, stream.Length);
        return Task.FromResult(info);
    }

    public Task<FileAccessToken> GenerateUploadToken(string userId, string fileName)
    {
        var token = new FileAccessToken(Constants.Demo, Constants.Demo,Guid.Empty);
        
        return Task.FromResult(token);
    }

    public Task Delete(FileIdentifier data)
    {
        return Task.CompletedTask;
    }

    public string GenerateReadToken(string companyId, string fileId, string fileName)
    {
        return Constants.Demo;
    }

    public string GetFileUrl(string container, string file, string? param = null)
    {
        return Constants.Demo;
    }
}

Zwróć uwagę, że wszędzie tam gdzie zwracamy jakiegoś stringa (np token do Azure czy Url do pliku), nasza okrojona implementacja zwraca „DEMO”, dzięki temu w aplikacji Blazor będziemy w stanie wykryć, czy mamy do czynienia z trybem testowym i ew. pominąć pewne kroki lub wyświetlić odpowiedni komunikat.

private async Task DownloadFileAsync(Guid fileId)
{
    try
    {
        var url = await FileDataService.GetFileDirectUrl(fileId);
        if (url == NoteBookApp.Shared.Constants.Demo)
        {
            ShowError("W wersji Demo wysyłanie plików jest wyłączone. Jednak standardowo w tym momencie Twój plik zostałby ściągnięty");
        }
        else
        {
            await JSRuntime.InvokeVoidAsync("DownloadFile", url);
        }
        
    }
    catch (ObjectNotFoundException ex)
    {
        ShowError("Plik już nie istnieje.", ex);
        await refreshFiles();
    }
    catch (Exception ex)
    {
        ShowError("Nie udało się pobrać pliku. Proszę spróbować ponownie.", ex);
    }
}

Czyszczenie bazy

W związku z tym, iż na początku poczyniliśmy pewne uproszczenia, między innymi odnośnie operacji na plikach, mamy bardzo prostą sytuację, gdy chcemy wyczyścić dane po naszym testowym użytkowniku. Należy tylko usunąć plik bazy z dysku i tyle.

Jednak na pierwszy rzut oka, może wydawać się to bardziej skomplikowane. Skoro mówimy o czyszczeniu bazy na koniec testowania, to tak naprawdę kiedy usuniemy plik z bazą danych? Przecież nie jesteśmy w stanie w łatwy sposób wykryć momentu, kiedy użytkownik przestaje być na naszej stronie. Możesz pomyśleć o umieszczeniu kodu w jakimś zdarzeniu na zakończenie sesji ASP.NET, albo zaimplementować komunikację za pomocą SignalR itp.

Moim jednak zdaniem, wogóle nie ma takiej potrzeby. Przynajmniej na początku istnienia programu możesz usuwać pliki np. raz dziennie, albo nawet raz na miesiąc ręcznie. Jednak jak na soloprogramistę przystało, ręczne usuwanie bym sobie odpuścił. Usuwanie raz dziennie wymagałoby zaimplementowania jakiegoś mechanizmu odpalającego naszą procedurę w nocy, co też wymaga pracy.

Proponuję Ci następujące rozwiązanie. W pliku Startup.cs dodaj następujący kod:

if (settings?.IsDemo == true)
{
    var tempFolder = new DirectoryInfo("wwwroot\\Temp");
    if (tempFolder.Exists)
    {
        tempFolder.Delete(true);
    }
    tempFolder.Create();
    app.UseSession();
}

W momencie, gdy pierwszy użytkownik wchodzi na naszą stronę, jest wykonywany kod w klasie Startup i wtedy właśnie usuniemy wszystkie poprzednie bazy danych. Nie interesuje nas czy ta procedura będzie wykonana raz czy 10 razy dziennie. Ważne, że pliki zostaną usunięte i to bez eventów i schedulerów.

Coś jeszcze?

Oczywiście powyższa implementacja jest celowo uproszczona, na potrzeby artykułu – zakładam, że w realnej aplikacji tryb Live Demo może być bardziej rozbudowany. To co jeszcze możemy dodać, to informacja dla użytkownika, jaka jest nazwa konta i hasło oraz wypełnić te pola już na starcie, aby było wymagane tylko kliknięcie w przycisk Login.

Ten krok w przypadku realnego projektu praktycznie zawsze będzie wykonany z uwagi na to, iż wygląd strony logowania (a także pozostałych stron, np rejestracji konta itp) będziemy chcieli dostosować do projektu graficznego strony. W naszym jednak przypadku potrzebujemy dodać do strony logowania informacje dotyczące testowej nazwy użytkownika i hasła oraz wypełnić tymi danymi odpowiednie pola. Dzięki temu użytkownik będzie miał uproszczone zadanie – wystarczy kliknąć w przycisk Login.

Informacje dot. logowania w trybie Live Demo

Jeśli zastanawiałeś się, jak to działa, że w naszym projekcie ASP.NET nie ma żadnych plików reprezentujących stronę logowania oraz rejestracji konta, a jednak sama aplikacja takie strony posiada to już śpieszę z wyjaśnieniem. Faktycznie, takich stron domyślnie w projekcie nie ma, gdyż są osadzone w pakiecie Microsoft.AspNetCore.Identity.UI. W sytuacji gdy chcemy zmodyfikować wygląd lub działanie tych stron, Visual Studio oferuje opcje wygenerowania i dodania ich do projektu.

Aby to zrobić należy:

  1. Kliknąć prawym myszy na nazwie projektu w oknie Solution Explorer (w naszym przypadku NoteBookApp.Server) i wybrać polecenie New… -> New Scaffolded Item…
Polecenie New Scaffolded Item... w Solution Explorer
  1. W oknie Add New Scaffolded Item zaznacz Identity i kliknij przycisk Add
  2. Po dłuższej chwili pojawi się okno, w którym masz możliwość wybrania, które strony chcesz, aby zostały wygenerowane. W naszym przypadku zaznaczam tylko Account\Login, jednak zwykle w projektach zaznaczam wszystkie opcje.
Wybór stron do wygenerowania w oknie Add Identity
  1. Dodatkowo musisz wskazać klasę Data context dla EntityFrameworka (kliknij + i dodaj domyślną). Niestety ten generator z góry zakłada, że używamy ORM z Microsoftu. Po wygenerowaniu, usuniemy zbędny kod.
  2. Kliknij przycisk Add. Po chwili odpowednie pliki powinny pojawić się w Twoim projekcie.
Wygenerowane pliki

6. Na koniec pozostaje usunąć wszystkie rzeczy związane z EntityFramework. Dlatego zaznacz w Solution Explorer Twój projekt i usuń następujące pakiety Nugetowe:

  • Microsoft.AspNetCore.Identity.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

Skompiluj kod i usuń wszystko co jest związane z EF i się nie kompiluje.

Podsumowanie

Gdy postanowiłem dodać tryb Live Demo do mojej aplikacji easyrenti, ku mojemu zdziwieniu okazało się, że nie ma w Internetach zbyt wiele przykładów jak to zrobić – co tym bardziej zachęciło mnie do tego, aby temat rozpracować. Sposób, który opisałem powyżej uważam, że jest prostym podejściem, które spełnia wszystkie postawione przed nim wymagania.

Jeśli zamiast NHibernate, korzystasz z EntityFrameworka, możesz spróbować podobnego rozwiązania – zakladam, że powinieneś być w stanie zaimplementować tryb Live Demo w swojej aplikacji. Do czego oczywiście szczerze zachęcam, gdyż uważam, że jest to doskonały sposób, aby zaprezentować potencjalnemu użytkownikowi wszystkie możliwości naszej aplikacji.

Share