Megosztás a következőn keresztül:


A ASP.NET Core háttérkiszolgáló SDK használata

Ez a cikk bemutatja, hogy konfigurálnia kell és használnia kell a ASP.NET Core háttérkiszolgáló SDK-t az adatszinkronizálási kiszolgáló létrehozásához.

Támogatott platformok

A ASP.NET Core háttérkiszolgáló támogatja a ASP.NET 6.0-s vagy újabb verzióját.

Az adatbázis-kiszolgálóknak meg kell felelniük az alábbi feltételeknek, DateTime és ezredmásodperc pontossággal tárolt vagy Timestamp típusmezővel kell rendelkezniük. Az adattár implementációit az Entity Framework Core és a LiteDb biztosítja.

Konkrét adatbázis-támogatásért tekintse meg a következő szakaszokat:

Új adatszinkronizálási kiszolgáló létrehozása

Az adatszinkronizálási kiszolgáló a normál ASP.NET Core-mechanizmusokat használja a kiszolgáló létrehozásához. Három lépésből áll:

  1. Hozzon létre egy ASP.NET 6.0-s (vagy újabb) kiszolgálóprojektet.
  2. Entity Framework Core hozzáadása
  3. Adatszinkronizálási szolgáltatások hozzáadása

Az Entity Framework Core-nal létrehozott ASP.NET Core-szolgáltatásról az oktatóanyagban olvashat.

Az adatszinkronizálási szolgáltatások engedélyezéséhez a következő NuGet-kódtárakat kell hozzáadnia:

Módosítsa a Program.cs fájlt. Adja hozzá a következő sort az összes többi szolgáltatásdefinícióhoz:

builder.Services.AddDatasyncControllers();

A ASP.NET Core-sablont datasync-server is használhatja:

# This only needs to be done once
dotnet new -i Microsoft.AspNetCore.Datasync.Template.CSharp
mkdir My.Datasync.Server
cd My.Datasync.Server
dotnet new datasync-server

A sablon tartalmaz egy mintamodellt és egy vezérlőt.

Táblavezérlő létrehozása SQL-táblához

Az alapértelmezett adattár az Entity Framework Core-t használja. A táblavezérlő létrehozása három lépésből áll:

  1. Hozzon létre egy modellosztályt az adatmodellhez.
  2. Adja hozzá a modellosztályt az DbContext alkalmazáshoz.
  3. Hozzon létre egy új TableController<T> osztályt a modell felfedéséhez.

Modellosztály létrehozása

Minden modellosztálynak implementálnia ITableDatakell. Minden adattártípushoz tartozik egy absztrakt osztály, amely implementálja azokat ITableData. Az Entity Framework Core-adattár a következőt használja EntityTableData:

public class TodoItem : EntityTableData
{
    /// <summary>
    /// Text of the Todo Item
    /// </summary>
    public string Text { get; set; }

    /// <summary>
    /// Is the item complete?
    /// </summary>
    public bool Complete { get; set; }
}

Az ITableData interfész biztosítja a rekord azonosítóját, valamint az adatszinkronizálási szolgáltatások kezeléséhez szükséges további tulajdonságokat:

  • UpdatedAt (DateTimeOffset?) megadja a rekord utolsó frissítésének dátumát.
  • Version (byte[]) egy átlátszatlan értéket biztosít, amely minden íráskor megváltozik.
  • Deleted (bool) igaz, ha a rekord törlésre van megjelölve, de még nincs törölve.

Az Adatszinkronizálási kódtár fenntartja ezeket a tulajdonságokat. Ezeket a tulajdonságokat ne módosítsa a saját kódjában.

Frissítse a DbContext

Az adatbázis minden modelljét regisztrálni kell a DbContext. Például:

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

    public DbSet<TodoItem> TodoItems { get; set; }
}

Táblavezérlő létrehozása

A táblavezérlő speciális ApiController. Íme egy minimális táblázatvezérlő:

[Route("tables/[controller]")]
public class TodoItemController : TableController<TodoItem>
{
    public TodoItemController(AppDbContext context) : base()
    {
        Repository = new EntityTableRepository<TodoItem>(context);
    }
}

