Jak používat sadu SDK back-endového serveru ASP.NET Core
Poznámka
Tento produkt je vyřazený. Náhradu za projekty používající .NET 8 nebo novější najdete v knihovně Community Toolkit Datasync.
Tento článek ukazuje, že musíte nakonfigurovat a použít sadu SDK back-endového serveru ASP.NET Core k vytvoření serveru synchronizace dat.
Podporované platformy
Back-endový server ASP.NET Core podporuje ASP.NET 6.0 nebo novější.
Databázové servery musí splňovat následující kritéria DateTime
nebo pole typu Timestamp
, které je uloženo s přesností milisekund. Implementace úložiště jsou poskytovány pro Entity Framework Core a LiteDb.
Konkrétní podporu databází najdete v následujících částech:
Vytvoření nového serveru synchronizace dat
Server synchronizace dat používá pro vytvoření serveru normální mechanismy ASP.NET Core. Skládá se ze tří kroků:
- Vytvořte projekt serveru ASP.NET 6.0 (nebo novější).
- Přidání Entity Framework Core
- Přidání služeb synchronizace dat
Informace o vytvoření služby ASP.NET Core pomocí Entity Framework Core najdete v kurzu.
Pokud chcete povolit služby synchronizace dat, musíte přidat následující knihovny NuGet:
- Microsoft.AspNetCore.Datasync
- tabulky založené na Entity Framework Core Microsoft.AspNetCore.Datasync.EFCore.
- Microsoft.AspNetCore.Datasync.InMemory pro tabulky v paměti.
Upravte soubor Program.cs
. Do všech ostatních definic služby přidejte následující řádek:
builder.Services.AddDatasyncControllers();
Můžete také použít šablonu ASP.NET Core datasync-server
:
# 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
Šablona obsahuje ukázkový model a kontroler.
Vytvoření kontroleru tabulky pro tabulku SQL
Výchozí úložiště používá Entity Framework Core. Vytvoření kontroleru tabulky je třístupňový proces:
- Vytvořte třídu modelu pro datový model.
- Přidejte třídu modelu do
DbContext
pro vaši aplikaci. - Vytvořte novou třídu
TableController<T>
pro zveřejnění modelu.
Vytvoření třídy modelu
Všechny třídy modelu musí implementovat ITableData
. Každý typ úložiště má abstraktní třídu, která implementuje ITableData
. Úložiště Entity Framework Core používá 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; }
}
Rozhraní ITableData
poskytuje ID záznamu spolu s dalšími vlastnostmi pro zpracování služeb synchronizace dat:
-
UpdatedAt
(DateTimeOffset?
) poskytuje datum poslední aktualizace záznamu. -
Version
(byte[]
) poskytuje neprůsažnou hodnotu, která se změní při každém zápisu. -
Deleted
(bool
) je true, pokud je záznam označen k odstranění, ale ještě není vymazaný.
Knihovna synchronizace dat tyto vlastnosti udržuje. Neupravujte tyto vlastnosti ve vlastním kódu.
Aktualizace DbContext
Každý model v databázi musí být registrován v DbContext
. Například:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; }
}
Vytvoření kontroleru tabulky
Kontroler tabulky je specializovaný ApiController
. Tady je minimální kontroler tabulky:
[Route("tables/[controller]")]
public class TodoItemController : TableController<TodoItem>
{
public TodoItemController(AppDbContext context) : base()
{
Repository = new EntityTableRepository<TodoItem>(context);
}
}
Poznámka
- Kontroler musí mít trasu. Podle konvence jsou tabulky vystaveny na dílčí cestě
/tables
, ale mohou být umístěny kdekoli. Pokud používáte klientské knihovny starší než verze 5.0.0, musí být tabulka dílčí cestou/tables
. - Kontroler musí dědit z
TableController<T>
, kde<T>
je implementaceITableData
implementace pro váš typ úložiště. - Přiřaďte úložiště na základě stejného typu jako váš model.
Implementace úložiště v paměti
Můžete také použít úložiště v paměti bez trvalého úložiště. Do Program.cs
přidejte službu singleton pro úložiště:
IEnumerable<Model> seedData = GenerateSeedData();
builder.Services.AddSingleton<IRepository<Model>>(new InMemoryRepository<Model>(seedData));
Nastavte kontroler tabulky následujícím způsobem:
[Route("tables/[controller]")]
public class ModelController : TableController<Model>
{
public MovieController(IRepository<Model> repository) : base(repository)
{
}
}
Konfigurace možností kontroleru tabulky
Pomocí TableControllerOptions
můžete nakonfigurovat určité aspekty kontroleru:
[Route("tables/[controller]")]
public class MoodelController : TableController<Model>
{
public ModelController(IRepository<Model> repository) : base(repository)
{
Options = new TableControllerOptions { PageSize = 25 };
}
}
Mezi možnosti, které můžete nastavit, patří:
-
PageSize
(int
, výchozí: 100) je maximální počet položek, které operace dotazu vrátí na jedné stránce. -
MaxTop
(int
, výchozí hodnota: 512000) je maximální počet položek vrácených v operaci dotazu bez stránkování. -
EnableSoftDelete
(bool
, výchozí hodnota: false) umožňuje obnovitelné odstranění, které místo odstranění z databáze označí položky jako odstraněné. Obnovitelné odstranění umožňuje klientům aktualizovat offline mezipaměť, ale vyžaduje, aby se odstraněné položky vyprázdnily z databáze samostatně. -
UnauthorizedStatusCode
(int
, výchozí: 401 Neautorizováno) je stavový kód vrácený, když uživatel nemůže provést akci.
Konfigurace přístupových oprávnění
Ve výchozím nastavení může uživatel dělat cokoliv, co chce s entitami v tabulce – vytvářet, číst, aktualizovat a odstraňovat libovolný záznam. Pro podrobnější kontrolu nad autorizací vytvořte třídu, která implementuje IAccessControlProvider
.
IAccessControlProvider
používá k implementaci autorizace tři metody:
-
GetDataView()
vrátí lambda, která omezuje, co uvidí připojený uživatel. -
IsAuthorizedAsync()
určuje, jestli může připojený uživatel provést akci u konkrétní požadované entity. -
PreCommitHookAsync()
před zápisem do úložiště upraví libovolnou entitu.
Mezi těmito třemi metodami můžete efektivně zvládnout většinu případů řízení přístupu. Pokud potřebujete přístup k HttpContext
, nakonfigurovatHttpContextAccessor .
Například následující tabulka implementuje osobní tabulku, kde uživatel vidí jenom svoje vlastní záznamy.
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;
}
}
Metody jsou asynchronní v případě, že potřebujete provést další vyhledávání databáze, abyste získali správnou odpověď. Rozhraní IAccessControlProvider<T>
můžete implementovat na řadiči, ale přesto musíte předat IHttpContextAccessor
pro přístup k HttpContext
bezpečným způsobem.
Pokud chcete použít tohoto zprostředkovatele řízení přístupu, aktualizujte TableController
následujícím způsobem:
[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);
}
}
Pokud chcete povolit neověřený i ověřený přístup k tabulce, ozdobte ho [AllowAnonymous]
místo [Authorize]
.
Konfigurace protokolování
Protokolování se zpracovává prostřednictvím normálního mechanismu protokolování pro ASP.NET Core. Přiřaďte objekt ILogger
vlastnosti Logger
:
[Authorize]
[Route("tables/[controller]")]
public class ModelController : TableController<Model>
{
public ModelController(AppDbContext context, Ilogger<ModelController> logger) : base()
{
Repository = new EntityTableRepository<Model>(context);
Logger = logger;
}
}
Monitorování změn úložiště
Když se úložiště změní, můžete aktivovat pracovní postupy, protokolovat odpověď klientovi nebo provádět jinou práci v jedné ze dvou metod:
Možnost 1: Implementace postCommitHookAsync
Rozhraní IAccessControlProvider<T>
poskytuje metodu PostCommitHookAsync()
. Metoda PostCommitHookAsync()
se volá po zápisu dat do úložiště, ale před vrácením dat klientovi. Je třeba dbát na to, aby se v této metodě nezměnila data vrácená klientovi.
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.
}
}
Tuto možnost použijte, pokud spouštíte asynchronní úlohy jako součást háku.
Možnost 2: Použití obslužné rutiny události RepositoryUpdated
Základní třída TableController<T>
obsahuje obslužnou rutinu události, která je volána ve stejnou dobu jako PostCommitHookAsync()
metoda.
[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
}
}
Povolení identity služby Azure App Service
Server pro synchronizaci dat ASP.NET Core podporujeProgram.cs
:
builder.Services.AddAuthentication(AzureAppServiceAuthentication.AuthenticationScheme)
.AddAzureAppServiceAuthentication(options => options.ForceEnable = true);
// Then later, after you have created the app
app.UseAuthentication();
app.UseAuthorization();
Podpora databáze
Entity Framework Core nenastavuje generování hodnot pro sloupce s datem a časem. (Viz generování hodnot data a času). Úložiště Azure Mobile Apps pro Entity Framework Core automaticky aktualizuje UpdatedAt
pole za vás. Pokud se ale vaše databáze aktualizuje mimo úložiště, musíte zajistit aktualizaci UpdatedAt
a Version
polí.
Azure SQL
Vytvořte trigger pro každou entitu:
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
Tento trigger můžete nainstalovat buď pomocí migrace, nebo bezprostředně po EnsureCreated()
k vytvoření databáze.
Azure Cosmos DB
Azure Cosmos DB je plně spravovaná databáze NoSQL pro vysoce výkonné aplikace libovolné velikosti nebo škálování. Informace o používání služby Azure Cosmos DB s Entity Framework Core najdete v tématu poskytovatel služby Azure Cosmos DB. Při používání služby Azure Cosmos DB s Azure Mobile Apps:
Nastavte kontejner Cosmos s složeným indexem, který určuje pole
UpdatedAt
aId
. Složené indexy je možné přidat do kontejneru prostřednictvím webu Azure Portal, ARM, Bicep, Terraformu nebo kódu. Tady je příklad definice bicep prostředku: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' } ] ] } } } }
Pokud stáhnete podmnožinu položek v tabulce, ujistěte se, že jste zadali všechny vlastnosti zahrnuté v dotazu.
Odvozujte modely z třídy
ETagEntityTableData
:public class TodoItem : ETagEntityTableData { public string Title { get; set; } public bool Completed { get; set; } }
Přidejte do
DbContext
metoduOnModelCreating(ModelBuilder)
. Ovladač cosmos DB pro Entity Framework umístí všechny entity do stejného kontejneru ve výchozím nastavení. Minimálně musíte vybrat vhodný klíč oddílu a zajistit, aby byla vlastnostEntityTag
označena jako značka souběžnosti. Následující fragment kódu například ukládá entityTodoItem
do vlastního kontejneru s odpovídajícím nastavením pro Azure Mobile Apps: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); }
Služba Azure Cosmos DB je podporována v balíčku NuGet Microsoft.AspNetCore.Datasync.EFCore
od verze 5.0.11. Další informace najdete na následujících odkazech:
- ukázkovécosmos DB .
- dokumentaci k poskytovateli Azure Cosmos DB EF Core.
- dokumentaci k zásadám indexu služby Cosmos DB.
PostgreSQL
Vytvořte trigger pro každou entitu:
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();
Tento trigger můžete nainstalovat buď pomocí migrace, nebo bezprostředně po EnsureCreated()
k vytvoření databáze.
SqLite
Varování
Nepoužívejte SqLite pro produkční služby. SqLite je vhodný pouze pro použití na straně klienta v produkčním prostředí.
SqLite nemá pole data a času, které podporuje přesnost milisekund. Proto není vhodný pro nic kromě testování. Pokud chcete použít SqLite, ujistěte se, že v každém modelu implementujete převaděč hodnot a porovnávač hodnot pro vlastnosti data a času. Nejjednodušší metoda implementace převaděčů hodnot a porovnávačů je v OnModelCreating(ModelBuilder)
metodě vašeho DbContext
:
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);
}
Nainstalujte trigger aktualizace při inicializaci databáze:
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);
}
}
}
Ujistěte se, že se metoda InstallUpdateTriggers
volá pouze jednou během inicializace databáze:
public void InitializeDatabase(DbContext context)
{
bool created = context.Database.EnsureCreated();
if (created && context.Database.IsSqlite())
{
InstallUpdateTriggers(context);
}
context.Database.SaveChanges();
}
LiteDB
LiteDB je bezserverová databáze doručená v jedné malé knihovně DLL napsané ve spravovaném kódu .NET C#. Jedná se o jednoduché a rychlé databázové řešení NoSQL pro samostatné aplikace. Použití LiteDb s trvalým úložištěm na disku:
Nainstalujte balíček
Microsoft.AspNetCore.Datasync.LiteDb
z NuGetu.Do
Program.cs
přidejte jedenton proLiteDatabase
:const connectionString = builder.Configuration.GetValue<string>("LiteDb:ConnectionString"); builder.Services.AddSingleton<LiteDatabase>(new LiteDatabase(connectionString));
Odvozujte modely z
LiteDbTableData
:public class TodoItem : LiteDbTableData { public string Title { get; set; } public bool Completed { get; set; } }
Můžete použít libovolný z
BsonMapper
atributů, které jsou dodávány s balíčkem NuGet LiteDb.Vytvořte kontroler pomocí
LiteDbRepository
:[Route("tables/[controller]")] public class TodoItemController : TableController<TodoItem> { public TodoItemController(LiteDatabase db) : base() { Repository = new LiteDbRepository<TodoItem>(db, "todoitems"); } }
Podpora OpenAPI
Rozhraní API definované kontrolery synchronizace dat můžete publikovat pomocí
NSwag
Postupujte podle základníchpokynůch
Přidejte do projektu balíčky pro podporu NSwag. Jsou vyžadovány následující balíčky:
Na začátek souboru
Program.cs
přidejte následující:using Microsoft.AspNetCore.Datasync.NSwag;
Přidejte službu pro vygenerování definice OpenAPI do souboru
Program.cs
:builder.Services.AddOpenApiDocument(options => { options.AddDatasyncProcessors(); });
Povolte middleware pro obsluhu vygenerovaného dokumentu JSON a uživatelského rozhraní Swagger, a to také v
Program.cs
:if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUI3(); }
Procházením koncového bodu /swagger
webové služby můžete procházet rozhraní API. Definici OpenAPI je pak možné importovat do jiných služeb (jako je Azure API Management). Další informace o konfiguraci NSwag najdete v tématu Začínáme s NSwag a ASP.NET Core.
Swashbuckle
Postupujte podle základních pokynů pro integraci Swashbuckle a pak upravte následujícím způsobem:
Přidejte do projektu balíčky pro podporu Swashbuckle. Jsou vyžadovány následující balíčky:
Přidejte službu pro vygenerování definice OpenAPI do souboru
Program.cs
:builder.Services.AddSwaggerGen(options => { options.AddDatasyncControllers(); }); builder.Services.AddSwaggerGenNewtonsoftSupport();
Poznámka
Metoda
AddDatasyncControllers()
přebírá volitelnouAssembly
, která odpovídá sestavení, které obsahuje kontrolery tabulky. ParametrAssembly
se vyžaduje jenom v případě, že jsou kontrolery tabulek v jiném projektu než služba.Povolte middleware pro obsluhu vygenerovaného dokumentu JSON a uživatelského rozhraní Swagger, a to také v
Program.cs
:if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); options.RoutePrefix = string.Empty; }); }
Při této konfiguraci vám procházení kořenového adresáře webové služby umožňuje procházet rozhraní API. Definici OpenAPI je pak možné importovat do jiných služeb (jako je Azure API Management). Další informace o konfiguraci Swashbuckle najdete v tématu Začínáme s Swashbuckle a ASP.NET Core.
Omezení
Edice ASP.NET Core knihoven služeb implementuje pro operaci seznamu OData v4. Pokud server běží v režimu zpětné kompatibility, filtrování podřetězce se nepodporuje.