Make 1 method async as well as sync in C# - c#

I have a very large set of methods that I want to make asynchronous accessible. The methods are complex and sometimes very long. The approach I can think off is copying all the existing methods and make them async. But when I have to make some changes I have to edit 2 methods. Is there a better approach with the code in one place?
As you can see the code is basically the same. Is it possible to combine those 2 methods into 1?
public async Task ManufacturersToWebshopAsync(HttpContext httpContext, ManufacturerNopServiceClient manufacturerNopServiceClient, bool onlyChanged = false, bool includeNight = false)
{
Log.Verbose("ManufacturersToWebshop", "Start", "");
// client
if (manufacturerNopServiceClient == null)
{
var host = httpContext.Request.Url.Host;
manufacturerNopServiceClient = GetManufacturerNopServiceClient(host);
}
var manufacturers = _manufacturerService.GetAllManufacturers();
if (onlyChanged && includeNight)
{
manufacturers = manufacturers.Where(x => x.State == State.Changed || x.State == State.Night).ToList();
}
else
{
if (onlyChanged)
{
manufacturers = manufacturers.Where(x => x.State == State.Changed).ToList();
}
if (includeNight)
{
manufacturers = manufacturers.Where(x => x.State == State.Night).ToList();
}
}
var tasks = new List<Task>();
var total = manufacturers.Count();
var count = 1;
foreach (var manufacturer in manufacturers)
{
Log.Information("ManufacturersToWebshop", "Manufacturer " + count + " van de " + total, "");
//tasks.Add(ManufacturerToWebshop(httpContext, manufacturer, manufacturerNopServiceClient));
await ManufacturerToWebshopAsync(httpContext, manufacturer, manufacturerNopServiceClient);
count++;
}
//await Task.WhenAll(tasks);
Log.Verbose("ManufacturersToWebshop", "End", "");
}
public void ManufacturersToWebshop(HttpContext httpContext, ManufacturerNopServiceClient manufacturerNopServiceClient, bool onlyChanged = false, bool includeNight = false)
{
Log.Verbose("ManufacturersToWebshop", "Start", "");
// client
if (manufacturerNopServiceClient == null)
{
var host = httpContext.Request.Url.Host;
manufacturerNopServiceClient = GetManufacturerNopServiceClient(host);
}
var manufacturers = _manufacturerService.GetAllManufacturers();
if (onlyChanged && includeNight)
{
manufacturers = manufacturers.Where(x => x.State == State.Changed || x.State == State.Night).ToList();
}
else
{
if (onlyChanged)
{
manufacturers = manufacturers.Where(x => x.State == State.Changed).ToList();
}
if (includeNight)
{
manufacturers = manufacturers.Where(x => x.State == State.Night).ToList();
}
}
var total = manufacturers.Count();
var count = 1;
foreach (var manufacturer in manufacturers)
{
Log.Information("ManufacturersToWebshop", "Manufacturer " + count + " van de " + total, "");
ManufacturerToWebshop(httpContext, manufacturer, manufacturerNopServiceClient);
count++;
}
Log.Verbose("ManufacturersToWebshop", "End", "");
}

Is it possible to combine those 2 methods into 1?
Not in a good way that works in all scenarios. There are hacks to write synchronous wrappers for asynchronous methods, and other hacks to write asynchronous wrappers for synchronous methods - but none of the options work in all scenarios. There is no generic, general-purpose solution for this problem.
I recommend that you consider what your method is doing, and decide whether it should be asynchronous or not. E.g., if it is doing I/O, then it should be asynchronous. Then, if the method should be asynchronous, just make it asynchronous (without a synchronous version).
If you're updating code, then this will require updating all the code that calls the new asynchronous method (and code that calls those methods, etc, etc). If this update will take too long to apply throughout the system, then (temporary) duplication of code is my recommended approach. However, if you don't control the calling code, then you might have to consider one of the hacks in the linked articles. Just be sure to think through the ramifications of each, and choose the one appropriate for your specific code.

Related

Generic TaskExecution Method with Variable Parameter functions & return types

