dotnet / dotnet/aspnetcore

DataProtection.PersistKeysToDbContext fails when using Cosmos DB EF 9. Possible fix included here.

Open
#59,717 9 comments 1 reaction 0 assignees View on GitHub
area-dataprotection
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Describe the bug

Cosmos DB Entity Framework for .Net 9 now requires all LINQ functions to be asynchronous. When attempting to use the synchronous method, it throws the following error:

`'Microsoft.EntityFrameworkCore.Database.SyncNotSupported': Azure Cosmos DB does not support synchronous I/O. Make sure to use and correctly await only async methods when using Entity Framework Core to access Azure Cosmos DB.`

For example, Entity.ToList() will fail while Entity.ToListAsync() will succeed.

I have found that the following code changes to `EntityFrameworkCoreXmlRepository` will solve the problem. Note where I check if DB is Cosmos, and there make async calls. Someone more cleaver than I can probably simplify this.

Question, can all EF providers support asynchronous methods? If so, then maybe make this async for all and not worry if this is for Cosmos or not?

```csharp
public virtual IReadOnlyCollection GetAllElements()
{
// forces complete enumeration
return GetAllElementsCore().ToList().AsReadOnly();

IEnumerable GetAllElementsCore()
{
using (var scope = _services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService();

List keys;

// Cosmos DB EF 9 supports only async methods.
if (context.Database.IsCosmos())
{
keys = context.DataProtectionKeys.AsNoTracking().ToListAsync().Result;
}
else
{
keys = context.DataProtectionKeys.AsNoTracking();
}

foreach (var key in keys)
{
_logger.ReadingXmlFromKey(key.FriendlyName!, key.Xml);

if (!string.IsNullOrEmpty(key.Xml))
{
yield return XElement.Parse(key.Xml);
}
}
}
}
}

///
public void StoreElement(XElement element, string friendlyName)
{
using (var scope = _services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService();
var newKey = new DataProtectionKey()
{
FriendlyName = friendlyName,
Xml = element.ToString(SaveOptions.DisableFormatting)
};

context.DataProtectionKeys.Add(newKey);
_logger.LogSavingKeyToDbContext(friendlyName, typeof(TContext).Name);

// Cosmos DB EF 9 supports only async methods.
if (context.Database.IsCosmos())
{
context.SaveChangesAsync().Wait();
}
else
{
context.SaveChanges();
}
}
}
```

### Expected Behavior

_No response_

### Steps To Reproduce

Create an asp.net project, .Net 9, with a Cosmos DB database. Then add data protection using the DbContext. Here is example code that will cause the error:

```csharp
// Add shared data protection here
builder.Services.AddDataProtection()
.SetApplicationName("editor").UseCryptographicAlgorithms(
new AuthenticatedEncryptorConfiguration
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
}).PersistKeysToDbContext();
```

Note: The `ApplicationDbContext` uses Cosmos DB.

### Exceptions (if any)

_No response_

### .NET Version

_No response_

### Anything else?

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.