How to access IReliableDictionary in service fabric - azure-service-fabric

[Route("testApp/{Id}")]
[HttpGet]
public async Task<List<string>> testApp(Guid Id)
{
return await new Heavy().WorkOnDictionary(Id);
}
I have created a stateful service and has implemented REST api calls in one of the call I want to access the IReliableDictionary but i cannot do that it asks for stateManager
I want to access IReliableDictionary inside WorkOnDictionary() function which is defined inside a class Heavy
public async Task<List<string>> WorkOnDictionary(Guid Id){
IReliableDictionary<string, List<string>> Dictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<string, List<string>>>("stringlistcache");
string monitorName=convertIdtoMonitor(Id);
using (var tx = this.StateManager.CreateTransaction())
{
var cachedValue = await Dictionary.TryGetValueAsync(tx, monitorName);
await tx.CommitAsync();
if(cachedValue.HasValue)
return cachedValue.Value
else
{
var res=await GetdetailFrmRemote(monitorName)
Dictionary.AddAsync(tx,monitorName,res);
return res;
}}
}
How should I initialize StateManager?

If I'm reading this correctly, it looks like you want to use IReliableStateManager in an MVC controller. In that case, simply inject it into your controller. ASP.NET Core has a nice DI system built in. The Service Fabric getting-started sample code shows you how to do this: https://github.com/Azure-Samples/service-fabric-dotnet-getting-started/tree/master/src/GettingStartedApplication/StatefulBackendService

Related

.NET MAUI: Passing objects from ObservableCollection to an "AddObject" page and back (MVVM)

My main page has an ObservableCollection (defined in a ViewModel) holding objects (defined in a Model) and I have other pages, one that shows details and one to add a new object. I use the MVVM CommunityToolkit.
I can pass one object from the collection to the details page:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.ScrumDetailsPage)}", true,
new Dictionary<string, object>
{
{"Scrum", scrum }
});
}
However, I am not able to pass the whole collection to the "Add" page, add the new object and pass it back:
[RelayCommand]
async Task AddNewScrum(ObservableCollection<DailyScrum> scrums)
{
if (scrums is null)
return;
await Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}", true,
new Dictionary<string, object>
{
{"Scrums", scrums }
});
}
How can I do this? Or is it a wrong attempt to pass around the collection? Can I write access the collection from another viewmodel?
If the ObservableCollection's size is small, you can try to use the Newtonsoft.Json package to convert it to string and then pass it. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Shell.Current.GoToAsync($"{nameof(View.AddScrumPage)}?param={jsonstring}");
And convert the string to ObservableCollection with the following code:
ObservableCollection<DailyScrum> data = JsonConvert.DeserializeObject<ObservableCollection<DailyScrum>>(jsonstring);
If the collection's size is big, you can store the string in the Preferences. Such as:
var jsonstring = JsonConvert.SerializeObject(scrums);
Preferences.Set("Data", jsonstring);
And get the data in the AddScrumPage:
bool hasKey = Preferences.ContainsKey("Data");
var content = Preferences.Get("Data", string.Empty);
ObservableCollection<DailyScrum> data = hasKey ? JsonConvert.DeserializeObject<Model>(content) : null;
Update
You can try to use the Shell.Current.Navigation, it has the same effect as the Shell.Current.GoToAsync, such as:
[RelayCommand]
async Task GoToDetails(DailyScrum scrum)
{
if (scrum is null)
return;
await Shell.Current.Navigation.PushAsync(new View.ScrumDetailsPage(scrums))
}

how to add a token head to a request using HttpClient from IHttpClientFactory in Blazor