I am trying to refactor Parallel Task Executor on 2 methods which executes on Variable Parameters & return types. can assume that both methods have same input parameters and return types but they can change.
For Eg: MyObject Task1(string,string,int), MyObject Task2(string,string,int) is valid
MyObject3 Task1(string,string,int, double), MyObject3 Task2(string,string,int,double) is valid too
currently, I am implementing it as follows. This works for one or two methods. But now I hit a case I need to do this for around 15 methods. How do I go about refactoring into its own methods and just calling it as RunTaskInParallel<ReturnTypeOfBothMethods>(Task1(params...), Task2(params...))
we can assume both methods (Task1, Task2) have same exact parameters & return types.
public async Task<UserPolicies> GetUserPoliciesAsync(string userId, string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch)
{
if (FlagManager.GetSettingValue<bool>(FlagConstants.EnableMyFeature, tenantId, logSettingValue: false)
&& userPoliciesToFetch.Contains(UserAssignedSettingType.UserPermission))
{
//Read tenant Status from cache or Backend.
var tenantStatus = await GetTenantStatusFromCacheOrBackendAsync(tenantId);
if (tenantStatus.IsFlighted && tenantStatus.IsMigrated.Equals(AAEConstants.TenantMigrationCompleted, StringComparison.OrdinalIgnoreCase)) //Tenant signedup & Migrated completely to AAE Flow
{
//This is the Part that needs to be repeated for different method types.
//Here its calling OtherService Method (GetUserPoliciesAsync) and custom Logic for Man in Middle method (GetAppDocFromCacheOrBackendAsync)
var taskList = new List<Task>()
{
_policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch)
};
//Preparing Default User teamsAppPerm document in case of BE request fail or tenantStatus.ResolutionModel == default.
TeamsAppPermissionPolicy teamsAppPermissionDocFromAAE = new TeamsAppPermissionPolicy();
if (tenantStatus.ResolutionModel.Equals(AAEConstants.TenantResolutionModelCustom, StringComparison.OrdinalIgnoreCase))
{
//only call AAE Cache or Backend for tenantStatus.ResolutionModel == Custom
taskList.Add(GetAppDocFromCacheOrBackendAsync(userId, tenantId));
}
var allTasks = Task.WhenAll(taskList);
await allTasks;
var otherPolicies = taskList[0].IsFaulted ?
new UserPolicies() : ((Task<UserPolicies>)taskList[0]).Result;
if (taskList.Count > 1) //Making sure we only read response if we hit AAE Backend. In which case tasklist length is 2.
{
teamsAppPermissionDocFromAAE = taskList[1].IsFaulted ?
new TeamsAppPermissionPolicy() : ((Task<TeamsAppPermissionPolicy>)taskList[1]).Result;
}
otherPolicies.AppsPermissionsPolicy = teamsAppPermissionDocFromAAE.ToMTTeamsAppPermissionPolicy().ToMTPolicyDocument();
//Wrap AAE TeamsApp doc response into other Policies
return otherPolicies;
}
else
{
//Tenant has not signed up or not migrated. Use existing PolicyService flow
return await _policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch);
}
}
else
{
//AAE feature is not enabled in ECS aka Kill Switch
return await _policyBackendProxy.GetUserPoliciesAsync(userId, tenantId, userPoliciesToFetch);
}
}
Here _policyBackendProxy is the original flow and I only interject its flow if certain conditions are met which are my if Statements. else go by previous flow.
This method returns UserPolicies object but there are few other methods I need to parallel execute old method or my corresponding new method but do not want to repeat the same logic for diff methods and return types.
Another similar method but returning diff type is below.
public async Task<AcmsSettings> GetTenantDefaultPoliciesAsync(string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch)
{
if (_flagManager.GetSettingValue<bool>(EcsSettingsConstants.EnableAppCatalogService, tenantId, logSettingValue: false)
&& userPoliciesToFetch.Contains(UserAssignedSettingType.AppsPermission))
{
var tenantStatus = await GetTenantStatusFromCacheOrBackendAsync(tenantId);
if (tenantStatus.IsFlighted
&& tenantStatus.IsMigrated.Equals(AAEConstants.TenantMigrationCompleted, StringComparison.OrdinalIgnoreCase)) //Tenant signedup & Migrated completely to AdminAppCatalog Flow
{
var taskList = new List<Task>();
if (tenantStatus.ResolutionModel.Equals(AAEConstants.TenantResolutionModelCustom, StringComparison.OrdinalIgnoreCase))
{
taskList.Add(GetTeamsAppDocFromCacheOrBackendAsync(null, tenantId));
}
//Preparing Default User teamsAppPerm document in case of BE request fail or tenantStatus.ResolutionModel == default.
TeamsAppPermissionPolicy teamsAppPermissionDocFromAAE = new TeamsAppPermissionPolicy();
var allTasks = Task.WhenAll(taskList);
await allTasks;
var otherPolicies = taskList[0].IsFaulted ?
new AcmsSettings() : ((Task<AcmsSettings>)taskList[0]).Result;
if (taskList.Count > 1) //Making sure we only read response if we hit AAE Backend. In which case tasklist length is 2.
{
teamsAppPermissionDocFromAAE = taskList[1].IsFaulted ?
new TeamsAppPermissionPolicy() : ((Task<TeamsAppPermissionPolicy>)taskList[1]).Result;
}
otherPolicies.AppsPermissionPolicy = teamsAppPermissionDocFromAAE.ToMTTeamsAppPermissionPolicy().ToMTPolicyDocument();
return otherPolicies;
}
else
{
return await _policyBackendProxy.GetTenantDefaultPoliciesAsync(tenantId, userPoliciesToFetch);
}
}
else
{
return await _policyBackendProxy.GetTenantDefaultPoliciesAsync(tenantId, userPoliciesToFetch);
}
}
The above method is similar to the first one except its taking diff parameters (string tenantId, IList<UserAssignedSettingType> userPoliciesToFetch) and returning diff type AcmsSettings.
I have to repeat this logic for every method in interface. I am certain there is a better and efficient way but not sure what is it.

How do I await this callback method to perform logic on the return data?

So I'm working in Silverlight right now unfortunately for the first time. I'm decently familiar with callbacks, but I'm not entirely sure how to convert this method to be synchronous to perform logic on the order data.
I've been frequently told that making this synchronous was ill-advised, but my goal is to check if certain fields have been modified in the XAML UI and are different from what exists in the database. Then prompt for a reason for the change. If there is a better way to go about this, I'd love to know.
I'm in Silverlight 5 with .Net Framework 4.0 in VS 2013
Thank you! Here's the async order provider:
public void GetOrder(string ordNumber, Action<Func<OrderLoadResults>> callback)
{
String exStr = String.Format("{0}.{1}() --> received an empty value for",
this.GetType().Name,
MethodBase.GetCurrentMethod().Name);
if (ordNumber == null)
{
throw new ArgumentNullException("ordNumber", exStr);
}
if (callback == null)
{
throw new ArgumentNullException("callback", exStr);
}
IOrderServiceAsync channel = CreateChannel();
AsyncCallback asyncCallback = ar => GetOrderCallback(callback, ar);
channel.BeginGetOrderByOrdNumber(ordNumber, asyncCallback.ThreadSafe(), channel);
}
And here's what I'm doing with it:
public List<ATMModifiedFieldModel> CheckForATMModifiedFields()
{
if (!_order.Stops.Items.Any(x => x.ModelState == ModelState.Modified))
{
return null;
}
List<StopModel> oldStop = new List<StopModel>();
Provider.OrderProvider orderProvider = new Provider.OrderProvider();
//Looking to convert this method to sync to pull the order info out to compare against
//orderProvider.GetOrder(_order.Item.OrdHdrNumber.ToString(),getResult => OnGetOrderComplete(getResult));
List<ATMModifiedFieldModel> modifiedFields = new List<ATMModifiedFieldModel>();
foreach (StopModel stop in _order.Stops.Items)
{
if (stop.ModelState == ModelState.Modified)
{
foreach (string ATMFieldName in Enum.GetNames(typeof(ATMFields)))
{
string beforeValue = "before value"; //Should check the value in the database
string afterValue = stop.GetType().GetProperty(ATMFieldName).GetValue(stop, null).ToString();
if (beforeValue != afterValue)
{
modifiedFields.Add(new ATMModifiedFieldModel(ATMFieldName, beforeValue, afterValue, stop.StpNumber, "Stop"));
}
}
}
}
return modifiedFields;
}

What's the right way to `subscribe`/`assign` to a Kafka topic using .NET `Confluent.Kafka`?

Task.Run(
() =>
{
try
{
var (topic, partitionOrNull, offsetOrNull) = target;
if (partitionOrNull == null && offsetOrNull == null) consumer.Subscribe(topic);
else
{
var partition = partitionOrNull ?? 0;
if (offsetOrNull != null) consumer.Assign(new TopicPartitionOffset(topic, partition, offsetOrNull.Value));
else consumer.Assign(new TopicPartition(topic, partition));
}
while (!cancellationToken.IsCancellationRequested)
{
var consumeResult = consumer.Consume(cancellationToken);
if (consumeResult.IsPartitionEOF) continue;
observer.OnNext((consumeResult.Offset.Value, consumeResult.Key, consumeResult.Value));
}
}
catch (Exception exception)
{
observer.OnError(exception);
}
},
cancellationToken);
return Task.FromResult<IDisposable>(consumer);
});
So there is a Kafka consumer (just a tiny wrapper around latest Confluent.Kafka) that covers both scenarios: subscribe with dynaimc rebalancing and assigning to a specified partition and/or offset. The problem is that when I specify only topic (partition == null && offset == null), consumer infinitely hangs without any progress. Am I misusing the framework or what's going on?
P.S. Specifying all three parameters accurately works prefectly fine.

Which one of these async calls is not like the other?

I suspect I have a deadlock issue, but it's an odd one that I can't rationalize. I have an API that needs to verify a few things in order to process the call. As part of the business logic, I might have to make more of those same calls as well. In this case, if a particular piece of data associated with an entity is not found, we attempt to use a backup (if one is configured), which requires checking other entities. Eventually, the code will hang.
Let's just dive into the code (comments highlight the calls in question).
API Controller:
public async Task<HttpResponseMessage> Get(int entityID, string content, bool? useBackUp = true)
{
//Some look-ups here, no issues at all
//This works, but it's this method that has an issue later in the process.
SystemEntity entityObj =
await BusinessLayer.GetSystemEntityAsync(SystemEntityID);
if (entityObj == null)
{
return new HttpResponseMessage
{
StatusCode = System.Net.HttpStatusCode.BadRequest,
Content = new StringContent("Entity is unavailable.")
};
}
string text = BusinessLayer.GetContentTextAsync(entityID
new List<string> {contentName}, useBackUp).Result.FirstOrDefault().Value;
if (text == null)
{
return new HttpResponseMessage {StatusCode = System.Net.HttpStatusCode.NoContent};
}
return new HttpResponseMessage
{
StatusCode = System.Net.HttpStatusCode.OK,
Content = new StringContent(text)
};
}
Business Layer:
public async Task<Dictionary<string, string>> GetContentTextAsync(int systemEntityID, List<string> contentNames, bool useBackUp)
{
Dictionary<string, string> records = new Dictionary<string, string>();
//We iterate for caching purposes
foreach (string name in contentNames)
{
string nameCopy = name;
string record = Cache.GetData(
string.Format("{0}_{1}_{2}", CONTENT, systemEntityID, name), () =>
DataLayer.GetCotnent(systemEntityID, nameCopy));
if (record == null && useBackUp)
{
List<int> entityIDs = new List<int> {systemEntityID};
int currentEntityID = systemEntityID;
//Here's that method again. This call seems to work.
SystemEntity currentEntity = await GetSystemEntityAsync(systemEntityID);
if (currentEntity != null && currentEntity.BackUpID.HasValue)
{
currentEntityID = (int) currentEntity.BackUpID;
}
while (!entityIDs.Contains(currentEntityID))
{
int id = currentEntityID;
record = Cache.GetData(
string.Format("{0}_{1}_{2}", CONTENT, systemEntityID, name), () =>
DataLayer.GetCotnent(id, nameCopy));
if (record != null) break;
entityIDs.Add(currentEntityID);
//This call seems to cause the deadlock
currentEntity = await GetSystemEntityAsync(currentEntityID);
if (currentEntity != null && currentEntity.BackUpID.HasValue)
{
currentEntityID = (int) currentEntity.UseBackupID;
}
}
}
if (record != null)
{
records.Add(name, record);
}
}
return records;
}
public async Task<SystemEntity> GetSystemEntityAsync(int systemEntityID)
{
SystemEntity systemEntity = await DataLayer.GetSystemEntity(
scc => scc.SystemEntityID == systemEntityID);
return systemEntity;
}
Data Layer:
public async Task<SystemEntity> GetSystemEntity(Expression<Func<SystemEntity, bool>> whereExpression)
{
using (EntityContext dbContext = createDbInstance())
{
//This is the last line that the debugger in VS 2013 brings me to. Stepping into this returns to whatever called the API method, waiting endlessly.
return await
dbContext.SystemEntity.Include(sc => sc.OtherEntity).Where(whereExpression).FirstOrDefaultAsync();
}
}
To recap: I call GetSystemEntityAsync three times. The first two times, it completes successfully. The third time, it hangs. If I comment out the first two calls so they don't run at all, the third one still hangs. If I remove the await and use just a normal FirstOrDefault in the return statement of the data layer method, then everything completes just fine.
Note: I have to keep the GetSystemEntityAsync method asynchronous. I cannot alter it to be synchronous.
What are the possible sources of the deadlock I'm encountering? I'm out of ideas on how to solve it.
Which one of these async calls is not like the other?
This one, I suspect:
string text = BusinessLayer.GetContentTextAsync(entityID
new List<string> {contentName}, useBackUp).Result.FirstOrDefault().Value;
Try changing it to this:
string text = (await BusinessLayer.GetContentTextAsync(entityID
new List<string> {contentName}, useBackUp)).FirstOrDefault().Value;
The possible source of the deadlock is described by Stephen Cleary in his "Don't Block on Async Code" blog post.

Combining latest from an observable of observables

Suppose I have a set of URIs that I am monitoring for availability. Each URI is either "up" or "down", and new URIs to monitor may be added to the system at any time:
public enum ConnectionStatus
{
Up,
Down
}
public class WebsiteStatus
{
public string Uri
{
get;
set;
}
public ConnectionStatus Status
{
get;
set;
}
}
public class Program
{
static void Main(string[] args)
{
var statusStream = new Subject<WebsiteStatus>();
Test(statusStream);
Console.WriteLine("Done");
Console.ReadKey();
}
private static void Test(IObservable<WebsiteStatus> statusStream)
{
}
}
Now suppose in Test() I want to reactively ascertain:
whether all URIs are down (as a bool)
which URIs are down (as IEnumerable<string>)
So Test would end up creating an observable like IObservable<Tuple<bool, IEnumerable<string>>> where the bool indicates whether all URIs are down and the IEnumerable<string> contains those URIs that are.
How do I go about this? My initial thinking is that I would need to group by the URI, then combine the latest from each group into a list that I could then perform a Select against. However, this did not work out due to the way CombineLatest works.
EDIT: Thanks to Matthew's answer I looked into rxx and found that it implemented a CombineLatest overload in exactly the fashion I would have expected in rx out of the box, except that I needed to change it so that it publishes even when there is only a single source stream being combined (by default it was waiting for a minimum of two source streams). Also, I can't justify pulling in an extra 2MB of binaries for the sake of one method, so I have copy/pasted it into my project. Doing so, I was able to solve as follows:
private static void Test(IObservable<WebsiteStatus> statusStream)
{
statusStream
.GroupBy(x => x.Uri)
.CombineLatest()
.Select(
x =>
{
var down = x.Where(y => y.Status == ConnectionStatus.Down);
var downCount = down.Count();
var downUris = down.Select(y => y.Uri).ToList();
return new
{
AllDown = x.Count == downCount,
DownUris = downUris
};
})
.Subscribe(x =>
{
Console.WriteLine(" Sources down ({0}): {1}", x.AllDown ? "that's all of them" : "some are still up", x.DownUris.Aggregate("", (y, z) => y += (z + " | ")));
});
}
The neatest way is to use the Rxx extension in this answer. An alternative is below, it just keeps a list of sites that are down/up.
var downStream = statusStream
.Aggregate<WebsiteStatus, IEnumerable<string>>(new string[0], (down, newStatus) =>
{
if (newStatus.IsUp)
return down.Where(uri => uri != newStatus.Uri);
else if (!down.Contains(newStatus.Uri))
return down.Concat(new string[] { newStatus.Uri });
else
return down;
});
var upStream = statusStream
.Aggregate<WebsiteStatus, IEnumerable<string>>(new string[0], (up, newStatus) =>
{
if (!newStatus.IsUp)
return up.Where(uri => uri != newStatus.Uri);
else if (!up.Contains(newStatus.Uri))
return down.Concat(new string[] { newStatus.Uri });
else
return up;
});
var allDown = upStream.Select(up => !up.Any());

Categories