ASP.NET 5 OAuthBearerAuthentication: The following authentication scheme was not accepted: Bearer - jwt

Updated:
Pinpoint helped me get this prototype off the launch pad - I was very close except for:
I need to upgrade to the beta6 SDK as per these instructions. Global.json now appears as follows:
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-beta6"
}
}
I updated references in project.json:
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-beta6",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta6",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta6",
"Microsoft.AspNet.StaticFiles": "1.0.0-beta6",
"System.IdentityModel.Tokens": "5.0.0-beta6-207211625",
"Serilog.Framework.Logging": "1.0.0-beta-43",
"Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta6"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --config hosting.ini"
},
"frameworks": {
"dnx451": { }
},
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"publishExclude": [
"node_modules",
"bower_components",
"**.xproj",
"**.user",
"**.vspscc"
]
}
The middleware order in startup's Configure method matters. UseOAuthBearerAuthentication needs to come before UseMvc. The Configure method in Startup.cs now appears as follows:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseOAuthBearerAuthentication();
app.UseMvc();
}
I'm working with ASP.NET 5 and trying to implement an extremely simple proof of concept for generating and consuming JWT tokens. I have read the articles here, here and here but this one most closely matches my needs.
To this end, I very carefully read the article, re-read it, internalized all the comments and then stood up a simple example. I can now generate a JWT token, but when I attempt to call my controller action which has been decorated with an authorize attribute [Authorize("Bearer")], I receive the following message:
The following authentication scheme was not accepted: Bearer
As I have not seen a high-fidelity A-to-Z example on how to do this, consider the following steps to reproduce:
Create a new Web API Project in Visual Studio 2015 (I am using Enterprise) by selecting "New Project...Web...ASP.NET Web Application" and then the "Web API" option under the "ASP.NET 5 Preview Templates"
Using the beta 5 SDK, global.json looks like this:
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-beta5",
"runtime": "clr",
"architecture": "x86"
}
}
Bringing in the dependencies necessary for JWT tokens, project.json looks like this:
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-beta6",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta6",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta6",
"System.IdentityModel.Tokens": "5.0.0-beta5-206011020",
"Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta5"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --config hosting.ini"
},
"frameworks": {
"dnx451": { }
},
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"publishExclude": [
"node_modules",
"bower_components",
"**.xproj",
"**.user",
"**.vspscc"
]
}
Startup.cs (this is an example not intended for production)
public class Startup
{
const string _TokenIssuer = "contoso.com" ;
const string _TokenAudience = "contoso.com/resources" ;
RsaSecurityKey _key = null ;
SigningCredentials _signingCredentials = null ;
public Startup(IHostingEnvironment env)
{
GenerateRsaKeys();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddInstance(_signingCredentials);
services.ConfigureOAuthBearerAuthentication
(
options =>
{
options.AutomaticAuthentication = true;
options.TokenValidationParameters.IssuerSigningKey = _key ;
options.TokenValidationParameters.ValidAudience = _TokenAudience;
options.TokenValidationParameters.ValidIssuer = _TokenIssuer ;
}
);
services.ConfigureAuthorization
(
options =>
{
options.
AddPolicy
(
"Bearer",
new AuthorizationPolicyBuilder().
AddAuthenticationSchemes(OAuthBearerAuthenticationDefaults.AuthenticationScheme).
RequireAuthenticatedUser().
Build()
);
}
);
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerfactory)
{
app.UseMvc();
app.UseOAuthBearerAuthentication();
}
void GenerateRsaKeys()
{
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
{
_key = new RsaSecurityKey(rsa.ExportParameters(true));
_signingCredentials =
new SigningCredentials
(
_key ,
SecurityAlgorithms.RsaSha256Signature ,
SecurityAlgorithms.Sha256Digest ,
"secret"
);
rsa.PersistKeyInCsp = false;
}
}
}
Some models:
Credentials.cs
public class Credentials
{
public string user { set;get;}
public string password { set;get;}
}
JwtToken.cs
public class JwtToken
{
public string access_token { set; get; }
public string token_type { set; get; }
}
A token controller for fetching a token (this is an example not intended for production), TokenController.cs:
[ Route("[controller]") ]
public class TokenController : Controller
{
private readonly OAuthBearerAuthenticationOptions _bearerOptions ;
private readonly SigningCredentials _signingCredentials ;
public TokenController
(
IOptions<OAuthBearerAuthenticationOptions> bearerOptions ,
SigningCredentials signingCredentials
)
{
_bearerOptions = bearerOptions.Options ;
_signingCredentials = signingCredentials ;
}
// POST: /token
[HttpPost()]
public JwtToken Token([FromBody] Credentials credentials)
{
// Pretend to validate credentials...
JwtSecurityTokenHandler handler =
_bearerOptions .
SecurityTokenValidators .
OfType<JwtSecurityTokenHandler>() .
First();
JwtSecurityToken securityToken =
handler .
CreateToken
(
issuer : _bearerOptions.TokenValidationParameters.ValidIssuer ,
audience : _bearerOptions.TokenValidationParameters.ValidAudience,
signingCredentials : _signingCredentials ,
subject : new ClaimsIdentity
(
new Claim []
{
new Claim(ClaimTypes.Name,"somebody"),
new Claim(ClaimTypes.Role,"admin" ),
new Claim(ClaimTypes.Role,"teacher" ),
}
) ,
expires : DateTime.Today.AddDays(1)
);
string token = handler.WriteToken(securityToken);
return new JwtToken()
{
access_token = token ,
token_type = "bearer"
};
}
}
A values controller to demonstrate ingesting the token, ValuesController.cs:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[Authorize("Bearer")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
Fire up a copy of postman (or your favorite REST client), launch the sample app under Visual Studio and make a POST request, similar to http: // localhost:22553/token/ with the JSON body:
{
"user" : "user",
"password" : "secret"
}
The app responds with a token:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6bnVsbH0.eyJ1bmlxdWVfbmFtZSI6InNvbWVib2R5Iiwicm9sZSI6WyJhZG1pbiIsInRlYWNoZXIiXSwiaXNzIjoiY29udG9zby5jb20iLCJhdWQiOiJjb250b3NvLmNvbS9yZXNvdXJjZXMiLCJleHAiOjE0Mzk1MzU2MDB9.anRgL10XFG_bKDDxY3D2xQSfhPRLGMjUTreQNsP1jDA6eRKwXHf3jtpCwm_saoWyUDFFA2TMI9e_LbP6F5l7vtozCluziE_GQkPkspUSWuWIpQJLPRTTPPZHGKmPmK4MLEl1zPPrggJWbvF9RBw3mMQ0KoMfjSL0vUQ8kZ7VXAel8dnYJccd-CFdnB6aDe79x2E9Se2iLxdhr--R_qgvfz1Fa6tR1dstqLQ-UjYqPWY4SOgBjM3abtjfLLVEzeQMVyezX7Cx9ObMXAGbGvQL6GB_T5RlfAoXWME4jM8Bzhd-07wwd732bBws4OXivj1sSz-qawNTnXmnuccLRtI1uA",
"token_type": "bearer"
}
Copy the token from the previous POST, then in postman make a GET request similar to http: // localhost:22553/api/values, taking care to add an Authorization header with the value "bearer YOURTOKEN" (e.g. bearer eyJ0eXAiOiJKV1QiLCJ...)
Observe that the app responds with the error:
System.InvalidOperationException
The following authentication scheme was not accepted: Bearer
Stack trace as follows:
at Microsoft.AspNet.Http.Authentication.Internal.DefaultAuthenticationManager.< AuthenticateAsync> d__9.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Http.Authentication.AuthenticationManager.< AuthenticateAsync> d__2.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter< TResult> .GetResult()
at Microsoft.AspNet.Mvc.AuthorizeFilter.< OnAuthorizationAsync> d__5.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAuthorizationFilterAsync> d__43.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAllAuthorizationFiltersAsync> d__42.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAsync> d__40.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.MvcRouteHandler.< InvokeActionAsync> d__4.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.MvcRouteHandler.< RouteAsync> d__3.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Routing.InnerAttributeRoute.< RouteAsync> d__10.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Routing.RouteCollection.< RouteAsync> d__9.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Builder.RouterMiddleware.< Invoke> d__4.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Hosting.Internal.RequestServicesContainerMiddleware.< Invoke> d__3.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Hosting.Internal.HostingEngine.< > c__DisplayClass29_0.< < Start> b__0> d.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Loader.IIS.RuntimeHttpApplication.< ProcessRequestAsyncImpl> d__10.MoveNext()
--- exception rethrown ---
at Microsoft.AspNet.Loader.IIS.RuntimeHttpApplication.< ProcessRequestAsyncImpl> d__10.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Loader.IIS.HttpApplicationBase.< InvokeProcessRequestAsyncImpl> d__9.MoveNext()
Note that adding logging adds virtually no additional insight, as the following logs show:
2015-08-13 13:32:35.969 -07:00 [Information] Request successfully matched the route with name 'null' and template '"api/Values"'.
Exception thrown: 'System.InvalidOperationException' in Microsoft.AspNet.Http.dll
2015-08-13 13:32:36.247 -07:00 [Error] An error occurred while handling the request.
2015-08-13 13:32:36.247 -07:00 System.InvalidOperationException: The following authentication scheme was not accepted: Bearer
I am hoping someone might understand where the breakdown in this example is taking place.