Megjegyzés:

  • A vezérlőnek rendelkeznie kell egy útvonalsal. Konvenció szerint a táblák a részúton /tablesjelennek meg, de bárhol elhelyezhetők. Ha az 5.0.0-s verziónál korábbi ügyfélkódtárakat használ, akkor a táblának a segédkönyvtárnak /tableskell lennie.
  • A vezérlőnek a következőtől TableController<T>kell örökölnie: az <T> adattártípus implementációjának ITableData helye.
  • Rendeljen hozzá egy tárházat a modell típusától függően.

Memóriabeli adattár implementálása

A memóriában lévő tárházat állandó tárterület nélkül is használhatja. Adjon hozzá egy egyszeri szolgáltatást az adattárhoz a következőben Program.cs:

IEnumerable<Model> seedData = GenerateSeedData();
builder.Services.AddSingleton<IRepository<Model>>(new InMemoryRepository<Model>(seedData));

Állítsa be a táblázatvezérlőt az alábbiak szerint:

[Route("tables/[controller]")]
public class ModelController : TableController<Model>
{
    public MovieController(IRepository<Model> repository) : base(repository)
    {
    }
}

Táblavezérlő beállításainak konfigurálása

A vezérlő bizonyos aspektusait a következővel TableControllerOptionskonfigurálhatja:

[Route("tables/[controller]")]
public class MoodelController : TableController<Model>
{
    public ModelController(IRepository<Model> repository) : base(repository)
    {
        Options = new TableControllerOptions { PageSize = 25 };
    }
}

A beállítási lehetőségek a következők:

  • PageSize (intalapértelmezett: 100) az egy lapon visszaadott lekérdezési művelet által visszaadott elemek maximális száma.
  • MaxTop (intalapértelmezett: 512000) a lekérdezési műveletben lapozás nélkül visszaadott elemek maximális száma.
  • EnableSoftDelete (boolalapértelmezett: false) lehetővé teszi a helyreállítható törlést, amely az elemeket töröltként jelöli meg az adatbázisból való törlés helyett. A helyreállítható törlés lehetővé teszi az ügyfelek számára az offline gyorsítótár frissítését, de a törölt elemeket külön kell törölni az adatbázisból.
  • UnauthorizedStatusCode (intalapértelmezett: 401 Jogosulatlan) az az állapotkód, amelyet akkor ad vissza, ha a felhasználó nem hajthat végre műveletet.

Hozzáférési engedélyek konfigurálása

A felhasználók alapértelmezés szerint bármit megtehetnek, amit egy tábla entitásaihoz szeretnének – bármilyen rekordot létrehozhatnak, olvashatnak, frissíthetnek és törölhetnek. Az engedélyezés részletesebb szabályozásához hozzon létre egy implementált osztályt IAccessControlProvider. Az IAccessControlProvider engedélyezés végrehajtásához három módszer használható:

  • GetDataView() egy lambdát ad vissza, amely korlátozza, hogy a csatlakoztatott felhasználó mit lát.
  • IsAuthorizedAsync() meghatározza, hogy a csatlakoztatott felhasználó végrehajthatja-e a műveletet a kért entitáson.
  • PreCommitHookAsync() közvetlenül az adattárba való írás előtt módosítja az entitásokat.

A három módszer között hatékonyan kezelheti a legtöbb hozzáférés-vezérlési esetet. Ha hozzá kell férnie a HttpContexthttpContextAccessorhoz, konfiguráljon egy HttpContextAccessort.

A következők például egy személyes táblát implementálnak, amelyben a felhasználó csak a saját rekordjait láthatja.

