The Notification System of Abp is not run .What wrong with it? - c#

Notification System used to send some message to specific users.But global event not avaible in js file.
code in js
abp.event.on('abp.notifications.received', function (userNotification) {
console.log("userNotification", userNotification);
});
code in application
public class NotificationTestService : AcsAppServiceBase, INotificationTestService
{
private INotificationPublisher _notificationPublisher;
private readonly IRepository<User, long> _userRepository;
public NotificationTestService(INotificationPublisher notificationPublisher, IRepository<User, long> userRepository)
{
_notificationPublisher = notificationPublisher;
_userRepository = userRepository;
}
public async void NotificationTest()
{
string message = "Test Message";
var currentUser = (await this.GetCurrentUserAsync()).ToUserIdentifier();
_notificationPublisher.Publish("NotificationTest", new MessageNotificationData(message), severity: NotificationSeverity.Info, userIds: new[] { currentUser });
}
}
notification data 1
notification data 2
I found the notification data in database,but event is not trigger in js file .No exception throw.
Thank you for your any help.

Related

Getting ILogger to write to the console from a service

I have a simple function app that uses MediatR pattern and the base function looks like this:
public class ShareEnvelopesFunction
{
private readonly IMediator _mediator;
public ShareEnvelopesFunction(IMediator mediator)
{
_mediator = mediator;
}
[FunctionName(nameof(ShareEnvelopesFunction))]
public async Task Run([TimerTrigger("%ScheduleShareEnvelopesSetting%")]TimerInfo timer, ILogger log)
{
log.LogInformation("Starting Share Envelopes function {0}", timer);
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}
}
As you can see ShareEnvelopesCommandRequest is a class that has only one property which is the ILogger
public class ShareEnvelopesCommandRequest : IRequest<ShareEnvelopesCommandResponse>
{
public ILogger log { get; set; }
}
Now in my command handler, if I use request.log.LogInformation, it logs messages to my console. A sample command handler code looks like this:
public class ShareEnvelopesCommandHandler : IRequestHandler<ShareEnvelopesCommandRequest, ShareEnvelopesCommandResponse>
{
private readonly IUnitOfWork _unitOfWork;
private readonly IDocuSignApiService _docuSignService;
public ShareEnvelopesCommandHandler(IUnitOfWork unitOfWork
, IDocuSignApiService docuSignService
)
{
_unitOfWork = unitOfWork;
_docuSignService = docuSignService;
}
public async Task<ShareEnvelopesCommandResponse> Handle(ShareEnvelopesCommandRequest request, CancellationToken cancellationToken)
{
request.log.LogInformation("Starting to share envelopes");
await _docuSignService.ShareEnvelopes(fromGroupUser.UserId, deltaUsers);
return new ShareEnvelopesCommandResponse() {isSuccess=true };
}
Now the real issue here is that, if you see the above code, I am injecting a docusign service and inside this service, I need to log certain information into the console. My sample docusign service looks like this:
public class DocuSignApiService : IDocuSignApiService
{
public IGroupsApi _groupsApi { get; set; }
public IAccountsApi _accountsApi { get; set; }
public DocuSignApiService(IGroupsApi groupsApi, IAccountsApi accountsApi)
{
_groupsApi = groupsApi;
_accountsApi = accountsApi;
}
public async Task ShareEnvelopes(string fromUserId, List<string> toUsersList)
{
//_logger.LogInformation("This is a test");
}
}
Now I need to be able to log any information from this service to the console. Now I can pass the ShareEnvelopesCommandRequest request to this service but I don't think that would be very efficient. So here is what I have tried:
I injected ILogger into the service:
public class DocuSignApiService : IDocuSignApiService
{
public IGroupsApi _groupsApi { get; set; }
public IAccountsApi _accountsApi { get; set; }
private readonly ILogger _logger;
public DocuSignApiService(IGroupsApi groupsApi, IAccountsApi accountsApi, ILogger<ShareEnvelopesCommandRequest> logger )
{
_groupsApi = groupsApi;
_accountsApi = accountsApi;
_logger = logger;
}
public async Task ShareEnvelopes(string fromUserId, List<string> toUsersList)
{
_logger.LogInformation("This is a test");
}
}
The configuration on DI in my startup class looked like this:
services.AddScoped<IDocuSignApiService>(_ => new DocuSignApiService(docuSignConfig,
_.GetService<IGroupsApi>(),
_.GetService<IAccountsApi>(),
_.GetService<ILogger<ShareEnvelopesCommandRequest>>()
)
);
However, doing that didn't log information into the azure function app console. Are there an ideas on how I can go about logging messages from a service into the console? Thanks in advance.
Maybe you have to setup log level in host.json like this:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"YourNamespace.DocuSignApiService": "Information",
"YourNamespace.ShareEnvelopesFunction": "Information",
}
}
}
I'm guessing you get the ILogger instance in your Run method directly from azure, whenever you run the function.
_.GetService<ILogger<ShareEnvelopesCommandRequest>>()
That above probably gets another instance unrelated to the one azure provides you and maybe that's why no logs happen.
If you're going to use DI, you gotta do it from the start of the Run method of your function class (ShareEnvelopesFunction).
public class ShareEnvelopesFunction
{
private readonly IMediator _mediator;
private readonly ServiceCollection _serviceCollection;
public ShareEnvelopesFunction(IMediator mediator, ServiceCollection serviceCollection)
{
_serviceCollection = serviceCollection;//inject this from your startup
_mediator = mediator;
}
[FunctionName(nameof(ShareEnvelopesFunction))]
public async Task Run([TimerTrigger("%ScheduleShareEnvelopesSetting%")] TimerInfo timer, ILogger log)
{
_serviceCollection.AddSingleton(log);//this should ensure that all log instances injected into your services are references to the one azure provided, or something like that
_serviceCollection.AddScoped<IDocuSignApiService,DocuSignApiService>();//now that your servicecollection knows what ILogger to inject, you can also inject your service normally, not sure if this needs to be done here tho, leaving it in your startup should be fine
log.LogInformation("Starting Share Envelopes function {0}", timer);
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}
}
I've done this in a recent AWS Lambda project, their ILambdaContext also serve as logger. I was having the same issue as you, no logs in DI dependent services.
If that doesn't do the trick, try rebuilding your ServiceProvider before Mediator does it's thing.
using (IServiceProvider serviceProvider = _serviceCollection.BuildServiceProvider())
{
log.LogInformation("Starting Share Envelopes function {0}", timer);
_mediator = serviceProvider.GetService<IMediator>();
var result = await _mediator.Send(new ShareEnvelopesCommandRequest { log = log });
}

