Getting rid of HttpContext dependency - can I use thread storage instead - c#

I'm writing WEB API service that uses a lot of legacy components. The legacy components rely heavily on calls to ContextInfo.ContextID (this is referenced like zillion of times).
public static class ContextInfo
{
public static int ContextID
{
get { return (int)HttpContext.Current.Items["ContextID"]; }
}
}
// sample legacy class
public class Legacy
{
public void Foo()
{
if (ContextInfo.ContextID == 7)
{
Bar();
}
}
}
Every single legacy web application then initializes HttpContext.Current.Items in Application_BeginRequest based on current url (domain) and some db settings.
void Application_BeginRequest()
{
HttpContext.Current.Items["ContextID"] = QueryDb(HttpContext.Current.Request.Url);
}
My web api service will have "dynamic" context. I.E.:
// sample web api method
void Get()
{
int contextID = GetContextBasedOnHttpHeader();
// Works but just icky
HttpContext.Current.Items["ContextID"] = context;
new Legacy().Foo();
}
Relying on http context in web api is just wrong. On the other rewriting all legacy components to inject contextID in a nice way is simply too much work.
I was thinking of abusing the Thread.SetData method instead - i.e.:
// sample web api method
void Get()
{
// filter would be better, but for simplicity sake
ContextInfo.ContextID = GetContextBasedOnHttpHeader();
new Legacy().Foo();
}
And rewriting the ContextInfo into something like this:
public interface IContextInfoProvider { int ContextID { get; set; } }
public class LegacyContextInfoProvider : IContextInfoProvider { ... }
public static class ContextInfo
{
public static IContextInfoProvider Provider = new LegacyContextInfoProvider();
public static int ContextID
{
return Provider.ContextID;
}
}
public class WebApiContextInfoProvider : IContextInfoProvider
{
public int ContextID {
get { return (int)Thread.GetData(Thread.AllocateNamedDataSlot("ContextID")); }
set { Thread.SetData(Thread.AllocateNamedDataSlot("ContextID"), value);
}
}
// on startup
ContextInfo.Provider = new WebApiContextInfoProvider();
We can also assume that the legacy components will run in the same thread (because you can't reference HttpContext.Current.Items once you've fired new thread - it will be null). My concern is with the thread safety - can I use it like this without any nasty side-effect ?

Related

Dependency Injection not resolving fast enough for use when a service relies on another service

I am injecting two services into my dot net core web api, the main service relies on data in the helper service. The helper service populates this data in the constructor, however when the main service goes to use this data it is not ready because the constructor of the helper service has not finished by the time it is needed.
I thought DI and the compiler would resolve and chain these services properly so the helper service would not be used until it was fully instantiated.
How I tell the main service to wait until the helper service is fully resolved and instantiated?
Generic sample code of what I am doing. I call the DoSomething() in MainSerice the HelperService calls out to an external API to get some data, that data is needed in the MainService.
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHelperService, HelperService);
services.Scoped<IMainService, MainService);
}
MainService.cs
public class MainService : IMainService
{
private readonly IHelperServuce _helper;
public MainService(IHelperService HelperService)
{
_helper = HelperService;
}
public void DoSomething()
{
string helperParameter = _helper.Param1; //This fails because the constructor of HelperService has not finished
}
}
HelperService.cs
public class HelperService : IHelperService
{
public HelperService()
{
GetParamData();
}
private async void GetParamData()
{
var response = await CallExternalAPIForParameters(); //This may take a second.
Params1 = response.value;
}
private string _param1;
public string Param1
{
get
{
return _param1;
}
private set
{
_param1 = value;
}
}
}
You are not awaiting the async method GetParamData() data in the constructor. That is, ofcourse, not possible. Your constructor should only initialize simple data. You could fix this by, instead of using a property to return, you could also return a Task from a method called (for example) Task<string> GetParam1(). Which could cache the string value.
for example:
public class HelperService : IHelperService
{
private string _param1;
// note: this is not threadsafe.
public async Task<string> GetParam1()
{
if(_param1 != null)
return _param1;
var response = await CallExternalAPIForParameters(); //This may take a second.
_params1 = response.value;
return _param1;
}
}
You could even return a ValueTask<string> because most of the calls can be executed synchronously.
Pass a lambda to the helper service that initializes the variable in your main service, as in...
Helper service.getfirstparam( (response) ->
{ firstparam = response.data;});
While (firstparam == null)
sleep
// now do your processing

Should I define methods as static in class library to use in Console Application

Scenario: I have a console application which references couple of class libraries. ClassLibEmployee pulls the data from SQL database and returns a List. I need to loop through the list of Employee's and send that to a WebAPI and update SQL DB with status. I created ClassLibPay which a wrapper for WebAPI.
ClassLibEmployee.EmployeeData ed = new ClassLibEmployee.EmployeeData();
var elist = ed.PullEmployees();
foreach (Employee e in elist) {
bool stat = ClassLibPay.ServiceWrap.Sendtopay(e.Id, e.Name, e.Pay, e.ExemptFlag, e.Hours);
ed.ChageStatus(e.Id, e.Name, e.Pay, e.ExemptFlag, e.Hours, stat);
}
In ClassLibEmployee, I defined class as public class EmployeeData
In ClassLibPay, I defined class as public static class ServiceWrap
Questions:
since I will be calling ChangeStatus method in EmployeeData for each employee, should that be a static class?
ServiceWrap is calling a service, is there a way to avoid creating instance of the service, for every Sendtopay call?
Console App
--References ClassLibEmployee
public class EmployeeData
{
public List<Employee> PullEmployees()
{
}
}
ConsoleApp
--References ClassLibPay
-- ClassLibPay calls a WebAPI
public static class ServiceWrap
{
public static bool Sendtopay(int id, string name, decimal pay, bool flg, int hours)
{
using (EDataSvc service = new EDataSvc())
{
service.serviceMethod(id,name,pay,flg,hours);
}
}
}
To prevent creating every time class, you definitely should move to DI way as Michael said.
This is very simple example how to use DI with console application based on Autofac library. Below we have Main console application and two classes where one is our wrapper(where maybe you want to prepare your data, and eDataService which should just send data to back-end. We register both classes as PerLifeTimeScope(here, this is singleton's - in another words have only one instance if we get it from the DI container). Of course you can choose ready frameworks with already integrated DI containers.
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var builder = new ContainerBuilder();
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();
builder.RegisterType<EDataSvc>().InstancePerLifetimeScope();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<MyService>();
service.MakeRequestAsync("test");
}
}
}
public class EDataSvc
{
public void SendRequestAsync()
{
//TODO:Send request
}
}
public class MyService : IMyService
{
private EDataSvc _eDataService;
public void MakeRequestAsync(EDataSvc eDataSvc)
{
_eDataService = eDataSvc;
}
public void MakeRequestAsync(string parameter)
{
//TODO prepare your data or additional logic
_eDataService.SendRequestAsync();
}
}
public interface IMyService
{
void MakeRequestAsync(string parameter);
}

Register same object multiple times with different configuration

I have this api client ICommunicationClient(url, tenant) registered in my IoC container. Now I'm facing the scenario where I can have 1 to n api clients. I need to register all of them and I'm not sure how to handle that. I've seen there's this RegisterCollection in SI though.
I'm considering use a ICommunicationClientProvider as a wrapper around the actual clients. It contains a list with all the registered clients and methods to retrieve them. I feel this is not the best approach and of course, it "forces" me to touch other pieces of the app.
public class CommunicationClientProvider : ICommunicationClientProvider
{
public CommunicationClientCollection CommunicationClientsCollection { get; set; }
public string Tenant { get; set; }
public ICommunicationClient GetClients()
{
return CommunicationClientsCollection[Tenant];
}
public void SetClients(CommunicationClientCollection clients)
{
CommunicationClientsCollection = clients;
}
}
public interface ICommunicationClientProvider
{
ICommunicationClient GetClients();
void SetClients(CommunicationClientCollection clients);
}
This to host the collection
public class CommunicationClientCollection : Dictionary<string, ICommunicationClient>
{
}
Here I register the collection against SI
var clients = new CommunicationClientProvider();
foreach (var supportedTenant in supportedTenants)
{
clients.CommunicationClientsCollection
.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClientProvider>(clients);
Do you know a better way of doing this? This is a normal scenario for example when you have multiple databases.
UPDATE: - ITenantContext part -
This is basically how my tenant context interface looks like:
public interface ITenantContext
{
string Tenant { get; set; }
}
and this is where I'm making my call to communication api:
public class MoveRequestedHandler : IHandlerAsync<MoveRequested>
{
private readonly IJctConfigurationService _communicationClient;
private readonly ITenantContext _tenantContext;
public MoveRequestedHandler(IJctConfigurationService communicationClient, ITenantContext tenantContext)
{
_communicationClient = communicationClient;
_tenantContext = tenantContext;
}
public async Task<bool> Handle(MoveRequested message)
{
_tenantContext.Tenant = message.Tenant;
_communicationClient.ChangeApn(message.Imei, true);
return await Task.FromResult(true);
}
}
here I register the ITenantContext
container.RegisterSingleton<ITenantContext, TenantContext>();
The tenant is defined within the MoveRequested object (message.Tenant).
How can I make CommunicationClient aware of that tenant?
If adding an ICommunicationClientProvider abstraction causes you to make sweeping changes throughout your application, there is clearly something wrong. You should typically be able to add features and make changes without having to do sweeping changes. And as a matter of fact, I think your current design already allows this.
Your ICommunicationClientProvider) acts like a factory, and factories are hardly ever the right solution. Instead, your are much better of using the Composite design pattern. For instance:
sealed class TenantCommunicationClientComposite : ICommunicationClient
{
private readonly ITenantContext tenantContext;
private readonly Dictionary<string, ICommunicationClient> clients;
public TenantCommunicationClientComposite(ITenantContext tenantContext,
Dictionary<string, ICommunicationClient> clients) {
this.tenantContext = tenantContext;
this.clients = clients;
}
object ICommunicationClient.ClientMethod(object parameter) =>
this.clients[this.tenantContext.CurrentTenantName].ClientMethod(parameter);
}
You can register this class as follows:
var dictionary = new Dictionary<string, ICommunicationClient>();
foreach (var supportedTenant in supportedTenants) {
dictionary.Add(supportedTenant, new CommunicationClient(
new Uri(configuration.AppSettings["communication_api." + supportedTenant]),
new TenantClientConfiguration(supportedTenant)));
}
container.RegisterSingleton<ICommunicationClient>(
new TenantCommunicationClientComposite(
new AspNetTenantContext(),
dictionary));
Here the ITenantContext is an abstraction that allows you to get the current tenant on who's behalf the current request is running. The AspNetTenantContext is an implementation that allows you to retrieve the current tenant in an ASP.NET application. You probably already have some code to detect the current tenant; you might need to move that code to such AspNetTenantContext class.

Calling one ServiceStack 4 service from another with a file upload thrown in for fun