public class PrivateAccessControlProvider<T>: IAccessControlProvider<T>
    where T : ITableData
    where T : IUserId
{
    private readonly IHttpContextAccessor _accessor;

    public PrivateAccessControlProvider(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    private string UserId { get => _accessor.HttpContext.User?.Identity?.Name; }

    public Expression<Func<T,bool>> GetDataView()
    {
      return (UserId == null)
        ? _ => false
        : model => model.UserId == UserId;
    }

    public Task<bool> IsAuthorizedAsync(TableOperation op, T entity, CancellationToken token = default)
    {
        if (op == TableOperation.Create || op == TableOperation.Query)
        {
            return Task.FromResult(true);
        }
        else
        {
            return Task.FromResult(entity?.UserId != null && entity?.UserId == UserId);
        }
    }

    public virtual Task PreCommitHookAsync(TableOperation operation, T entity, CancellationToken token = default)
    {
        entity.UserId == UserId;
        return Task.CompletedTask;
    }
}

A metódusok aszinkronok arra az esetre, ha további adatbázis-keresésre van szükség a helyes válasz eléréséhez. Implementálhatja a IAccessControlProvider<T> kezelőfelületet a vezérlőn, de továbbra is át kell adnia a IHttpContextAccessor szál biztonságos eléréséhez HttpContext .

A hozzáférés-vezérlési szolgáltató használatához frissítse a TableController következőket:

[Authorize]
[Route("tables/[controller]")]
public class ModelController : TableController<Model>
{
    public ModelsController(AppDbContext context, IHttpContextAccessor accessor) : base()
    {
        AccessControlProvider = new PrivateAccessControlProvider<Model>(accessor);
        Repository = new EntityTableRepository<Model>(context);
    }
}

Ha nem hitelesített és hitelesített hozzáférést is engedélyezni szeretne egy táblához, akkor ahelyett, hogy a táblázatot [AllowAnonymous] díszítenék [Authorize].

Naplózás konfigurálása

A naplózás a ASP.NET Core normál naplózási mechanizmusán keresztül történik. Rendelje hozzá az ILogger objektumot a Logger tulajdonsághoz:

[Authorize]
[Route("tables/[controller]")]
public class ModelController : TableController<Model>
{
    public ModelController(AppDbContext context, Ilogger<ModelController> logger) : base()
    {
        Repository = new EntityTableRepository<Model>(context);
        Logger = logger;
    }
}

Tárház változásainak figyelése

Az adattár módosításakor munkafolyamatokat indíthat el, naplózhatja a választ az ügyfélnek, vagy elvégezhet más műveleteket a két módszer egyikében:

1. lehetőség: PostCommitHookAsync implementálása

Az IAccessControlProvider<T> interfész egy metódust PostCommitHookAsync() biztosít. A metódus PostCommitHookAsync() meghívása azután történik, hogy az adatok az adattárba vannak írva, de mielőtt visszaküldené az adatokat az ügyfélnek. Ügyelni kell arra, hogy az ügyfélnek visszaadott adatok ne változhassanak ebben a módszerben.

public class MyAccessControlProvider<T> : AccessControlProvider<T> where T : ITableData
{
    public override async Task PostCommitHookAsync(TableOperation op, T entity, CancellationToken cancellationToken = default)
    {
        // Do any work you need to here.
        // Make sure you await any asynchronous operations.
    }
}

Ezt a lehetőséget akkor használja, ha a horog részeként aszinkron feladatokat futtat.

2. lehetőség: A RepositoryUpdated eseménykezelő használata

Az TableController<T> alaposztály tartalmaz egy eseménykezelőt, amelyet a metódus használatával egyidejűleg hív meg.PostCommitHookAsync()

[Authorize]
[Route(tables/[controller])]
public class ModelController : TableController<Model>
{
    public ModelController(AppDbContext context) : base()
    {
        Repository = new EntityTableRepository<Model>(context);
        RepositoryUpdated += OnRepositoryUpdated;
    }

    internal void OnRepositoryUpdated(object sender, RepositoryUpdatedEventArgs e) 
    {
        // The RepositoryUpdatedEventArgs contains Operation, Entity, EntityName
    }
}

Azure-alkalmazás szolgáltatás identitásának engedélyezése

A ASP.NET Core adatszinkronizálási kiszolgáló támogatja ASP.NET Core Identity-t, vagy bármely más, támogatni kívánt hitelesítési és engedélyezési sémát. Az Azure Mobile Apps korábbi verzióinak frissítéséhez egy identitásszolgáltatót is biztosítunk, amely implementálja Azure-alkalmazás Service Identity szolgáltatást. Ha Azure-alkalmazás szolgáltatásadentitást szeretné konfigurálni az alkalmazásban, szerkessze a következőtProgram.cs:

builder.Services.AddAuthentication(AzureAppServiceAuthentication.AuthenticationScheme)
  .AddAzureAppServiceAuthentication(options => options.ForceEnable = true);

// Then later, after you have created the app
app.UseAuthentication();
app.UseAuthorization();

Adatbázis-támogatás

Az Entity Framework Core nem állít be értékgenerálást a dátum/idő oszlopokhoz. (Lásd: Dátum/idő érték létrehozása). Az Entity Framework Core Azure Mobile Apps-adattára automatikusan frissíti a UpdatedAt mezőt. Ha azonban az adatbázis az adattáron kívül van frissítve, gondoskodnia kell a mezők és Version mezők frissítésérőlUpdatedAt.

Azure SQL

Hozzon létre egy eseményindítót az egyes entitásokhoz:

CREATE OR ALTER TRIGGER [dbo].[TodoItems_UpdatedAt] ON [dbo].[TodoItems]
    AFTER INSERT, UPDATE
AS
BEGIN
    SET NOCOUNT ON;
    UPDATE 
        [dbo].[TodoItems] 
    SET 
        [UpdatedAt] = GETUTCDATE() 
    WHERE 
        [Id] IN (SELECT [Id] FROM INSERTED);
END

Ezt az eseményindítót migrálással vagy közvetlenül az adatbázis létrehozása után EnsureCreated() telepítheti.

Azure Cosmos DB

Az Azure Cosmos DB egy teljes körűen felügyelt NoSQL-adatbázis bármilyen méretű vagy méretű nagy teljesítményű alkalmazásokhoz. Az Azure Cosmos DB és az Entity Framework Core használatával kapcsolatos információkért tekintse meg az Azure Cosmos DB-szolgáltatót . Az Azure Cosmos DB és az Azure Mobile Apps használatakor:

  1. Állítsa be a Cosmos-tárolót egy összetett indexkel, amely meghatározza a mezőket és Id a UpdatedAt mezőket. Az összetett indexek hozzáadhatók egy tárolóhoz az Azure Portalon, az ARM-en, a Bicep-en, a Terraformon vagy a kódon belül. Íme egy példa bicep-erőforrásdefinícióra :

    resource cosmosContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = {
        name: 'TodoItems'
        parent: cosmosDatabase
        properties: {
            resource: {
                id: 'TodoItems'
                partitionKey: {
                    paths: [
                        '/Id'
                    ]
                    kind: 'Hash'
                }
                indexingPolicy: {
                    indexingMode: 'consistent'
                    automatic: true
                    includedPaths: [
                        {
                            path: '/*'
                        }
                    ]
                    excludedPaths: [
                        {
                            path: '/"_etag"/?'
                        }
                    ]
                    compositeIndexes: [
                        [
                            {
                                path: '/UpdatedAt'
                                order: 'ascending'
                            }
                            {
                                path: '/Id'
                                order: 'ascending'
                            }
                        ]
                    ]
                }
            }
        }
    }
    

    Ha lekéri a tábla elemeinek egy részét, győződjön meg arról, hogy megadja a lekérdezésben érintett összes tulajdonságot.

  2. Modellek származtatása az ETagEntityTableData osztályból:

    public class TodoItem : ETagEntityTableData
    {
        public string Title { get; set; }
        public bool Completed { get; set; }
    }
    
  3. Adjon hozzá egy metódust OnModelCreating(ModelBuilder) a DbContext. Az Entity Framework Cosmos DB-illesztője alapértelmezés szerint ugyanabba a tárolóba helyezi az összes entitást. Legalább ki kell választania egy megfelelő partíciókulcsot, és meg kell győződnie arról, hogy a EntityTag tulajdonság egyidejűségi címkeként van megjelölve. Az alábbi kódrészlet például a saját tárolójában tárolja az TodoItem entitásokat az Azure Mobile Apps megfelelő beállításaival:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<TodoItem>(builder =>
        {
            // Store this model in a specific container.
            builder.ToContainer("TodoItems");
            // Do not include a discriminator for the model in the partition key.
            builder.HasNoDiscriminator();
            // Set the partition key to the Id of the record.
            builder.HasPartitionKey(model => model.Id);
            // Set the concurrency tag to the EntityTag property.
            builder.Property(model => model.EntityTag).IsETagConcurrency();
        });
        base.OnModelCreating(builder);
    }
    

