In the context of a .net6 incremental source generator, what does the IncrementalValuesProvider.WithTrackingName(string name) method do?
In addition how/when is it intended to be used?
[Generator]
public class MyGenerator : IIncrementalGenerator
{
var incremntalValueProvider = context.SyntaxProvider
.CreateSyntaxProvider(predicate: (n, c) => { ... }, transform: (s, c) => { ... });
incremntalValueProvider = incremntalValueProvider.WithTrackingName("Some tracking name");
}
With most things when it comes to source generators, I have been able to google and use a bit of trial and error, to figure out how things work.
However with this particular method, those aproaches has not been helpful. In addition it seems like Micrsofts documentation is pretty much out of date since
https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md does not even mention the method.
I have a controller which returns a large json object. If this object does not exist, it will generate and return it afterwards. The generation takes about 5 seconds, and if the client sent the request multiple times, the object gets generated with x-times the children. So my question is: Is there a way to block the second request, until the first one finished, independent who sent the request?
Normally I would do it with a Singleton, but because I am having scoped services, singleton does not work here
Warning: this is very oppinionated and maybe not suitable for Stack Overflow, but here it is anyway
Although I'll provide no code... when things take a while to generate, you don't usually spend that time directly in controller code, but do something like "start a background task to generate the result, and provide a "task id", which can be queried on another different call).
So, my preferred course of action for this would be having two different controller actions:
Generate, which creates the background job, assigns it some id, and returns the id
GetResult, to which you pass the task id, and returns either different error codes for "job id doesn't exist", "job id isn't finished", or a 200 with the result.
This way, your clients will need to call both, however, in Generate, you can check if the job is already being created and return an existing job id.
This of course moves the need to "retry and check" to your client: in exchange, you don't leave the connection to the server opened during those 5 seconds (which could potentially be multiplied by a number of clients) and return fast.
Otherwise, if you don't care about having your clients wait for a response during those 5 seconds, you could do a simple:
if(resultDoesntExist) {
resultDoesntExist = false; // You can use locks for the boolean setters or Interlocked instead of just setting a member
resultIsBeingGenerated = true;
generateResult(); // <-- this is what takes 5 seconds
resultIsBeingGenerated = false;
}
while(resultIsBeingGenerated) { await Task.Delay(10); } // <-- other clients will wait here
var result = getResult(); // <-- this should be fast once the result is already created
return result;
note: those booleans and the actual loop could be on the controller, or on the service, or wherever you see fit: just be wary of making them thread-safe in however method you see appropriate
So you basically make other clients wait till the first one generates the result, with "almost" no CPU load on the server... however with a connection open and a thread from the threadpool used, so I just DO NOT recommend this :-)
PS: #Leaky solution above is also good, but it also shifts the responsability to retry to the client, and if you are going to do that, I'd probably go directly with a "background job id", instead of having the first (the one that generates the result) one take 5 seconds. IMO, if it can be avoided, no API action should ever take 5 seconds to return :-)
Do you have an example for Interlocked.CompareExchange?
Sure. I'm definitely not the most knowledgeable person when it comes to multi-threading stuff, but this is quite simple (as you might know, Interlocked has no support for bool, so it's customary to represent it with an integral type):
public class QueryStatus
{
private static int _flag;
// Returns false if the query has already started.
public bool TrySetStarted()
=> Interlocked.CompareExchange(ref _flag, 1, 0) == 0;
public void SetFinished()
=> Interlocked.Exchange(ref _flag, 0);
}
I think it's the safest if you use it like this, with a 'Try' method, which tries to set the value and tells you if it was already set, in an atomic way.
Besides simply adding this (I mean just the field and the methods) to your existing component, you can also use it as a separate component, injected from the IOC container as scoped. Or even injected as a singleton, and then you don't have to use a static field.
Storing state like this should be good for as long as the application is running, but if the hosted application is recycled due to inactivity, it's obviously lost. Though, that won't happen while a request is still processing, and definitely won't happen in 5 seconds.
(And if you wanted to synchronize between app service instances, you could 'quickly' save a flag to the database, in a transaction with proper isolation level set. Or use e.g. Azure Redis Cache.)
Example solution
As Kit noted, rightly so, I didn't provide a full solution above.
So, a crude implementation could go like this:
public class SomeQueryService : ISomeQueryService
{
private static int _hasStartedFlag;
private static bool TrySetStarted()
=> Interlocked.CompareExchange(ref _hasStartedFlag, 1, 0) == 0;
private static void SetFinished()
=> Interlocked.Exchange(ref _hasStartedFlag, 0);
public async Task<(bool couldExecute, object result)> TryExecute()
{
if (!TrySetStarted())
return (couldExecute: false, result: null);
// Safely execute long query.
SetFinished();
return (couldExecute: true, result: result);
}
}
// In the controller, obviously
[HttpGet()]
public async Task<IActionResult> DoLongQuery([FromServices] ISomeQueryService someQueryService)
{
var (couldExecute, result) = await someQueryService.TryExecute();
if (!couldExecute)
{
return new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = "Another request has already started. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
return Ok(result);
}
Of course possibly you'd want to extract the 'blocking' logic from the controller action into somewhere else, for example an action filter. In that case the flag should also go into a separate component that could be shared between the query service and the filter.
General use action filter
I felt bad about my inelegant solution above, and I realized that this problem can be generalized into basically a connection number limiter on an endpoint.
I wrote this small action filter that can be applied to any endpoint (multiple endpoints), and it accepts the number of allowed connections:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConcurrencyLimiterAttribute : ActionFilterAttribute
{
private readonly int _allowedConnections;
private static readonly ConcurrentDictionary<string, int> _connections = new ConcurrentDictionary<string, int>();
public ConcurrencyLimiterAttribute(int allowedConnections = 1)
=> _allowedConnections = allowedConnections;
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var key = context.HttpContext.Request.Path;
if (_connections.AddOrUpdate(key, 1, (k, v) => ++v) > _allowedConnections)
{
Close(withError: true);
return;
}
try
{
await next();
}
finally
{
Close();
}
void Close(bool withError = false)
{
if (withError)
{
context.Result = new ObjectResult(new ProblemDetails
{
Status = StatusCodes.Status503ServiceUnavailable,
Title = $"Maximum {_allowedConnections} simultaneous connections are allowed. Try again later.",
Type = "https://tools.ietf.org/html/rfc7231#section-6.6.4"
})
{ StatusCode = StatusCodes.Status503ServiceUnavailable };
}
_connections.AddOrUpdate(key, 0, (k, v) => --v);
}
}
}
What is the common rule or best practices about creating (POST method) a new item in my database with a Web API (REST). Should the body of my POST method contains the identifier?
With identifier:
public HttpResponseMessage Post(AddressModel addressModel)
{
using (var context = new DbContext())
{
if (context.address.Any(y => y.id == addressModel.id)
throw new Exception("Item already exist"); // or return BadRequest
...
}
}
Without identifier:
public HttpResponseMessage Post(AddressModel addressModel)
{
if (addressModel.id != null)
throw new Exception("identifier cannot be set"); // or return BadRequest
using (var context = new DbContext())
{
addressModel.id = GetNewId();
// GetNewId generate the next correct if from data
// from context.address.last ...
...
}
}
And of course (before downvoting) you can just tell me there is no correct answer because the first solution is more efficient in this or this case and the other one is better for other reason. Of course I have my own opinion and this is an opinion question for people without experience like me. But for people with experience I'm sure they can answer this question without hesitation and with non opinionated arguments. So this is not en opinionated question or please tell me why.
If you are creating an object, the API should be responsible for assigning the unique id. Otherwise, the caller has to know too much about how the API works (what type of id, how do we get unique values, etc.).
Also, you can return the assigned if if the caller needs it for later operations.
I'm using WebApi2 and OData. I want add custom action, and use it by GET method
GET /odata/Providers(2)/DoSth
but I dont understand how it works exactly. Here is code for one of my controller:
public class ProvidersController : ODataController
{
private Entities db = new Entities();
// GET: odata/Providers
[Queryable]
public IQueryable<PROVIDER> GetProviders()
{
return db.PROVIDER;
}
//... OTHER GENERATED METHODS
//MY TEST METHOD SHOULD BE inoked: GET /odata/Providers(2)/DoSth
public int DoSth()
{
return 22;
}
}
and WebApiConfigFile:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PROVIDER>("Providers").EntityType.HasKey(o => o.P_ID);
//others entities ...
//my custom action without any parameters, returns int:
ActionConfiguration getTest = builder.Entity<PROVIDER>().Action("DoSth");
getTest.Returns<int>();
Method existing in /odata/$metadata
but cant run this method from the url (still showing 404: "No HTTP resource was found that matches the request URI").
Any ideas how to improve this issue?
In OData an action can only be invoked by the POST method. So just change the request from GET to POST.
If it doesn't work, add an attribute to the method in the controller:
[HttpPost]
public int DoSth()
{
return 22;
}
If you just start to play with OData, I recommend you start from OData V4, which is an OASIS standard. Here is a sample about actions: https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataActionsSample/ .
I solved the problem in a different way... I am not a deep dive programmer only an intermediate... I do however solve problems in any way possible as they arise...
I required a search capability that could not be handled by standard $filter functionality and I needed to return an IQueryable just like any OData 4 controller would (less a get function).
First in my appropriate controller... I took the exact same signature of my Get"Entity" call and added a parameter.
[EnableQuery]
public IQueryable<detail> Getdetails([FromODataUri] int key)
{
return db.masters.Where(m => m.masterid == key).SelectMany(m => m.details);
}
// custom function goes here...
[EnableQuery]
public IQueryable<detail> GetSearchDetails([FromODataUri] int key, [FromODataUri] IEnumerable<int> search)
{
1) do your own function logic here... mine happens to be a very complex search...
2) make sure to return iQueryable as result... I use standard linq queries and then the final return is toList() as IQueryable
3) I also did a simple return search.Count = 0 ? return all results : return queried results.
}
In the WebAPi Config this is the signature;
1) the first line says place the code in the MasterController.
2) the second line tells me what to call the function.
3) the third line tells me what to return.
4) the fourth line tells me what to call the parameter and what type it is...
5) the fifth line is VERY important if you want to avoid having to have to call "://.../namespace.function(param='value')". This removes the dotted namespace constraint. see:this
builder.EntityType<master>()
.Function("GetSearchDetails")
.ReturnsCollectionFromEntitySet<detail>("details")
.CollectionParameter<int>("search");
config.EnableUnqualifiedNameCall(unqualifiedNameCall: true);
This approach solved many of my problems on the client side... Now, i can call h!!p://domain/odata/master(n)/GetSearchDetails(search=[2,10,31]) or if it were an array of string h!!p://domain/odata/master(n)/GetSearchDetails(search=['two','ten','thirtyone']) and it returns an IQueryable just like calling the underlying entity... However, the added benifit is that ALL the standard OData v4 functionality is still there $filter, $select... etc...
I am trying to mock a method that first checks if the data is present in database, if not then it calls the save method. How do i mock the if condition?
Code is like this
public MyCode AddMyCode(MyCode myCode)
{
int workingUnitId = _sessionManager.CurrentUser.WorkingUnit.Id;
if (!_db.MyCodes.Any(d => d.Code == myCode.Code && d.UnitId == this.CurrentUserParentUnitId))
{
_db.MyCodes.Add(myCode);
SaveMyCode();
return myCode;
}
return myCode;
}
you should use interfaces to decouple database access code (EF here) and your business logic.
this way you can test your logic without need for a real database.
a good tutorial could be this : http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
You can mock your data base call by setting up some dummy data into MyCodes and see whether the rest of the code is working fine.
Updated:
include a static method like this:
public static Mock<ControllerContext> MockSession()
{
var controllerContext = new Mock<ControllerContext>();
controllerContext.Setup(X => X.HttpContext.Session["UserName"]).Returns("Avinash");
return controllerContext;
}
Then you can do:
In your test method,
Use the above static method to mock the session:
target.ControllerContext = MockSession().Object;
This will mock the session. The above example is from asp.net MVC Prespective. I am not sure how to do it with normal webforms.
You can try to implement it for webforms also based on above.