I have a working service in 4.0.31 that looks like this:
public object Post(MyDTO request)
{
foreach (var uploadedFile in base.Request.Files)
{
... do something ...
}
return new MyDTOResponse();
}
Everything works great, and I'm happy!
But now, I want to call the same service method from within another service, the way to do this apparently is:
public object Post(MyOtherDTO request)
{
var myService = base.ResolveService<MyService>();
// now I call some new method I wrote to bypass the file upload part, since
// myService.Post() doesn't know about the file upload part
var myResponse = myService.NewMethodThatLetsMePassAStreamToTheOtherService(streamData);
... do other stuff...
return new MyOtherDTOResponse();
}
While I'm not unhappy with this, it does create a hard dependency between the two services, so I'm not thrilled like I usually am with ServiceStack!
Is there a more elegant way of putting this together? I'm probably just missing something really, really obvious...
I'm not 100% clear on what the issue is, if it's how to share logic between services? then you could pull common logic out of each service class and reference the shared code in both Services.
If no dependencies are required I'll refactor the shared code behind re-usable extension methods.
If dependencies are required I will refactor it behind a shared logic class that's a dependency in both Services, see the IGreeter example in the sharing logic between MVC and ServiceStack answer:
public class MyService1 : Service
{
public ISharedDep SharedDep { get; set]
public object Any(Request1 request)
{
//...
}
}
public class MyService2 : Service
{
public ISharedDep SharedDep { get; set]
public object Any(Request2 request)
{
//...
}
}
Shared logic using Request Context using base class
If it's common code used by many Services that requires the base.Request context than you could move it to a common Service base class:
public class MyServiceBase : Service
{
public ISharedDep SharedDep { get; set]
public object SharedMethod(object request)
{
//...
}
}
public class MyServices1 : MyServiceBase { ... }
public class MyServices2 : MyServiceBase { ... }
Shared logic using Request Context using Extension method
If you prefer not to use a base class, this can be re-factored behind an extension method as well:
public static void MyServiceExtensions
{
public static object SharedMethod(this IServicBase service, object request)
{
var sharedDep = service.TryResolve<ISharedDep>();
return sharedDep.SharedMethodWithRequestCtx(request, service.Request);
}
}
Loose Coupling by executing a Request DTO
If the issue is about a loose-coupled way to call Services without a reference to the implementation itself you can execute the Request DTO with the ServiceController:
public class MyService : Service
{
public object Any(Request requestDto)
{
var altDto = new AltRequest { Id = requestDto.Id };
var response = HostContext.ServiceController.Execute(altDto, base.Request);
//...
}
}
Note: this API is available as base.ExecuteRequest(requestDto) in v4.0.32+.
Uploading Files to a HTTP Service In Memory
If the issue is instead how to execute a Service that handles file uploads, there's an example in the embedded version of HttpBenchmarks showing how to call a Service that processes HTTP File uploads with a custom Request Context that uses local FileSystem files instead:
using (var admin = Resolve<AdminServices>())
{
//...
var dir = new FileSystemVirtualPathProvider(this, Config.WebHostPhysicalPath);
var files = dir.GetAllMatchingFiles("*.txt")
.Concat(dir.GetAllMatchingFiles("*.zip"));
admin.Request = new BasicRequest
{
Files = files.Map(x => new HttpFile {
ContentLength = x.Length,
ContentType = MimeTypes.GetMimeType(x.Name),
FileName = x.Name,
InputStream = x.OpenRead(),
} as IHttpFile).ToArray()
};
if (admin.Request.Files.Length > 0)
{
admin.Post(new UploadTestResults
{
TestPlanId = 1,
TestRunId = testRun.Id,
CreateNewTestRuns = true,
});
}
}

ASP.Net Application Warmup - Exposing Collections

My MVC application currently uses the Global.asax, Application_Start method to load tons of data, and then exposes it as collections. For example:
Current Usage Example:
// Global.asax
public static DataRepository Repository { get; set; }
protected void Application_Start()
{
// All the normal stuff...
// Preload this repository.
DataRepository = new DataRepository();
}
// HomeController.cs Example
public ActionResult Index(){
return Json(MyApplication.Repository.GetSomeCollection(),
JsonRequestBehavior.AllowGet);
}
What I'm trying to do:
I want to use the ASP.Net 4.0 + IIS 7.5 Application Preload functionality, but need to expose the repository to the rest of the application. Something like:
// pseudo code attempt at goal
public class ApplicationPreload : IProcessHostPreloadClient
{
public MyRepositoryClass Repository { get; set; }
public void Preload(string[] parameters)
{
// repository class's constructor talks to DB and does other crap.
Repository = new MyRepositoryClass();
}
}
Question
How can I expose a repository class or even a simple IEnumerable<T> collection using the Preload() method implemented via IProcessHostPreloadClient?
If you're just aiming to expose an IEnumerable<T> try stuffing it into HttpRuntime.Cache from the implementation of IProcessHostPreloadClient. You can then optionally expose the collection from the Global.asax application class.
Something like:
public class ApplicationPreload : IProcessHostPreloadClient
{
public void Preload(string[] parameters)
{
var repository = new MyRepositoryClass();
HttpRuntime.Cache.Insert(
"CollectionName",
repository.GetCollection(),
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
null);
}
}
public class MvcApplication : HttpApplication
{
public IEnumerable<CollectionItem> CollectionName
{
get { return HttpRuntime.Cache["CollectionName"] as IEnumerable<CollectionItem>; }
}
}

Categories

Resources