Using HttpContext in a background task after response has been completed

A user can trigger a long-running job by sending a request to an ASP.NET Core controller. Currently, the controller executes the job and then sends a 200 OK response. The problem is that the client has to wait rather long for the response.
This is why I am currently trying to process the job in a background task. I am using an IBackgroundTaskQueue where all jobs are stored and an IHostedService that processes the jobs whenever a new one is enqueued. It is similar to the code in the Microsoft documentation.
But the job does need access to the database and therefore the user has to authenticate using Active Directory. Hence, I need access to the HttpContext.User property in the background task. Unfortunately, the HttpContext is disposed when the response is sent and before the processing of the job begins.
Demonstration
public class Job
{
public Job(string message)
{
Message = message;
}
public string Message { get; }
}
The controller enqueues a new job in the task queue.
[HttpGet]
public IActionResult EnqueueJob()
{
var job = new Job("Hello World");
this.taskQueue.QueueBackgroundWorkItem(job);
return Accepted();
}
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private ConcurrentQueue<Job> jobs = new ConcurrentQueue<Job>();
private SemaphoreSlim signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(Job job)
{
jobs.Enqueue(job);
signal.Release();
}
public async Task<Job> DequeueAsync(CancellationToken cancellationToken)
{
await signal.WaitAsync(cancellationToken);
jobs.TryDequeue(out var job);
return job;
}
}
The IHostedService creates a new JobRunner for each job it dequeues. I'm using a IServiceScopeFactory here to have dependency injection available. JobRunner also has a lot more dependencies in the real code.
public class JobRunnerService : BackgroundService
{
private readonly IServiceScopeFactory serviceScopeFactory;
private readonly IBackgroundTaskQueue taskQueue;
public JobRunnerService(IServiceScopeFactory serviceScopeFactory, IBackgroundTaskQueue taskQueue)
{
this.serviceScopeFactory = serviceScopeFactory;
this.taskQueue = taskQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (stoppingToken.IsCancellationRequested == false)
{
var job = await taskQueue.DequeueAsync(stoppingToken);
using (var scope = serviceScopeFactory.CreateScope())
{
var serviceProvider = scope.ServiceProvider;
var runner = serviceProvider.GetRequiredService<JobRunner>();
runner.Run(job);
}
}
}
}
public class JobRunner
{
private readonly ILogger<JobRunner> logger;
private readonly IIdentityProvider identityProvider;
public JobRunner(ILogger<JobRunner> logger, IIdentityProvider identityProvider)
{
this.logger = logger;
this.identityProvider= identityProvider;
}
public void Run(Job job)
{
var principal = identityProvider.GetUserName();
logger.LogInformation($"{principal} started a new job. Message: {job.Message}");
}
}
public class IdentityProvider : IIdentityProvider
{
private readonly IHttpContextAccessor httpContextAccessor;
public IdentityProvider(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public string GetUserName()
=> httpContextAccessor.HttpContext.User.Identity.Name; // throws NullReferenceException
}
Now, when sending a request, a NullReferenceException is thrown in JobRunner.Run() because httpContextAccessor.HttpContext is null.
What I've tried
I haven't had a good idea yet how to approach this problem. I know that it would be possible to copy the necessary information from the HttpContext, but don't know how to make them available to dependency injection services.
I thought that maybe I could create a new IServiceProvider that uses the services of an old one, but replaces the implementation for IHttpContextAccesor, but it does not seem to be possible.
How can I use the HttpContext in the background task although the response has been completed?

How to depenency injection/register open generic types

I have a microservices architecture using simple injector in each service. The services communicates through Azure Service Bus. I'm currently trying to find a way to implement a generic solution/library for interacting with Azure Service Bus. The library is the core infrastructure of the services and has a topic publisher (for pushing events /messages to azure) and a subscriber (for listening to messages from azure).
Besides that I have a common interface for the events /messages containing an ID and time stamp for creation. I also have a generic interface for event handlers IEventHandler<T> where T : IEvent. Now my problem is, how do I best keep my composition root separated from the rest of the code while still being able to register a set of handlers for the different types of events in a given service?
Reading the docs for simple injector suggests a factory or something like that, but my interface is generic and the factory is not which makes public IEventHandler GetHandler (Type eventType) illegal...
UPDATE: Added code
Publishing:
public interface IEventPublisher
{
Task PublishAsync(IEvent #event);
}
public class EventPublisher : IEventPublisher
{
private readonly ITopicClient topicClient;
public EventPublisher(ITopicClient topicClient)
{
this.topicClient = topicClient;
}
public async Task PublishAsync(IEvent #event)
{
try
{
var json = JsonConvert.SerializeObject(#event);
var message = new Message()
{
Body = Encoding.UTF8.GetBytes(json),
PartitionKey = nameof(#event),
MessageId = #event.Id.ToString()
};
message.UserProperties.Add("Type", #event.GetType().FullName);
await topicClient.SendAsync(message);
}
catch (Exception e)
{
//Handle error
}
}
}
Handling events:
public interface IEventHandler<T> where T : IEvent
{
void HandleEvent(T #event);
}
public interface IEventSubscriber
{
//Currently empty, might need some method for registration of handlers?
}
public class EventSubscriber : IEventSubscriber
{
private readonly ISubscriptionClient subscriptionClient;
public EventSubscriber(ISubscriptionClient subscriptionClient, )
{
this.subscriptionClient = subscriptionClient;
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
AutoComplete = false
};
this.subscriptionClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
var context = exceptionReceivedEventArgs.ExceptionReceivedContext;
logger.Error($"Message handler encountered an exception {exceptionReceivedEventArgs.Exception}.");
logger.Error("Exception context for troubleshooting:");
logger.Error($"- Endpoint: {context.Endpoint}");
logger.Error($"- Entity Path: {context.EntityPath}");
logger.Error($"- Executing Action: {context.Action}");
return Task.CompletedTask;
}
private async Task ProcessMessagesAsync(Message message, CancellationToken cancellationToken)
{
JsonConvert.DeserializeObject<BankDataChangedEvent>(Encoding.UTF8.GetString(message.Body));
// HERE I NEED SOME CODE TO FETCH/FIND THE RIGHT HANDLER FOR THE EVENT TYPE
await subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
}
Most of the pulisher and subscriber are boilerplate code from Microsoft docs for Azure Service Bus with .Net - only slightly modified.
Maybe I know what do you want.
Do you have something like this?
internal sealed class CommonEventConsumer :
IConsumer<Event1>,
IConsumer<Event2>
{
private readonly ISomeService _someService;
public CommonEventConsumer(ISomeService someService)
{
_someService = someService;
}
public async Task HandleEventAsync(Event1 eventMessage)
{
await _someService.DoSomeThing1(eventMessage);
}
public async Task HandleEventAsync(Event2 eventMessage)
{
await _someService.DoSomeThing2(eventMessage);
}
}
where IConsumer<Tevent> is global interface which has method HandleEventAsync(Tevent event message); and CommonEventConsumer is average consumer in any of your microservice.
Also the publisher is like:
public sealed class EventPublisher : IEventPublisher
{
public async Task PublishAsync<T>(T eventMessage)
{
var subscriptions = DependencyResolver.ResolveAll<IConsumer<T>>();
foreach (var subscription in subscriptions)
{
await subscription.HandleEventAsync(eventMessage);
}
}
}
where I resolve all event subscribers and push messages to them.
If yes, then I have the same structure in my application, and my SimpleInjector registration for eventConsumers look's like that:
private static void RegisterConsumers(Container container)
{
container.Register<IEventPublisher, EventPublisher>(Lifestyle.Scoped);
container.Collection.Register(typeof(IConsumer<>), new[] {
typeof(CommonConsumer),
typeof(MeasurementEventConsumer),
typeof(PartRepairEventConsumer),
typeof(OrderItemEventConsumer),
typeof(OrderTaskEventConsumer),
typeof(OrderItemWorkStatusEventConsumer),
typeof(OrderItemTaskEventConsumer),
typeof(OrderTaxEventConsumer)
});
}

Tenant session is lost in IRealTimeNotifier

We are trying to achieve a scenario where user can subscribe to notifications via channels.
For example, user can subscribe to notifications via Email, SMS, Real Time Notifications, etc.
For now, in order to achieve email notifications, I am implementing the IRealTimeNotifier in this way:
public class EmailNotifier : IRealTimeNotifier, ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly ISettingManager _settingManager;
public IAbpSession AbpSession { get; set; }
public EmailNotifier(IEmailSender emailSender, ISettingManager settingManager, IAbpSession abpSession)
{
this._emailSender = emailSender;
this._settingManager = settingManager;
this.AbpSession = NullAbpSession.Instance;
}
public async Task SendNotificationsAsync(UserNotification[] userNotifications)
{
List<Task> sendEmails = new List<Task>();
string fromAddress = this._settingManager.GetSettingValueForTenant(EmailSettingNames.Smtp.UserName, Convert.ToInt32(this.AbpSession.TenantId));
foreach (UserNotification notification in userNotifications)
{
notification.Notification.Data.Properties.TryGetValue("toAddresses", out object toAddresses);
notification.Notification.Data.Properties.TryGetValue("subject", out object sub);
notification.Notification.Data.Properties.TryGetValue("body", out object content);
notification.Notification.Data.Properties.TryGetValue("toAddresses", out object toAddresses);
List<string> toEmails = toAddresses as List<string> ;
string subject = Convert.ToString(sub);
string body = Convert.ToString(content);
toEmails.ForEach(to =>
{
sendEmails.Add(this._emailSender.SendAsync(fromAddress, to, subject, body));
});
}
await Task.Run(() =>
{
sendEmails.ForEach(async task =>
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception)
{
// TODO #1: Add email to background job to be sent later.
// TODO #2: Add circuit breaker to email sending mechanism
/*
Email sending is failing because of two possible reasons.
1. Email addresses are wrong.
2. SMTP server is down.
3. Break the circuit while the SMTP server is down.
*/
// TODO #3 (Optional): Notify tenant admin about failure.
// TODO #4: Remove throw statement for graceful degradation.
throw;
}
});
});
}
}
The problem is with the IAbpSession, whether I inject it via property or constructor, at the time of execution of this notifier, the response has already been returned and the TenantId in this session is null, so the email is being sent with host configurations and not tenant configuration.
Similarly, I need to implement IRealTimeNotifier for SMS. I think I can reuse the SignalRRealTimeNotifier from ABP, but the tenantId in session is being set to null.
This is where the publisher is being called:
public class EventUserEmailer : IEventHandler<EntityCreatedEventData<Event>>
{
public ILogger Logger { get; set; }
private readonly IEventManager _eventManager;
private readonly UserManager _userManager;
private readonly IAbpSession _abpSession;
private readonly INotificationPublisher _notiticationPublisher;
public EventUserEmailer(
UserManager userManager,
IEventManager eventManager,
IAbpSession abpSession,
INotificationPublisher notiticationPublisher)
{
_userManager = userManager;
_eventManager = eventManager;
_notiticationPublisher = notiticationPublisher;
_abpSession = abpSession;
Logger = NullLogger.Instance;
}
[UnitOfWork]
public virtual void HandleEvent(EntityCreatedEventData<Event> eventData)
{
// TODO: Send email to all tenant users as a notification
var users = _userManager
.Users
.Where(u => u.TenantId == eventData.Entity.TenantId)
.ToList();
// Send notification to all subscribed uses of tenant
_notiticationPublisher.Publish(AppNotificationNames.EventCreated);
}
}
Can anybody recommend a better way? Or point anything out that we are doing wrong here.
I have not thought about how to handle DI of these particular notifiers yet. For testing purposes, I have given a named injection in my module like this:
IocManager.IocContainer.Register(
Component.For<IRealTimeNotifier>()
.ImplementedBy<EmailNotifier>()
.Named("Email")
.LifestyleTransient()
);
Current ABP version: 3.7.1
This is the information I have until now. If anything is needed, you can ask in comments.
// Send notification to all subscribed uses of tenant
_notificationPublisher.Publish(AppNotificationNames.EventCreated);
If you publish notifications like this, ABP's default implementation enqueues a background job.
There is no session in a background job.
You can get the tenantId like this:
var tenantId = userNotifications[0].Notification.TenantId;
using (AbpSession.Use(tenantId, null))
{
// ...
}

Google OAuth Data Storage for tokens

Our app needs to let users sync sync their Google calendar on to our internal events module but I'm stuck on doing the authorization and storing it on to our DB. I was following this sample Google OAuth for reference but in that sample it was stated to implement your own DataStore that uses EF, I went ahead and tried it but I can't seem to make it to work and I've been stuck for a couple of weeks now, any help will be greatly appreciated. Thanks!
I had something like this before:
public class TokenDataStore : IDataStore
{
private readonly IUnitOfWork _unitOfWork;
private readonly IUserRepository _userRepository;
private readonly IBusinessRepository _businessRepository;
public TokenDataStore(IUnitOfWork unitOfWork, IUserRepository userRepository, IBusinessRepository businessRepository)
{
this._unitOfWork = unitOfWork;
this._userRepository = userRepository;
this._businessRepository = businessRepository;
}
//Todo: Add IdataStore Members
public static string GenerateStoredKey(string key, Type t)
{
return string.Format("{0}-{1}", t.FullName, key);
}
}
Some more information:
- MVC 4
-.Net 4.5
- EF 5
- Google APIs
EDIT
Ok, so I made some progress this past couple of hours, I was finally able to make my own AppFlowMetaData
private readonly IAuthorizationCodeFlow flow;
private readonly GoogleAPI _API;
private readonly IGoogleAPIRepository _googleAPIRepository;
private readonly IUnitOfWork _unitOfWork;
public AppFlowMetadata(GoogleAPI API, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
{
this._API = API;
this._googleAPIRepository = googleAPIRepository;
this._unitOfWork = unitOfWork;
this.flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = ConfigurationManager.AppSettings["GP_Key"],
ClientSecret = ConfigurationManager.AppSettings["GP_Secret"]
},
Scopes = new[] { CalendarService.Scope.Calendar },
DataStore = new TokenDataStore(_API, _googleAPIRepository, _unitOfWork)
});
}
also my own data store class:
private readonly IGoogleAPIRepository _googleAPIRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly GoogleAPI _model;
public TokenDataStore(GoogleAPI model, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
{
this._googleAPIRepository = googleAPIRepository;
this._unitOfWork = unitOfWork;
this._model = model;
}
Now I encounter a problem during the call back. So, when a user signs in using their account, in my data store class, I save the token as a string in the database and when the code gets to the part where I get the token from my model the data type passed is a string and not the usual Token Response. Here is the complete code for my data store:
public class TokenDataStore : IDataStore
{
private readonly IGoogleAPIRepository _googleAPIRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly GoogleAPI _model;
public TokenDataStore(GoogleAPI model, IGoogleAPIRepository googleAPIRepository, IUnitOfWork unitOfWork)
{
this._googleAPIRepository = googleAPIRepository;
this._unitOfWork = unitOfWork;
this._model = model;
}
public System.Threading.Tasks.Task StoreAsync<T>(string key, T value)
{
var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
if(serialized.Contains("access_token"))
{
_model.TokenString = serialized;
_googleAPIRepository.Save(_model, EntityState.Modified);
_unitOfWork.Commit();
}
return TaskEx.Delay(0);
}
public System.Threading.Tasks.Task DeleteAsync<T>(string key)
{
_model.TokenString = "";
_googleAPIRepository.Save(_model, EntityState.Modified);
_unitOfWork.Commit();
return TaskEx.Delay(0);
}
public Task<T> GetAsync<T>(string key)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
try
{
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(_model.TokenString));
}
catch(Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
public System.Threading.Tasks.Task ClearAsync()
{
return TaskEx.Delay(0);
}
}
Kevin, it is not clear what isn't working..
Could be:
Tokens are not being stored in your database
Authentication is not happening when accessing users resources at later time
I assume it is the latter, so there are two challenges you need to fix:
Token expiration is making Database invalid
Tokens are not being fed back into googleauthenticator when accessing users resources
Things you need to do are to ensure you allow the access to be offline so the server has access to the resources while the user is not present or session out of scope. Read chapter "offline access" here how to do this: Using oAuth for WebApplications
Make sure you validate that the tokens are still valid when feeding them back into the authenticator. If not you need to use refresh token to renew authentication. Validating token
You will need to ensure the googleauthenticator is called with the tokens from you Database to ensure your code has access.
Hope I assumed the correct problem... ;)
You can use the FileDataStore (https://code.google.com/p/google-api-dotnet-client/source/browse/Src/GoogleApis.DotNet4/Apis/Util/Store/FileDataStore.cs), that stores its data in files and not EntityFramework.
Regarding your question - I didn't get it, what is your exact problem?
You need more details on how to inject that data store into your flow object? Or you help with EF? ... Please provide more information.
Try storing an object with the following parameters in your Entity Framework Repository.
public string access_token { get; set; }
public string token_type { get; set; }
public long? expires_in { get; set; }
public string refresh_token { get; set; }
public string Issued { get; set; }
I'm retrieving my tokens from an Web Api which contains an Entity Framework Repository. I retrieve my object as a JSON string. Use Google.Api.Json.NewtonsoftJsonSerializer to deserialize the Json string.
public Task<T> GetAsync<T>(string key)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
try
{
string dataStoreView = this.httpClient.Get(uri).ResponseBody;
tcs.SetResult(Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize<T>(dataStoreView));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}

Categories

Resources