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:
- Hozzon létre egy ASP.NET 6.0-s (vagy újabb) kiszolgálóprojektet.
- Entity Framework Core hozzáadása
- 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:
- Microsoft.AspNetCore.Datasync
- Microsoft.AspNetCore.Datasync.EFCore entity Framework Core-alapú táblákhoz.
- Microsoft.AspNetCore.Datasync.InMemory memóriabeli táblákhoz.
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:
- Hozzon létre egy modellosztályt az adatmodellhez.
- Adja hozzá a modellosztályt az
DbContext
alkalmazáshoz. - Hozzon létre egy új
TableController<T>
osztályt a modell felfedéséhez.
Modellosztály létrehozása
Minden modellosztálynak implementálnia ITableData
kell. 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
/tables
jelennek 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/tables
kell lennie. - A vezérlőnek a következőtől
TableController<T>
kell örökölnie: az<T>
adattártípus implementációjánakITableData
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 TableControllerOptions
konfigurá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
(int
alapértelmezett: 100) az egy lapon visszaadott lekérdezési művelet által visszaadott elemek maximális száma.MaxTop
(int
alapértelmezett: 512000) a lekérdezési műveletben lapozás nélkül visszaadott elemek maximális száma.EnableSoftDelete
(bool
alapé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
(int
alapé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 HttpContext
httpContextAccessorhoz, 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:
Állítsa be a Cosmos-tárolót egy összetett indexkel, amely meghatározza a mezőket és
Id
aUpdatedAt
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.
Modellek származtatása az
ETagEntityTableData
osztályból:public class TodoItem : ETagEntityTableData { public string Title { get; set; } public bool Completed { get; set; } }
Adjon hozzá egy metódust
OnModelCreating(ModelBuilder)
aDbContext
. 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 aEntityTag
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 azTodoItem
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:
- Cosmos DB-minta.
- Az EF Core Azure Cosmos DB Provider dokumentációja.
- A Cosmos DB indexszabályzatának dokumentációja.
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 DbContext
a 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:
Telepítse a csomagot a
Microsoft.AspNetCore.Datasync.LiteDb
NuGetből.Adjon hozzá egy egytonnát a
LiteDatabase
Program.cs
következőhöz:const connectionString = builder.Configuration.GetValue<string>("LiteDb:ConnectionString"); builder.Services.AddSingleton<LiteDatabase>(new LiteDatabase(connectionString));
Modellek származtatása a
LiteDbTableData
kö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.Vezérlő létrehozása a
LiteDbRepository
kö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:
Vegyen fel csomagokat a projektbe az NSwag támogatásához. A következő csomagokra van szükség:
Adja hozzá a következőket a fájl elejéhez
Program.cs
:using Microsoft.AspNetCore.Datasync.NSwag;
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(); });
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.cs
is: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:
Csomagok hozzáadása a projekthez a Swashbuckle támogatásához. A következő csomagokra van szükség:
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álisAssembly
megoldást használ. AAssembly
paraméter csak akkor szükséges, ha a táblavezérlők a szolgáltatástól eltérő projektben vannak.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.cs
is: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.
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: