I am unable to get FluentScheduler working in .Net Framework 4.5.2 Web api. Few days ago, I asked a similar question about scheduling through Console application and could get it to work with help but unfortunately facing issues with Web Api now. Below is the code.
[HttpPost]
[Route("Schedule")]
public IHttpActionResult Schedule([FromBody] SchedulerModel schedulerModel)
{
var registry = new Registry();
registry.Schedule<MyJob>().ToRunNow();
JobManager.Initialize(registry);
JobManager.StopAndBlock();
return Json(new { success = true, message = "Scheduled!" });
}
Below is the job I want to schedule which for now is just writing text to a file
public class SampleJob: IJob, IRegisteredObject
{
private readonly object _lock = new object();
private bool _shuttingDown;
public SampleJob()
{
HostingEnvironment.RegisterObject(this);
}
public void Execute()
{
lock (_lock)
{
if (_shuttingDown)
return;
//Schedule writing to a text file
WriteToFile();
}
}
public void WriteToFile()
{
string text = "Random text";
File.WriteAllText(#"C:\Users\Public\TestFolder\WriteText.txt", text);
}
public void Stop(bool immediate)
{
lock (_lock)
{
_shuttingDown = true;
}
HostingEnvironment.UnregisterObject(this);
}
Got this resolved finally. It turns out the issue was with my Registry class. I had to change it as follows.
public class ScheduledJobRegistry: Registry
{
public ScheduledJobRegistry(DateTime appointment)
{
//Removed the following line and replaced with next two lines
//Schedule<SampleJob>().ToRunOnceIn(5).Seconds();
IJob job = new SampleJob();
JobManager.AddJob(job, s => s.ToRunOnceIn(5).Seconds());
}
}
[HttpPost]
[Route("Schedule")]
public IHttpActionResult Schedule([FromBody] SchedulerModel schedulerModel)
{
JobManager.Initialize(new ScheduledJobRegistry());
JobManager.StopAndBlock();
return Json(new { success = true, message = "Scheduled!" });
}
Another point to note: I could get this to work but hosting Api in IIS makes it tricky because we have to deal with App Pool recycles, idle time etc. But this looks like a good start.
Related
I'm using Simple.OData.Client to query and update in our crm dynamics system.
But each query, insertion or update takes up to 10 seconds. It works like a charm on postman. That means that the server is not the problem.
Here is my Code:
Base Class
public abstract class CrmBaseDao<T> where T : class
{
protected ODataClient GetClient()
{
return new ODataClient(new ODataClientSettings(BuildServiceUrl())
{
Credentials = new NetworkCredential(Settings.Default.CrmUsername, Settings.Default.CrmPassword),
IgnoreUnmappedProperties = true
});
}
public async Task<IEnumerable<T>> GetAll()
{
var client = GetClient();
return await client.For<T>().FindEntriesAsync();
}
private string BuildServiceUrl()
{
return Settings.Default.CrmBaseUrl + "/api/data/v8.2/";
}
}
Derived class:
public void Insert(Account entity)
{
var task = GetClient()
.For<Account>()
.Set(ConvertToAnonymousType(entity))
.InsertEntryAsync();
task.Wait();
entity.accountid = task.Result.accountid;
}
public void Update(Account entity)
{
var task = GetClient()
.For<Account>()
.Key(entity.accountid)
.Set(ConvertToAnonymousType(entity))
.UpdateEntryAsync();
task.Wait();
}
private object ConvertToAnonymousType(Account entity)
{
return new
{
entity.address1_city,
entity.address1_fax,
entity.address1_line1,
entity.address1_postalcode,
entity.address1_stateorprovince,
entity.he_accountnumber,
entity.name,
entity.telephone1,
entity.telephone2
};
}
public async Task<Account> GetByHeAccountNumber(string accountNumber)
{
return await GetClient().For<Account>()
.Filter(x => x.he_accountnumber == accountNumber)
.FindEntryAsync();
}
The call:
private void InsertIDocsToCrm()
{
foreach (var file in GetAllXmlFiles(Settings.Default.IDocPath))
{
var sapAccountEntity = GetSapAccountEntity(file);
var crmAccountEntity = AccountConverter.Convert(sapAccountEntity);
var existingAccount = crmAccountDao.GetByHeAccountNumber(crmAccountEntity.he_accountnumber);
existingAccount.Wait();
if (existingAccount.Result != null)
{
crmAccountEntity.accountid = existingAccount.Result.accountid;
crmAccountDao.Update(crmAccountEntity);
}
else
crmAccountDao.Insert(crmAccountEntity);
}
}
This whole function takes a very long time (30 sec+)
Is there any chance to speed that up?
Additionaly it does take a lot of memory?!
Thanks
I suspect this might have to do with a size of schema. I will follow the issue you opened on GitHub.
UPDATE. I ran some benchmarks mocking server responses, and processing inside Simple.OData.Client took just milliseconds. I suggest you run benchmarks on a server side. You can also try assigning metadata file references to MetadataDocument property of ODataClientSettings.
I'm building a C# UWP program (using MvvMLight framework) to run on a Raspberry Pi running Windows 10 Iot.
I have a method which downloads data from a SQL database via a WCF connection, I can see from debugging the WCF service is working, and the data downloads.
I'm puzzled why I can call this method two different ways, one updates the UI, one doesn't. Please can anyone explain why?
public class TagRegStep2ViewModel : ViewModelBase
{
private ObservableCollection<EmployeeName> _Employees;
private string _OnScreenInstructions;
public AsyncRelayCommand StartWizardCommand { get; private set; }
public TagRegStep2ViewModel()
{
// Initialise Messenger
Messenger.Default.Register<InitialiseStepMessage>(this, (msg) => InitialiseStep(msg.Step));
// Initialise Commands
StartWizardCommand = new AsyncRelayCommand(() => GetData(), () => true);
//Set dummy default value
OnScreenInstructions = "Default Value";
}
// I can see data is downloaded from WCF service and the ObservableCollection updates, but this isn't reflected in the UI.
public void InitialiseStep(ViewModelBase vm)
{
if (vm.GetType() == this.GetType())
{
Debug.WriteLine("InitialiseStep() called");
StartWizardCommand.TryExecute();
}
}
// Method that retrieves data from WCF service.
// When called from a relay command, it updates the ObservableCollection and UI.
// When called from a MessengerCommand it updates the ObservableCollection but not the UI. Why???
public async Task GetData()
{
Debug.WriteLine("Getting data...");
OnScreenInstructions = "Loading Employees";
await Task.Delay(25);
var emps = await LoadEmployees(); //not posted this method, but it works
Employees = new ObservableCollection<EmployeeName>(emps);
OnScreenInstructions = "Please select an employee.";
await Task.Delay(25);
Debug.WriteLine("ObservableCollection Count :" + Employees.Count);
}
public ObservableCollection<EmployeeName> Employees
{
get { return _Employees; }
set { Set(() => Employees, ref _Employees, value); }
}
public string OnScreenInstructions
{
get { return _OnScreenInstructions; }
set { Set(() => OnScreenInstructions, ref _OnScreenInstructions, value); }
}
}
Seeking some input on a behaviour I'm noticing in my code below. This is my first attempt at async/await using Xamarin Forms and I have perused hundreds of posts, blogs and articles on the subject including the writings from Stephen Cleary on async from constructors and best practices to avoid locking. Although I am using a MVVM framework I assume my issue is more generic than that so I'll ignore it for the moment here.
If I am still missing something or there are ways to improve what I'm trying to do ... happy to listen and learn.
At a high level the logic is as follows:
Application starts and initialises
During initialisation verify database exist and if not - create the SQLite DB. Currently I force this every time to simulate a new application and pre-populate it with some sample data for development purposes
After initialisation completed load results set and display
This works most of the time but I have noticed 2 infrequent occurrences due to the async handling of the database initialisation and pre-populating:
Occasionally not all sample records created are displayed once the app started up - I assume this is because the pre-population phase has not completed when the results are loaded
Occasionally I get an error that one of the tables have not been created - I assume this is because the database initialisation has not completed when the results are loaded
The code - simplified to show the flow during initialisation and startup:
----------- VIEW / PAGE MODEL ----------------
public class MyListItemsPageModel
{
private ObservableRangeCollection<MyListItem> _myListItems;
private Command loadItemsCommand;
public MyListItemsPageModel()
{
_myListItems = new ObservableRangeCollection<MyListItem>();
}
public override void Init(object initData)
{
if (LoadItemsCommand.CanExecute(null))
LoadItemsCommand.Execute(null);
}
public Command LoadItemsCommand
{
get
{
return loadItemsCommand ?? (loadItemsCommand = new Command(async () => await ExecuteLoadItemsAsyncCommand(), () => { return !IsBusy; }));
}
}
public ObservableRangeCollection<MyListItem> MyListItems {
get { return _myListItems ?? (_myListItems = new ObservableRangeCollection<MyListItem>()); }
private set {
_myListItems = value;
}
}
private async Task ExecuteLoadItemsAsyncCommand() {
if (IsBusy)
return;
IsBusy = true;
loadItemsCommand.ChangeCanExecute();
var _results = await MySpecificDBServiceClass.LoadAllItemsAsync;
MyListItems = new ObservableRangeCollection<MyListItem>(_results.OrderBy(x => x.ItemName).ToList());
IsBusy = false;
loadItemsCommand.ChangeCanExecute();
}
}
----------- DB Service Class ----------------
// THERE IS A SPECIFIC SERVICE LAYER BETWEEN THIS CLASS AND THE PAGE VIEW MODEL HANDLING THE CASTING OF TO THE SPECIFIC DATA TYPE
// public class MySpecificDBServiceClass : MyGenericDBServiceClass
public class MyGenericDBServiceClass<T>: IDataAccessService<T> where T : class, IDataModel, new()
{
public SQLiteAsyncConnection _connection = FreshIOC.Container.Resolve<ISQLiteFactory>().CreateConnection();
internal static readonly AsyncLock Mutex = new AsyncLock();
public DataServiceBase()
{
// removed this from the constructor
//if (_connection != null)
//{
// IsInitialized = DatabaseManager.CreateTableAsync(_connection);
//}
}
public Task<bool> IsInitialized { get; private set; }
public virtual async Task<List<T>> LoadAllItemsAsync()
{
// Temporary async/await initialisation code. This will be moved to the start up as per Stephen's suggestion
await DBInitialiser();
var itemList = new List<T>();
using (await Mutex.LockAsync().ConfigureAwait(false))
{
itemList = await _connection.Table<T>().ToListAsync().ConfigureAwait(false);
}
return itemList;
}
}
----------- DB Manager Class ----------------
public class DatabaseManager
{
static double CURRENT_DATABASE_VERSION = 0.0;
static readonly AsyncLock Mutex = new AsyncLock();
private static bool IsDBInitialised = false;
private DatabaseManager() { }
public static async Task<bool> CreateTableAsync(SQLiteAsyncConnection CurrentConnection)
{
if (CurrentConnection == null || IsDBInitialised)
return IsDBInitialised;
await ProcessDBScripts(CurrentConnection);
return IsDBInitialised;
}
private static async Task ProcessDBScripts(SQLiteAsyncConnection CurrentConnection)
{
using (await Mutex.LockAsync().ConfigureAwait(false))
{
var _tasks = new List<Task>();
if (CURRENT_DATABASE_VERSION <= 0.1) // Dev DB - recreate everytime
{
_tasks.Add(CurrentConnection.DropTableAsync<Table1>());
_tasks.Add(CurrentConnection.DropTableAsync<Table2>());
await Task.WhenAll(_tasks).ConfigureAwait(false);
}
_tasks.Clear();
_tasks.Add(CurrentConnection.CreateTableAsync<Table1>());
_tasks.Add(CurrentConnection.CreateTableAsync<Table2>());
await Task.WhenAll(_tasks).ConfigureAwait(false);
_tasks.Clear();
_tasks.Add(UpgradeDBIfRequired(CurrentConnection));
await Task.WhenAll(_tasks).ConfigureAwait(false);
}
IsDBInitialised = true;
}
private static async Task UpgradeDBIfRequired(SQLiteAsyncConnection _connection)
{
await CreateSampleData();
return;
// ... rest of code not relevant at the moment
}
private static async Task CreateSampleData()
{
IDataAccessService<MyListItem> _dataService = FreshIOC.Container.Resolve<IDataAccessService<MyListItem>>();
ObservableRangeCollection<MyListItem> _items = new ObservableRangeCollection<MyListItem>(); ;
_items.Add(new MyListItem() { ItemName = "Test 1", ItemCount = 14 });
_items.Add(new MyListItem() { ItemName = "Test 2", ItemCount = 9 });
_items.Add(new MyListItem() { ItemName = "Test 3", ItemCount = 5 });
await _dataService.SaveAllItemsAsync(_items).ConfigureAwait(false);
_items = null;
_dataService = null;
IDataAccessService<Sample> _dataService2 = FreshIOC.Container.Resolve<IDataAccessService<AnotherSampleTable>>();
ObservableRangeCollection<Sample> _sampleList = new ObservableRangeCollection<Sample>(); ;
_sampleList.Add(new GuestGroup() { SampleName = "ABC" });
_sampleList.Add(new GuestGroup() { SampleName = "DEF" });
await _dataService2.SaveAllItemsAsync(_sampleList).ConfigureAwait(false);
_sampleList = null;
_dataService2 = null;
}
}
In your DataServiceBase constructor, you're calling DatabaseManager.CreateTableAsync() but not awaiting it, so by the time your constructor exits, that method has not yet completed running, and given that it does very little before awaiting, it's probably barely started at that point. As you can't effectively use await in a constructor, you need to remodel things so you do that initialisation at some other point; e.g. perhaps lazily when needed.
Then you also want to not use .Result/.Wait() whenever possible, especially as you're in an async method anyway (e.g. ProcessDBScripts()), so instead of doing
var _test = CurrentConnection.DropTableAsync<MyListItem>().Result;
rather do
var _test = await CurrentConnection.DropTableAsync<MyListItem>();
You also don't need to use Task.Run() for methods that return Task types anyway. So instead of
_tasks.Add(Task.Run(() => CurrentConnection.CreateTableAsync<MyListItem>().ConfigureAwait(false)));
_tasks.Add(Task.Run(() => CurrentConnection.CreateTableAsync<AnotherSampleTable>().ConfigureAwait(false)));
just do
_tasks.Add(CurrentConnection.CreateTableAsync<MyListItem>()));
_tasks.Add(CurrentConnection.CreateTableAsync<AnotherSampleTable>()));
sellotape has correctly diagnosed the code problem: the constructor is starting an asynchronous method but nothing is (a)waiting for it to complete. A simple fix would be to add await IsInitialized; to the beginning of LoadAllItemsAsync.
However, there's also a design problem:
After initialisation completed load results set and display
That's not possible on Xamarin, or any other modern UI platform. You must load your UI immediately and synchronously. What you should do is immediately display a splash/loading page and start the asynchronous initialization work. Then, when the async init is completed, update your VM/UI with your "real" page. If you just have LoadAllItemsAsync await IsInitialized, then your app will sit there for some time showing the user zero data before it "fills in".
You may find my NotifyTask<T> type (available on NuGet) useful here if you want to show a splash/spinner instead of zero data.
So I'm trying to use Signalr with the Twitter Streaming API, and specifically, for this I'm using the Tweetinvi C# API (http://tweetinvi.codeplex.com/).
The purpose of the app is to stream tweets to a page in realtime filtered with certain keywords.
The TweetInvi library works a treat, and I have a command line application successfully printing out tweets with certain keywords in.
The basic outline of my usage is as follows:
I have an MVC web app with a single page, with a text input and a button (for updating filters) it then calls the Hub method in the Signalr Hub, to start the stream if there isn't one already present and Stops it on a second button click.
All this is working fine, except when it comes to the signalr part.
public class TweetHub : Hub
{
private IStreamManager _streamManager;
public void AddTweet(String tweet, double lat, double lon)
{
Clients.All.addTweet(tweet, lat, lon);
}
public void StartStream(String[] filters)
{
string accessToken = ConfigurationManager.AppSettings["AccessToken"];
string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);
if (_streamManager != null && _streamManager.StreamIsOpen())
{
_streamManager.StopStream();
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
else if (_streamManager != null && !_streamManager.StreamIsOpen())
{
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
else
{
_streamManager = new StreamManager();
_streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
}
}
public void StopStream()
{
if (_streamManager != null && _streamManager.StreamIsOpen())
{
_streamManager.StopStream();
}
}
}
That is the code for my Signalr Hub. As I said, using js I can trigger the start and stop stream methods fine.
This is the code for my StreamManager class:
public class StreamManager : IStreamManager
{
private StreamClient _streamClient;
private bool _streamOpen = false;
public void StartStream(IToken token, String[] filters, Action<ITweet> action)
{
if (_streamClient == null)
_streamClient = new StreamClient(token, filters, new FilteredStream());
_streamClient.StartStream(action);
_streamOpen = true;
}
public void StopStream()
{
if (_streamClient != null)
{
_streamClient.StopStream();
_streamOpen = false;
}
}
public bool StreamIsOpen()
{
return _streamOpen;
}
public void Dispose()
{
if (_streamOpen)
{
StopStream();
}
_streamClient.Dispose();
_streamClient = null;
}
}
The code for my StreamClient class:
public class StreamClient : IStreamClient
{
private IFilteredStream _filteredStream;
private IToken _token;
private bool _streamOpen = false;
public StreamClient(IToken token, String[] filters, IFilteredStream filteredStream)
{
_token = token;
_filteredStream = filteredStream;
AddFilters(filters);
}
private void AddFilters(String[] filters)
{
for (int i = 0; i < filters.Length; ++i)
{
_filteredStream.AddTrack(filters[i]);
}
}
public void StartStream(Action<ITweet> action)
{
_filteredStream.StartStream(_token, action);
_streamOpen = true;
}
public void StartStream(Func<ITweet, bool> predicateFunc)
{
_filteredStream.StartStream(_token, predicateFunc);
_streamOpen = true;
}
public void StopStream()
{
_filteredStream.StopStream();
_streamOpen = false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
if (_streamOpen)
{
_filteredStream.StopStream();
_filteredStream = null;
_token = null;
}
}
}
This code above, is where it makes a call to the Tweetinvi library directly.
My problem is that when I pass the Hub method into the StreamManager's StartStream method as an Action parameter, the AddTweet method never gets hit.
As I said, this all works fine, when using a command prompt application as a client instead, and using this code:
static void Main(string[] args)
{
string accessToken = ConfigurationManager.AppSettings["AccessToken"];
string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);
String[] filters = new string[2]
{
"test",
"twitter"
};
StreamClient streamClient = new StreamClient(token, filters, new FilteredStream());
streamClient.StartStream(tweet => TestMethod());
}
public static void TestMethod()
{
Console.WriteLine("test");
}
This works perfectly and prints out tweets with those keywords as they are received.
This leads me to believe that is a problem with the way I am using Signalr, that the signalr method is never getting hit, because the stream definitely gets opened, I just have a sneaky suspicion that it is something to do with the lifetime of the hub and the way I am using it.
I suspect this because, although the StartStream Method in my Hub gets called fine, and updates the button being clicked, when I think click again to call StopStream, the StopStream method gets hit, but my "_streamManager" member variable is null, which it shouldn't be IF the hub maintains state during it's lifetime, which I guess it doesn't.
Either that or it's being disposed of and then the stream wouldnt exist anymore anyway.
I don't really have enough experience with Signalr to properly debug.
Thanks in advance for any help.
Instances of a hub class are transient. That means that SignalR creates an instance of the hub class each time a method in that hub class is called or when a connection event occurs (e.g. onConnected, onDisconnected). When the method being called is done doing its work, that hub instance is disposed.
Read this: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#transience
So you should not try to maintain state information about a connection in your hub class. In your case that would be "_streamManager".
So I think you should move all your business logic to another class (that doesn't derive from Hub).Encapsulate them in methods and call them from your signalR methods.
If you need to call a method in the hub from your sever code, you should get a hub context first.
See how to do that here: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub
Hope this helps!
I am trying to implement a simple Windows 8 C# XAML application where there are two calls made to access single web service one from project to load and display data and other to display notification.
Since there are two calls made for same web service I want that if one call is already made to the service the other call should wait and use the same response from the first call.
How can i achieve this kind of functionality? I have not added any code since there is no code that i have written for this. I am just trying to think first and then will I code.
Please let me know I can get some help for this kind of project structure?
You can do this by caching the Task that's currently downloading and not starting the download again if there is a cached Task:
private volatile Task<string> m_cachedWebServiceTask;
async Task<string> AccessWebServiceAsync()
{
var task = m_cachedWebServiceTask;
if (task == null)
task = m_cachedWebServiceTask = DownloadFromWebServiceAsync();
try
{
return await task;
}
finally
{
m_cachedWebServiceTask = null;
}
}
Note that this code has a race condition: if you call AccessWebServiceAsync() twice at the same time, there is a small chance DownloadFromWebServiceAsync() will be called twice too. But since this in only an optimization, I think that shouldn't be a problem. If it is a problem for you, you would need to guard the access to the field by a lock.
As I had the feeling that this problem needs further attention and it's solution could still be optimized, I decided to post another approach. The OP is mostly a problem about leveraging the following 3 scopes of requirements: user experience within the application, the application's internal requirements and the web service's loading with multiple requests.
The application needs to make an initial request to load the data.
When he asks for it, the user expects to get the results with the latest updates.
On the other side, it makes no initiate a large series of calls to the web service within a very short moment of time.
So, managing what happens in this very short moment of time it's actually the solution to the problem.
On the client side, the Service1Client class:
public partial class Service1Client
{
// default time buffer value
int _timeBuffer = 100;
// a time buffer used for returning the same response
public int TimeBuffer
{
get { return _timeBuffer; }
set { _timeBuffer = value; }
}
// the start and end time of the web service request
static DateTime _start, _end;
// a volatile static variable to store the response in the buffering time
volatile static string _response;
// used for blocking other threads until the current thread finishes it's job
object _locker = new object();
public async Task<string> GetResponseData()
{
return await Task.Factory.StartNew<string>(() =>
{
lock (_locker)
{
if (DateTime.Now >= _end.AddMilliseconds(TimeBuffer))
{
_start = DateTime.Now;
var async = GetDataAsync();
_response = async.Result;
_end = DateTime.Now;
}
}
return _response;
});
}
}
The console application used for testing:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
client.TimeBuffer = 150;
Console.WriteLine(client.GetResponseData().Result);
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}
As a remark, note that, for the reason of a clear sample, I decided to change the returned type of the GetDate WCF service's method from DateTime to string.
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData();
}
public class Service1 : IService1
{
public string GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now.ToString();
}
}
For your scenario, a feasible idea would be to extend the service class.
The IService1 interface definition:
[ServiceContract]
public interface IService1
{
[OperationContract]
DateTime GetData();
}
The Service1 class definition:
public class Service1 : IService1
{
public DateTime GetData()
{
System.Threading.Thread.Sleep(5000);
return DateTime.Now;
}
}
On the client side, extend the Service1Client class definition and add a new method:
public partial class Service1Client
{
static bool _isOpen;
static DateTime? _cachedResponse;
object _locker = new object();
public DateTime GetResponseData()
{
if (!_isOpen)
{
if (!_cachedResponse.HasValue)
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
}
else
{
Task.Factory.StartNew<DateTime>(() =>
{
lock (_locker)
{
_isOpen = true;
_cachedResponse = GetData();
_isOpen = false;
}
return _cachedResponse.Value;
});
}
}
return _cachedResponse.Value;
}
}
Test it:
class Program
{
static void Main(string[] args)
{
while (true)
{
var client = new ServiceReference1.Service1Client();
Console.WriteLine(client.GetResponseData());
if (Console.ReadKey().Key == ConsoleKey.Enter)
break;
}
}
}