Az Azure Cosmos DB a NuGet-csomagban támogatott az Microsoft.AspNetCore.Datasync.EFCore 5.0.11-ös verzió óta. További információkért tekintse át az alábbi hivatkozásokat:

PostgreSQL

Hozzon létre egy eseményindítót az egyes entitásokhoz:

CREATE OR REPLACE FUNCTION todoitems_datasync() RETURNS trigger AS $$
BEGIN
    NEW."UpdatedAt" = NOW() AT TIME ZONE 'UTC';
    NEW."Version" = convert_to(gen_random_uuid()::text, 'UTF8');
    RETURN NEW
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE TRIGGER
    todoitems_datasync
BEFORE INSERT OR UPDATE ON
    "TodoItems"
FOR EACH ROW EXECUTE PROCEDURE
    todoitems_datasync();

Ezt az eseményindítót migrálással vagy közvetlenül az adatbázis létrehozása után EnsureCreated() telepítheti.

Sqlite

Figyelmeztetés

Ne használja a SqLite-t éles szolgáltatásokhoz. A SqLite csak ügyféloldali használatra alkalmas éles környezetben.

A SqLite nem rendelkezik olyan dátum/idő mezővel, amely támogatja az ezredmásodperc pontosságát. Így a tesztelésen kívül semmire sem alkalmas. Ha SqLite-t szeretne használni, győződjön meg arról, hogy minden modellen implementál egy értékkonvertert és érték-összehasonlítót a dátum/idő tulajdonságokhoz. Az értékkonverterek és összehasonlítók implementálásának legegyszerűbb módszere DbContexta OnModelCreating(ModelBuilder) következő módszer:

protected override void OnModelCreating(ModelBuilder builder)
{
    var timestampProps = builder.Model.GetEntityTypes().SelectMany(t => t.GetProperties())
        .Where(p => p.ClrType == typeof(byte[]) && p.ValueGenerated == ValueGenerated.OnAddOrUpdate);
    var converter = new ValueConverter<byte[], string>(
        v => Encoding.UTF8.GetString(v),
        v => Encoding.UTF8.GetBytes(v)
    );
    foreach (var property in timestampProps)
    {
        property.SetValueConverter(converter);
        property.SetDefaultValueSql("STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')");
    }
    base.OnModelCreating(builder);
}

Telepítsen egy frissítési eseményindítót az adatbázis inicializálásakor:

internal static void InstallUpdateTriggers(DbContext context)
{
    foreach (var table in context.Model.GetEntityTypes())
    {
        var props = table.GetProperties().Where(prop => prop.ClrType == typeof(byte[]) && prop.ValueGenerated == ValueGenerated.OnAddOrUpdate);
        foreach (var property in props)
        {
            var sql = $@"
                CREATE TRIGGER s_{table.GetTableName()}_{prop.Name}_UPDATE AFTER UPDATE ON {table.GetTableName()}
                BEGIN
                    UPDATE {table.GetTableName()}
                    SET {prop.Name} = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')
                    WHERE rowid = NEW.rowid;
                END
            ";
            context.Database.ExecuteSqlRaw(sql);
        }
    }
}

Győződjön meg arról, hogy a InstallUpdateTriggers metódus csak egyszer van meghívva az adatbázis inicializálása során:

public void InitializeDatabase(DbContext context)
{
    bool created = context.Database.EnsureCreated();
    if (created && context.Database.IsSqlite())
    {
        InstallUpdateTriggers(context);
    }
    context.Database.SaveChanges();
}

LiteDB