You must register the OAuth2 bearer authentication middleware before MVC, or your users will be unauthenticated when reaching MVC:
public class Startup {
public void Configure(IApplicationBuilder app) {
app.UseJwtBearerAuthentication(new JwtBearerOptions {
// Your JWT bearer options.
});
app.UseMvc();
}
}

Related

getting 500 error by calling any route which is getting data from database in azure-web-app-service

recently i have deployed my .net 5.0 api in azure web app without azure database. The app is running fine but i am not able to call any route which is getting data from my postgresql database. The api and getting data from my database is working fine in my local pc, but when i call any route in azure web app i get 500 internal server error and the detail error look like this.
error #1
Category: Microsoft.EntityFrameworkCore.Database.Connection
EventId: 20004
SpanId: 81307fd2722df04f
TraceId: 17283bbac7e9c74ab0dea8dafdf0a12a
ParentId: 0000000000000000
ConnectionId: 0HMB63E788EM6
RequestId: 0HMB63E788EM6:00000004
RequestPath: /api/someList
ActionId: d26cb19d-75cd-4847-828f-a74e2b5324ee
ActionName: QuranApi.Controllers.QuranTextController.GetSomeList (MyApi)
An error occurred using the connection to database 'MyDatabase' on server
'tcp://localhost:5432'.
error #2
Category: Microsoft.EntityFrameworkCore.Query
EventId: 10100
SpanId: 81307fd2722df04f
TraceId: 17283bbac7e9c74ab0dea8dafdf0a12a
ParentId: 0000000000000000
ConnectionId: 0HMB63E788EM6
RequestId: 0HMB63E788EM6:00000004
RequestPath: /api/someList
ActionId: d26cb19d-75cd-4847-828f-a74e2b5324ee
ActionName: QuranApi.Controllers.QuranTextController.GetSomeList (MyApi)
An exception occurred while iterating over the results of a query for context type
'QuranApi.PostgreSqlContext'.
System.InvalidOperationException: An exception has been raised that is likely due to
a transient failure.
---> Npgsql.NpgsqlException (0x80004005): Exception while connecting
---> System.Net.Sockets.SocketException (10013): An attempt was made to access a
socket in a way forbidden by its access permissions.
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async,
CancellationToken cancellationToken)
at Npgsql.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async,
CancellationToken cancellationToken)
at Npgsql.ConnectorPool.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout
timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.ConnectorPool.<>c__DisplayClass38_0.<<Rent>g__RentAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Npgsql.NpgsqlConnection.<>c__DisplayClass41_0.<<Open>g__OpenAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Npgsql.NpgsqlConnection.Open()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.
OpenDbConnection(Boolean errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean
errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean
errorsExpected)
at
Microsoft.EntityFrameworkCore.Storage.RelationalCommand.
ExecuteReader(RelationalCommandParameterObject parameterObject)at
Microsoft.EntityFrameworkCore.Query.Internal.
SingleQueryingEnumerable`1.Enumerator.
InitializeReader(DbContext _, Boolean result)at
Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.
NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation,
Func`3 verifySucceeded)
--- End of inner exception stack trace ---
at
Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.
NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation,
Func`3 verifySucceeded)at Microsoft.EntityFrameworkCore.Query.Internal.
SingleQueryingEnumerable`1.Enumerator.MoveNext()
Exception:
System.InvalidOperationException: An exception has been raised that is likely due to
a transient failure.
---> Npgsql.NpgsqlException (0x80004005): Exception while connecting
---> System.Net.Sockets.SocketException (10013): An attempt was made to access a
socket in a way forbidden by its access permissions.
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.Connect(NpgsqlTimeout timeout)
at Npgsql.NpgsqlConnector.RawOpen(NpgsqlTimeout timeout, Boolean async,
CancellationToken cancellationToken)
at Npgsql.NpgsqlConnector.Open(NpgsqlTimeout timeout, Boolean async,
CancellationToken cancellationToken)
at Npgsql.ConnectorPool.OpenNewConnector(NpgsqlConnection conn, NpgsqlTimeout
timeout, Boolean async, CancellationToken cancellationToken)
at Npgsql.ConnectorPool.<>c__DisplayClass38_0.<<Rent>g__RentAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Npgsql.NpgsqlConnection.<>c__DisplayClass41_0.<<Open>g__OpenAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
at Npgsql.NpgsqlConnection.Open()at
Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenDbConnection(Boolean
errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(Boolean
errorsExpected)
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean
errorsExpected) at
Microsoft.EntityFrameworkCore.Storage.RelationalCommand.
ExecuteReader(RelationalCommandParameterObject parameterObject)
at
Microsoft.EntityFrameworkCore.Query.Internal.
SingleQueryingEnumerable`1.Enumerator.InitializeReader(DbContext _, Boolean result)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.
Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
--- End of inner exception stack trace ---
at
Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.
Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at
Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.
Enumerator.MoveNext()
error #3
Category: Microsoft.AspNetCore.Server.Kestrel
EventId: 13
SpanId: 81307fd2722df04f
TraceId: 17283bbac7e9c74ab0dea8dafdf0a12a
ParentId: 0000000000000000
ConnectionId: 0HMB63E788EM6
RequestId: 0HMB63E788EM6:00000004
RequestPath: /api/someList
Connection id "0HMB63E788EM6", Request id "0HMB63E788EM6:00000004": An unhandled
exception was thrown by the application.
Exception:
System.InvalidOperationException: An exception has been raised that is likely due to
a transient failure.
---> Npgsql.NpgsqlException (0x80004005): Exception while connecting
---> System.Net.Sockets.SocketException (10013): An attempt was made to access a
socket in a way forbidden by its access permissions.
my Program.cs File
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls("https://localhost:5000", "https://IP-Address-of-my-PC")
.UseIISIntegration()
.UseStartup<Startup>();
});
}
And my Startup.cs File
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
var sqlConnectionString = Configuration["ConnectionString"];
services.AddDbContext<PostgreSqlContext>(options =>
options.UseNpgsql(connectionString: sqlConnectionString));
services.AddScoped<IDataAccessProvider, DataAccessProvider>();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1",
new OpenApiInfo
{
Title = "MyApi",
Version = "v1"
});
});
}
// This method gets called by the runtime.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger();
app.UseSwaggerUI(options =>
options.SwaggerEndpoint("/swagger/v1/swagger.json", "MyApi v1"));
}
}
i would be very happy if anybody can help me with this. I dont know what to do.

POST results in a HTTP 415 Unsupported Media Type response

I created an API which recovers the deck created by a user and store it in the database but the API gives me a 415 error.
Ising react I would like to use the api to communicate the front-end and the back-end
Thank you for your help :)
[Route("api/[controller]")]
[ApiController]
public class DeckController : Controller
{
private readonly ILogger<DeckController> _logger;
private readonly mtgContext _context;
public DeckController(ILogger<DeckController> logger, mtgContext context)
{
_logger = logger;
_context = context;
}
[HttpPost]
public OkObjectResult Add([FromBody] Deck deck)
{
try
{
Deck D = new Deck();
D.Name = deck.Name;
D.CreateAt = deck.CreateAt;
D.User = new User();
_context.Deck.Add(D);
_context.SaveChanges();
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
return Ok(new
{
Success = true,
returnCode = "200"
});
}
}
const [name, setName] = useState("");
const deck = useSelector(state => state.cardList.decklist);
function addDeck() {
fetch(`/api/Deck`, {
method: 'POST',
body: JSON.stringify({ Name: name, CreateAt: "2007-07-15", JoinCards: deck })
})
}
POST results in a HTTP 415 Unsupported Media Type response
When I tested with your code, I did reproduce the problem.
Through debug, it is found that in the fetch method, the contentType needs to be added to the application/json format to pass the json data, but there is a problem with your writing.
To sove it, just to change the content of headers in fecth as follow:
headers: { 'Content-Type': "application/json" }
I update the information and I give it to you hoping that it helps you find the origin of the problem.
[Route ("api/[controller]")]
[ApiController]
public class DeckController : Controller {
private readonly mtgContext _context;
public DeckController (mtgContext context) {
_context = context;
}
[HttpPost]
public void Add ([FromBody] JsonObject deck) {
}
}
function addDeck() {
const test = JSON.stringify({ Name: name, CreateAt: Date.now(), cardList: deck });
console.log(typeof(test));
fetch(`/api/Deck`, {
method: 'POST',
body: test,
headers: { contentType: 'application/json' }
})
}
System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'System.JsonObject'
at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DeserializeCreateObjectDelegateIsNull(Type invalidType)
at System.Text.Json.JsonSerializer.HandleStartObject(JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadCore(JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan`1 buffer, JsonSerializerOptions options, ReadStack& readStack)
at System.Text.Json.JsonSerializer.ReadAsync[TValue](Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

How to get DataLogger working from Android App

We are working on an app for the MoveSense that tracks the users movement in some particular cases. However due to our environment the Bluetooth connection can drop out intermittently. To not have any data loss we want to store the sensor data on the MoveSense and read it once the connection is back. In the documentation we found the DataLogger interface, but we are having issues getting it to work.
In our Android app we first subscribe to the sensor we want (for now only the gyro, but we will expand to include the accelerometer once we have the gyro up and running).
To do this we execute a put command:
Mds put() uri: suunto://<SERIAL>/Mem/DataLogger/Config contract: {"config": { "dataEntries": {"dataEntry": [{"path": "/Meas/Gyro/13"}]}}}
This command is accepted with a '200' code (figuring out the right JSON also took some time as the documentation lacks the 'config' part and uses a completely different path).
After this we try to activate the logger:
Mds put() uri: suunto://<SERIAL>/Mem/DataLogger/State contract: {"newState": 5}
But on this command we get a '403' (FORBIDDEN) error back:
[SDS RESPONSE] type: PUT status: FORBIDDEN header: {"Status": 403, "TaskId": 28, "Reason": "FORBIDDEN", "Uri": "suunto://<SERIAL>/Mem/DataLogger/State", "Content-Length": 0}
What are we doing wrong here? Is there an error in the config? Of are we forgetting some other action?
Note that we made sure to flash an app on the MoveSense that has the DataLoger and Logbook modules enabled.
First step before we can start logging we need to create DataLogger config.
Example for config with Accelerometer and Gyroscope logs.
{
"dataEntries" : {
"dataEntry" : [{
"path" : "/Meas/Acc/13"
}, {
"path" : "/Meas/Gyro/13"
}
]
}
}
Creating config in Android example:
PATH: {serial}/Mem/DataLogger/Config/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + "/Mem/DataLogger/Config/",
jsonConfig, new MdsResponseListener() {
#Override
public void onSuccess(String s) {
}
#Override
public void onError(MdsException e) {
}
});
EXAMPLE RESPONSE:
{"Content": {"dataEntries": {"dataEntry": [{"path": "/Meas/Acc/13"}, {"path": "/Meas/Gyro/13"}]}}}
When config is ready we can start logging.
To start logging, PUT value DATALOGGER_LOGGING (=3) to Mem/DataLogger/State resource
Android start logging example:
PATH: {serial}/Mem/DataLogger/State/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + /Mem/DataLogger/State/,
"{\"newState\":3}", new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"Content": 3}
To stop logging, PUT value DATALOGGER_READY (=2) to Mem/DataLogger/State resource
Android stop logging example:
PATH: {serial}/Mem/DataLogger/State/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + /Mem/DataLogger/State/,
"{\"newState\":2}", new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"Content": 2}
After log file is created we can get all entries / logs from the device:
PATH: /MDS/Logbook/{serial}/Entries REQUEST: GET
Mds.builder().build(context).get("suunto://" + movesenseSerial + "/Mem/Logbook/Entries/",
null, new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"elements": [{"Id": 1, "ModificationTimestamp": 536927972, "Size": null}, {"Id": 5, "ModificationTimestamp": 4446227, "Size": null}]}
When we have Entries we can read them
PATH: /MDS/Logbook/{serial}/byId/{LogId}/Summary REQUEST: GET
Mds.builder().build(context).get("suunto://MDS/Logbook/" + movesenseSerial + "/byId/" + entryId + "/Data",
null, new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});

how to use multi DBContextPool?

In EFCore 2.0 Add new feature, DbContext pooling.
i know how to use it in single context,
however, sometimes need multi context in project,
public class BContext : DbContext
{
public BContext(DbContextOptions<BContext> options) : base(options) { }
}
public class AContext : DbContext
{
public AContext(DbContextOptions<AContext> options) : base(options) { }
}
ConfigureServices
services.AddDbContextPool<AContext>(options =>
{
options.UseInMemoryDatabase("AContext.InMemory");
});
services.AddDbContextPool<BContext>(options =>
{
options.UseInMemoryDatabase("BContext.InMemory");
});
Controller
public class HomeController : Controller
{
private readonly AContext aContext;
public HomeController(AContext aContext)
{
this.aContext = aContext;
}
public IActionResult Index()
{
return View();
}
}
When i use any context, exception throw.
System.ArgumentException: Expression of type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MultiContext.Contexts.BContext]' cannot be used for constructor parameter of type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MultiContext.Contexts.AContext]'
Parameter name: arguments[0]
at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
at System.Dynamic.Utils.ExpressionUtils.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments, String methodParamName)
at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, IEnumerable`1 arguments)
at System.Linq.Expressions.Expression.New(ConstructorInfo constructor, Expression[] arguments)
at Microsoft.EntityFrameworkCore.Internal.DbContextPool`1.CreateActivator(DbContextOptions options)
at Microsoft.EntityFrameworkCore.Internal.DbContextPool`1..ctor(DbContextOptions options)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitSingleton(SingletonCallSite singletonCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass22_0.b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__2`1.b__2_1(IServiceProvider p)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(IServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass22_0.b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] )
at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__15.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.d__7.MoveNext()
Ok. i've found problem. You need to download EF Core, then change constructor for
DbContextPool< TContext>
original
public DbContextPool([NotNull] DbContextOptions options)
and change to
public DbContextPool([NotNull] DbContextOptions<TContext> options)
otherwise DI will use last added options :)
Or you can register your own factory of DbContextPool
services.AddDbContextPool<AContext>(options =>
{
options.UseInMemoryDatabase("AContext.InMemory");
});
services.AddDbContextPool<BContext>(options =>
{
options.UseInMemoryDatabase("BContext.InMemory");
});
collection.AddSingleton(svcs => new DbContextPool<AContext>(svcs.GetService<DbContextOptions<AContext>>()));
collection.AddSingleton(svcs => new DbContextPool<BContext>(svcs.GetService<DbContextOptions<BContext>>()));

Async update or insert MongoDB documents using .Net driver

I have the document like this
public class SomeDocument
{
public Guid Id { get; set; }
public string PropertyA { get; set; }
public string PropertyB { get; set; }
}
Now I have two different services (A and B) that update PropertyA and PropertyB appropriately and work in asynchronous manner. That means I don't know what service will finish first and should create the document and who should update it.
So, to update (or create) the document I currently use code like this in service A
var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var options = new FindOneAndUpdateOptions<SomeDocument, SomeDocument>() { IsUpsert = true };
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyA, "Property A value");
await Database.GetCollection<SomeDocument>("someDocuments").FindOneAndUpdateAsync(filter, update, options);
and the next code from service B
var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var options = new FindOneAndUpdateOptions<SomeDocument, SomeDocument>() { IsUpsert = true };
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyB, "Property B value");
await Database.GetCollection<SomeDocument>("someDocuments").FindOneAndUpdateAsync(filter, update, options);
Everything looks fine but sometimes I get the next error when both services work simultaneously
Unhandled Exception: MongoDB.Driver.MongoCommandException: Command findAndModify failed: E11000 duplicate key error collection: someDocuments index: _id_ dup key: { : BinData(3, B85ED193195A274DA94BC86B655B4509) }.
at MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.ProcessReply(ConnectionId connectionId, ReplyMessage`1 reply)
at MongoDB.Driver.Core.WireProtocol.CommandWireProtocol`1.<ExecuteAsync>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.Core.Servers.Server.ServerChannel.<ExecuteProtocolAsync>d__26`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.Core.Operations.CommandOperationBase`1.<ExecuteProtocolAsync>d__29.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.Core.Operations.WriteCommandOperation`1.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.Core.Operations.FindAndModifyOperationBase`1.<ExecuteAsync>d__19.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.OperationExecutor.<ExecuteWriteOperationAsync>d__3`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at MongoDB.Driver.MongoCollectionImpl`1.<ExecuteWriteOperationAsync>d__62`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at CVSP.MongoDbStore.MongoDbWriteModelFacade.<AddRecordField>d__6.MoveNext() in D:\Projects\Test\Source\MongoDbStore\WriteModel\MongoDbWriteModelFacade.cs:line 58
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
How should I insert/update documents in this case?
UPDATE
Extension did the trick
public static async Task<TProjection> FindOneAndUpdateWithConcurrencyAsync<TDocument, TProjection>(this IMongoCollection<TDocument> collection, FilterDefinition<TDocument> filter, UpdateDefinition<TDocument> update, FindOneAndUpdateOptions<TDocument, TProjection> options = null, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
return await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken);
}
catch (MongoException ex)
{
Thread.Sleep(10);
return await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken);
}
}
using try/catch at first glans looks strange and I didn't like it from the beginning but after reading https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/#upsert-and-unique-index all doubt gone away.
Well, this is syncronization issue and unfortunately there is no simple solution for it. In order to find a hack, let's disect what might be happening in backend.
Let's assume we have two threads (services) trying to upsert a document.
t1: 00:00:00.250 -> find document with Id (1)
t2: 00:00:00.255 -> find document with id (1)
t1: 00:00:00.260 -> No document found
t2: 00:00:00.262 -> No document found
t1: 00:00:00.300 -> Insert a document with Id(1)
t2: 00:00:00.300 -> Insert a document with Id(1)
Bingo... we got exception. Both threads are trying to insert document with same id.
No what we can do here?
Let's turn this shortcommig to our advantage. Capture this exception and try again to call upsert. This time, it will successfuly find document and update it.
I have modified code for ServiceA nd ServiceB as below and tried to insert 10000 documents in tight loop:
public async Task ServiceA(Guid id)
{
var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyA, "Property A value");
var options = new UpdateOptions() { IsUpsert = true };
var database = _client.GetDatabase("stackoverflow");
var collection = database.GetCollection<SomeDocument>(CollectionName,
new MongoCollectionSettings
{
WriteConcern = WriteConcern.W1
});
await collection.UpdateOneAsync(filter, update, options);
}
public async Task ServiceB(Guid id)
{
var filter = new FilterDefinitionBuilder<SomeDocument>().Where(r => r.Id == id);
var update = Builders<SomeDocument>.Update.Set(r => r.PropertyB, "Property B value");
var options = new UpdateOptions() { IsUpsert = true };
var database = _client.GetDatabase("stackoverflow");
var collection = database.GetCollection<SomeDocument>(CollectionName,
new MongoCollectionSettings
{
WriteConcern = WriteConcern.W1
});
await collection.UpdateOneAsync(filter, update, options);
}
Here is my lanuching code. Not perfect but serves purpose.
for (var i = 0; i < 10000; i++)
{
var _guid = Guid.NewGuid();
var _tasks = new[]
{
new Task(async (x) =>
{
var p = new Program();
try
{
await p.ServiceA(Guid.Parse(x.ToString()));
}
catch (MongoWriteException me)
{
await Task.Delay(5);
await p.ServiceA(Guid.Parse(x.ToString()));
}
}, _guid),
new Task(async (x) =>
{
var p = new Program();
try
{
await p.ServiceB(Guid.Parse(x.ToString()));
}
catch (MongoWriteException me)
{
await Task.Delay(5);
await p.ServiceB(Guid.Parse(x.ToString()));
}
}, _guid)
};
_tasks[0].Start();
_tasks[1].Start();
Task.WaitAll(_tasks);
}