I am trying to use JWT in my API, and configuration is completed, can use postman tool to access data from it. However when I use Blazor as front end to access it , the request doesn't have token, so always give a 401 code.
Below is my Blazor code.
program.cs
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
builder.Services.AddScoped(
sp => sp.GetService<IHttpClientFactory>().CreateClient("OptionAPI"));
OptionService.cs
public class OptionService : IOptionService {
private readonly HttpClient _httpClient;
public OptionService(HttpClient httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
_httpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await _httpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
I tired use " new AuthenticationHeaderValue("Bearer", "token");" to attach token in header, but its not working, still give 401 code.
And I also tried use
private readonly IHttpClientFactory _httpClient;
public OptionService(IHttpClientFactory httpClient) {
_httpClient = httpClient;
}
public async Task<IEnumerable<OptionOutputDto>> GetOptionsAsync(Guid quizId, Guid questionId) {
var newHttpClient = _httpClient.CreateClient();
newHttpClient.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", "token");
return await JsonSerializer.DeserializeAsync<IEnumerable<OptionOutputDto>>(
await newHttpClient.GetStreamAsync($"api/quizzes/{quizId}/{questionId}/options"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}
it's also not working, give me an error,
Unhandled exception rendering component: A suitable constructor for type 'Services.OptionService' could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
System.InvalidOperationException: A suitable constructor for type .....
Can anyone has a simple way to attach token in request header?
Thanks in advance.
I think the good option is :
builder.Services.AddHttpClient<IOptionService, OptionService> ("OptionAPI", (sp, cl) => {
cl.BaseAddress = new Uri("https://localhost:7172");
});
Could you check if the token is present in header or not?
Your error is most likely related to how the OptionService is being registered in dependency injection. It either needs an empty constructor adding - and/or - you need to ensure that the constructor has all of its dependencies registered correctly in the ServicesCollection too.
The exception is quite explicit:
Ensure the type is concrete and all parameters of a public constructor
are either registered as services or passed as arguments. Also ensure
no extraneous arguments are provided
I gave a similar answer here. Basically you need to include the BaseAddressAuthorizationMessageHandler when defining your httpclients. If you're using a typed httpclient, you can inject the IAccessTokenProvider and get the token from there. Kinda like this:
public class MyHttpClient(IAccessTokenProvider tokenProvider, HttpClient httpClient)
{
_tokenProvider = tokenProvider;
_httpClient = httpClient;
}
private async Task RequestAuthToken()
{
var requestToken = await _tokenProvider.RequestAccessToken();
requestToken.TryGetToken(out var token);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Value);
}
public async Task<IEnumerable<ReplyDto>> SendHttpRequest()
{
await RequestAuthToken();
return await JsonSerializer.DeserializeAsync<IEnumerable<ReplyDto>>(
await _httpClient.GetStreamAsync("api/getendpoint"),
new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
});
}

Authorization with Azure SignalR service fails because IServiceCollection is null

In a project I have a SignalR hub which can do authorization. The simplified version of the hub looks like this.
public class WeatherHub : Hub
{
public async Task SubscribeForWeatherChange(string city)
{
// Will never throw. Based on authentication state it will evaluate to true or false regardless of Azure
// SignalR service is being used or not.
bool authenticated1 = this.Context.User.Identity.IsAuthenticated;
bool authenticated2 = this.Context.GetHttpContext().User.Identity.IsAuthenticated;
// Will throw when Azure SignalR service is being used.
string accessToken = await this.Context.GetHttpContext().GetTokenAsync("cookies", "access_token");
this.EnsureIsAuthorized(accessToken); // Executes HTTP call and throws based on response (!= 200).
await this.Groups.AddToGroupAsync(this.Context.ConnectionId, city);
}
public async Task SendWeatherChange(string city, string weather)
{
await this.Clients.Group(city).SendAsync("WeatherChanged", city, weather);
}
}
This code runs perfectly fine. The access token is being extracted and can be used when executing HTTP calls. It is getting problematic when we want to use Azure SignalR service. In this case reading out the access token does not work.
My investigation shows that when SignalR service is being used the extension method GetTokenAsync(this HttpContext context, string scheme, string tokenName) is throwing an ArgumentNullException because the context.RequestServices is null.
Is my way of doing authorization wrong? Is there a better approach? Why is the IServiceProvider null?
My service configuration looks like this.
public void ConfigureServices(IServiceCollection services)
{
services
.AddSignalR()
.AddAzureSignalR();
services
.AddAuthentication(options =>
{
options.DefaultScheme = "cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("cookies", options =>
{
// cookie configuration
})
.AddOpenIdConnect("oidc", options =>
{
options.SaveTokens = true;
// oidc configuration
});
services.AddControllers();
}

How to Access Provider with out Context in Flutter

I have a problem with the Flutter Provider pattern, I need to access Provides from Class where I don't have Context.
Providers :
import 'package:flutter/foundation.dart';
class TokenProvider with ChangeNotifier {
TokenService tokenService = TokenService();
String _accessToken = '';
String get accessToken {
return _accessToken;
}
dynamic setAccessToken(data) async {
_accessToken = data;
}
}
Class :
import '../constants/constants.dart';
import '../models/models.dart';
import './network-call/base-service.dart';
class TokenService extends BaseService {
Future<String> getToken() async {
final dynamic response = await serviceCall(
url: ApiName().apiName(api: ServiceName.TOKEN),
method: ApiMethod.POST,
queryParameters: {
'id': Preferences().env['id'],
'secret': Preferences().env['secret'],
'type': 'rrrr'
});
Need to set this responce Data in Providers
}
}
Thank you.
try to add "notifyListeners();"
dynamic setAccessToken(data) async {
_accessToken = data;
notifyListeners();
}
I use to pass the context when needed, as dlohani suggests in the comment at the question, but I found myself in the same situation and applied a solution inspired by the communication pattern used between isolates: messages exchange.
The Provider class need to have a ReceiverPort field which listens to request messages. Ones the message reaches this listener you're inside the Provider, so you can retrieve data and send them back, again in the ReceiverPort's fashion, that is using the sendPort of the ReceiverPort registered in the requesting class.
In code below I suppose messages are Maps to clarify the type of data exchanged:
class SomeProvider with ChangeNotifier {
var _innerData = yourData;
var _providerReceiverPort = ReceiverPort();
SomeProvider() {
// The registration is necessary to "publish" the receiver port
IsolateNameServer.registerPortWithName(
_providerReceiverPort, "SomeProviderPort");
_providerReceiverPort.listen(
(message) {
// I retrieve the port to send the response to
var port = message["sendPort"];
// The answer follows the rules of messaging: maps and lists are ok
port.send({"data": _innerData.getSomething()});
}
);
}
}
class SomeClient {
var _clientReceiverPort = ReceiverPort();
someFunction(){
// First step: prepare the receiver to obtain data
_clientReceiverPort.listen(
(message) {
// Data are stored in the map containing a "data" key
var response = message["data"];
...
}
);
// Now I can retrieve the provider port by using the same name it uses to publish it
var providerPort = IsolateNameServer.lookupPortByName("SomeProviderPort");
// The message must include the sendPort to permit the provider to address the response
providerPort.send({"sendPort": _clientReceiverPort.sendPort});
}
}
The drawback of this solution is that the Provider doesn't work as a provider for the SomeClient class. You can obviously notify if any change in the listener is important for the subscribers: for example, I use this pattern to update data in the provider from a background isolate.
As I said, this is a workaround, any suggestion to improve is welcome.

System.TimeoutException in Service Fabric stateful service. Gets struck in ITransaction.CommitAsync method

I have a stateful service that uses IReliableConcurrentQueue to store state. I am using a Stateless ASP.NET Core service to expose two REST endpoints.
The GET endpoint gets specified number of items from the named IReliableConcurrentQueue. It also writes to a separate reliable concurrent queue in the same transaction before returning.
The POST endpoint receives a list of items and adds them to IReliableConcurrentQueue.
When I run the service from Visual Studio locally in debug mode, when I hit one endpoint (GET or POST) repeatedly, it works fine without any issues.
When I hit both endpoints simultaneously, after 3 or 4 hits (sometimes 10), the service stops responding and I see Timeout exceptions.
REST Endpoints (Stateless Service)
[HttpGet]
[Route("{number}/{queueName}")]
public async Task<IEnumerable<QueueItem>> Get(int number, string queueName)
{
IEnumerable<QueueItem> items = await statefulServiceProxy.GetItems(number, queueName);
return items;
}
[HttpPost]
[Route("item")]
public async Task Set([FromBody]IEnumerable<Item> items)
{
await statefulServiceProxy.SetItem(items);
}
Stateful Service
Always gets stuck in CommitAsync function in either ReadAndLockItems() or SetItem().
public async Task<IEnumerable<QueueItem>> GetItems(int number, string queueName)
{
var items = await this.ReadAndLockItems(number, queueName);
return items;
}
private async Task<IEnumerable<QueueItem>> ReadAndLockItems(int number, string queueName)
{
var itemQueue = await this.StateManager.GetOrAddAsync<IReliableConcurrentQueue<QueueItem>>(queueName);
var lockQueue = await this.StateManager.GetOrAddAsync<IReliableConcurrentQueue<QueueItem>>("LockQueue");
List<QueueItem> results = new List<QueueItem>();
using (var tx = this.StateManager.CreateTransaction())
{
for (int i = 0; i < number; i++)
{
var element = await itemQueue.TryDequeueAsync(tx);
if (element.HasValue)
{
results.Add(element.Value);
}
}
results.ToList().ForEach(async r =>
{
await lockQueue.EnqueueAsync(tx, r);
});
await tx.CommitAsync(); // This is where it gets stuck.
}
return results;
}
public async Task SetItem(IEnumerable<Product> products)
{
var productQueue = await this.StateManager.GetOrAddAsync<IReliableConcurrentQueue<Product>>("ProductQueue");
using (var tx = this.StateManager.CreateTransaction())
{
foreach (Product p in products)
{
await productQueue.EnqueueAsync(tx, p);
}
await tx.CommitAsync(); // Sometimes it gets stuck here. It's not consistent. Either in this function or the above one.
}
}
I checked this Post and tried the changes mentioned there. It doesn't still work.
Exception details:
System.TimeoutException: This can happen if message is dropped when service is busy or its long running operation and taking more time than configured Operation Timeout.
at Microsoft.ServiceFabric.Services.Communication.Client.ServicePartitionClient`1.<InvokeWithRetryAsync>d__24`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
Is there anything wrong with the way I am using the collections and the related transactions?