A LiteDB egy kiszolgáló nélküli adatbázis, amely egyetlen kis DLL-ben, .NET C# felügyelt kódban van megírva. Ez egy egyszerű és gyors NoSQL-adatbázismegoldás önálló alkalmazásokhoz. A LiteDb használata lemezen tárolt állandó tárterülettel:

  1. Telepítse a csomagot a Microsoft.AspNetCore.Datasync.LiteDb NuGetből.

  2. Adjon hozzá egy egytonnát a LiteDatabaseProgram.cskövetkezőhöz:

    const connectionString = builder.Configuration.GetValue<string>("LiteDb:ConnectionString");
    builder.Services.AddSingleton<LiteDatabase>(new LiteDatabase(connectionString));
    
  3. Modellek származtatása a LiteDbTableDatakövetkezőből:

    public class TodoItem : LiteDbTableData
    {
        public string Title { get; set; }
        public bool Completed { get; set; }
    }
    

    A LiteDb NuGet-csomaghoz kapott attribútumok bármelyikét BsonMapper használhatja.

  4. Vezérlő létrehozása a LiteDbRepositorykövetkező használatával:

    [Route("tables/[controller]")]
    public class TodoItemController : TableController<TodoItem>
    {
        public TodoItemController(LiteDatabase db) : base()
        {
            Repository = new LiteDbRepository<TodoItem>(db, "todoitems");
        }
    }
    

OpenAPI-támogatás

Az adatszinkronizálási vezérlők által definiált API-t az NSwag vagy a Swashbuckle használatával teheti közzé. Mindkét esetben először állítsa be a szolgáltatást, ahogyan a választott tár esetében általában tenné.

NSwag

Kövesse az NSwag-integráció alapvető utasításait, majd módosítsa az alábbiak szerint:

  1. Vegyen fel csomagokat a projektbe az NSwag támogatásához. A következő csomagokra van szükség:

  2. Adja hozzá a következőket a fájl elejéhez Program.cs :

    using Microsoft.AspNetCore.Datasync.NSwag;
    
  3. Adjon hozzá egy szolgáltatást egy OpenAPI-definíció létrehozásához a Program.cs fájlhoz:

    builder.Services.AddOpenApiDocument(options =>
    {
        options.AddDatasyncProcessors();
    });
    
  4. Engedélyezze a köztes szoftvert a létrehozott JSON-dokumentum és a Swagger felhasználói felület kiszolgálásához a következőben Program.csis:

    if (app.Environment.IsDevelopment())
    {
        app.UseOpenApi();
        app.UseSwaggerUI3();
    }
    

A webszolgáltatás végpontjának böngészésével /swagger tallózhat az API-val. Az OpenAPI-definíció ezután importálható más szolgáltatásokba (például az Azure API Managementbe). Az NSwag konfigurálásáról további információt az NSwag és a ASP.NET Core használatbavétele című témakörben talál.

Swashbuckle

Kövesse a Swashbuckle-integráció alapvető utasításait, majd módosítsa az alábbiak szerint:

  1. Csomagok hozzáadása a projekthez a Swashbuckle támogatásához. A következő csomagokra van szükség:

  2. Adjon hozzá egy szolgáltatást egy OpenAPI-definíció létrehozásához a Program.cs fájlhoz:

    builder.Services.AddSwaggerGen(options => 
    {
        options.AddDatasyncControllers();
    });
    builder.Services.AddSwaggerGenNewtonsoftSupport();
    

    Megjegyzés:

    A AddDatasyncControllers() metódus a táblavezérlőket tartalmazó szerelvénynek megfelelő opcionális Assembly megoldást használ. A Assembly paraméter csak akkor szükséges, ha a táblavezérlők a szolgáltatástól eltérő projektben vannak.

  3. Engedélyezze a köztes szoftvert a létrehozott JSON-dokumentum és a Swagger felhasználói felület kiszolgálásához a következőben Program.csis:

    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI(options => 
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
            options.RoutePrefix = string.Empty;
        });
    }
    

Ezzel a konfigurációval a webszolgáltatás gyökerének böngészésével tallózhat az API-k között. Az OpenAPI-definíció ezután importálható más szolgáltatásokba (például az Azure API Managementbe). A Swashbuckle konfigurálásáról további információt a Swashbuckle és a ASP.NET Core használatának első lépéseiben talál.

Korlátozások

A szolgáltatáskódtárak ASP.NET Core kiadása az OData v4-et implementálja a listaművelethez. Ha a kiszolgáló visszamenőleges kompatibilitási módban fut, az alszűrések szűrése nem támogatott.