How do I access data of the request body (specific Data Member) in the global.asax using a WCF service?
For example:
I created a WCF service project with a global.asax file for dependency injection:
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
//How to access request data here?
return kernel;
}
}
I have a string GetData(Request request) Operation Contract and I want to access a Data Member from the Request Data Contract within the CreateKernel method when a request was fired to my service.
Related
I am trying to create Singleton CacheManager class that has dependency on IMemoryCache.
public class CacheManager:ICacheManager
{
private readonly IMemoryCache _cache;
public CacheManager(IMemoryCache cache)
{
_cache = cache;
}
public void LoadCache(MyData data)
{
// load cache here at startup from DB
}
}
I also have a Scoped service that retrives data from the database
public class LookupService:ILookupService
{
private readonly MyDatabaseContext _dbContext;
public class LookupService(MyDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public void Dispose()
{
//Dispose DBContext here
}
// some async methods that returns lookup collection
}
Register these services in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// EF
services.AddDbContext<MyDatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// domain services
services.AddScoped<ILookupService, LookupService>();
services.AddMemoryCache();
// singleton
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var service = scope.ServiceProvider.GetService<ILookupService>())
{
how do i create cacheManager instance by injecting IMemoryCache and also register callback function
}
}
});
}
ILookupService is registered as Scoped service becuase it has dependency on DBContext which is also (by default) registered with Scoped lifetime. I do not want to change lifetime of these services.
However I want CacheManager to be registered as Singleton, that means I cannot inject ILookupService as dependency into CacheManager.
So here is my possible solution to create & register singleton instance of CacheManager
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var lookupService = scope.ServiceProvider.GetService<ILookupService>())
{
var cache = scope.ServiceProvider.GetService<IMemoryCache>();
var manger = new CacheManager(cache);
manger.LoadCache(lookupService.GetData());
return manger;
}
}
});
Not sure this is the best way to create CacheManager. How do I implement a callback function to re-populate CacheEntry if it becomes null?
I guess I would simply configure services.AddSingleton<CacheManager>();
(CacheManager having a default constructor)
After configuring all of the DI dependencies and having a serviceprovider, get the Cachemanager singleton and initialize it with LoadCache.
(so let DI create "empty" singleton cachemanager, but initialize immediately somewhere in startup of application)
var cachemanager = scope.ServiceProvider.Get<CacheManager>();
var lookupService = scope.ServiceProvider.Get<ILookupService>();
var cache = scope.ServiceProvider.Get<IMemoryCache>();
cachemanager.Cache = cache;
cachemanager.LoadCache(lookupService.GetData());
Looks like the underlying issue is that ILookupService cannot be resolved until runtime and requests start coming in. You need to create CacheManager before this.
DI COMPOSITION
This should be done when the app starts - as in this class of mine. Note the different lifetimes for different types of object but I just focus on creating the objects rather than interactions.
DI RESOLUTION
.Net uses a container per request pattern where scoped objects are stored against the HttpRequest object. So a singleton basically needs to ask for the current ILookupService, which is done by calling:
container.GetService<ILookupService>
So include the DI container as a constructor argument to your CacheManager class and you will be all set up. This is the service locator pattern and is needed to meet your requirement.
An alternative per request resolution mechanism is via the HttpContext object as in this class, where the following code is used:
IAuthorizer authorizer = (IAuthorizer)this.Context.RequestServices.GetService(typeof(IAuthorizer));
SUMMARY
The important thing is to understand the above design pattern, and you can then apply it to any technology.
register Cache service as singleton, try below code
public class CacheService : ICacheService
{
private ObjectCache _memoryCache;
/// <summary>
/// Initializes a new instance of the <see cref="CacheService"/> class.
/// </summary>
public CacheService()
{
this._memoryCache = System.Runtime.Caching.MemoryCache.Default;
}
}
Is it somehow possible to resolve a scoped service in a singleton service's method being called by a scoped one?
E.g. i have a singleton service "GlobalService" and a scoped one "UserService".
If the UserService executes a method "Job" in "GlobalService", is it somehow possible to get scoped services in this method by using Assembly.GetCallingAssembly()? Otherwise I need to pass all the required parameters.
Thank you ✌
#DeepkaMishra's answer won't work in all scenarios.
I used it myself in blazor webassembly loggingprovider and httpcontext came as null.
For more details, read this, just adding quoted text here.
Think of HttpContext as a telephone call. If you pick the phone up
when no-one has called then there is no context i.e. it is null. When
someone does call then you have a valid context. This is the same
principal for a web call. The Configure method in Startup is not a web
call and, as such, does not have a HttpContext.
Working solution, I found is provided in this.
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
//here you can get the scoped service
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Singleton would have one single instance which can be used by your scoped service. Your scoped service method can use singleton service instance.
If you call a singleton service's method, you can get the scoped service object in it. You can use IHttpcontextAccessor to resolve the scoped service instance inside that method.
internal class Singleton
{
private readonly IHttpContextAccessor httpContextAccessor;
public Singleton(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public int Job()
{
return httpContextAccessor.HttpContext.RequestServices.GetRequiredService<Scoped>().MyProperty;
}
}
You would need to register these service in Startup's ConfigureServices method:
services.AddHttpContextAccessor();
services.AddScoped<Scoped>();
services.AddSingleton<Singleton>();
I have NET Core 2 Web API application. During the process i have to invoke Client A's API to get some data. So i am using HttpClient to invoke it. Client A also requires me to pass userid and password in header.
So instead of directly injecting HttpClient i have wrapper around HttpClient something like below
public class ClientA : IClientA
{
private readonly HttpClient _httpClient;
public ClientA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<string> GetData()
{
return await _httpClient.HttpGetAsync("someurl");
}
}
Then use ClientA in Service
public class MyService :IMyService
{
private readonly IClientA _clientA
public MyService(IClientA clientA)
{
_clientA= clientA
}
public void DoSomethig()
{
_clientA.GetData();
}
}
Then i am registering everything in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
services.AddScoped(factory =>
{
Func<Task<IClientA>> provider = async () =>
{
using (var dbContext = factory.GetService<MyDBContext>())
{
// get userid and password from database here
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("UserId",userid);
httpClient.DefaultRequestHeaders.Add("Password",password);
return new ClientA(httpClient);
}
};
return provider;
});
}
However i am getting error
System.InvalidOperationException: Unable to resolve service for type
'System.Net.Http.HttpClient' while attempting to activate
'XXXXXXXXX.ClientA'. at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, ISet1 callSiteChain,
ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type
serviceType, Type implementationType, ISet1 callSiteChain)
remaining exception removed for brevity
Notice that during registration i am newing-up instance of HttpClient and passing it to ClientA class because i have to set userid and password.
To get rid the above error I can register HttpClient with UserID and Password with DI framework and i guess that would work.
However, in that case, if have another client, ClientB, that takes HttpClient then DI framework will inject same httpclient that has userid and password. and that will create security issue because ClientB would see ClientA's credentials in request headers.
public class ClientB(HttpClient client)
{
private readonly _httpClient;
public class ClientB(HttpClient client)
{
_httpClient = client;
}
public string CallClientB(string url)
{
// here ClientB will receive ClientA's credentials
return await _httpClient.HttpGetAsync(url);
}
}
You don't want to be instantiating httpclient in a scoped context, that is creating an instance of httpclient per request which is not the recommended usage pattern for that class. (won't scale well). https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
Create a singleton with a separate interface per customer (assuming a small number of customers) - possibly with a code access security demand in its implementation, depending on your setup (identity impersonation enabled?)
That will a) scale well b) only run once per customer per application instance/startup and c) enforce an access check for usage.
Also, this answer is connected and relevant to your header requirements - HttpClient single instance with different authentication headers
resolved my issue
services.AddScoped<IClientA>(factory =>
{
var dbContext = factory.GetService<MyDBContext>();
// get userid and password from database here
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("UserId",userid);
httpClient.DefaultRequestHeaders.Add("Password",password);
return new ClientA(httpClient);
});
My scoped service for some reason seems to be generating different instances of the same class when I try to access it in 2 middlewares within the same request.
Scenario: I am adding a scoped service as such:
public interface ISimplyRecorder
{
void AddInfo(string key, string value);
Dictionary<string, string> GetAllInfo();
}
public class SimplyCoreRecorderService : ISimplyRecorder
{
private Dictionary<string,string> data;
public SimplyCoreRecorderService()
{
data = new Dictionary<string, string>();
}
public void AddInfo(string key,string value)
{
data.Add("",value);
}
public Dictionary<string,string> GetAllInfo()
{
return data;
}
}
and then the following in startup.cs
services.AddScoped<ISimplyRecorder,SimplyRecorderService>();
now I am calling this service in the constructor of a sample Middleware. I am able to access the service with a new instance and add data into it and then I call await _next(context). However, when I am calling the service in my HomeController, MVC which follows the middleware above, I seem to be getting a new instance of the service even though it's the same request.
HomeController:
ISimplyRecorder _simply;
private IHostingEnvironment _env;
public HomeController(IHostingEnvironment env,ISimplyRecorder simply)
{
_simply = simply;
_env = env;
}
public IActionResult Index()
{
_simply.AddInfo("Home:Action","resulted in index action");
return View();
}
complete code available at: https://github.com/muqeet-khan/SimplyCore if someone wants to give it a go.
Middlewares are instantiated only once when it's first involved, then all the following requests are handled by that middleware instance. NOT a new middleware instance for each request.
You get your ISimplyRecorder in the constructor of the middleware and "cache" it as a private readonly variable. This means the middleware will get the ISimplyRecorder instance of the first request, then keep adding data to that instance for all the following requests rather than the new ISimplyRecorder instance for the following requests which you get in HomeController.
To solve it, you need to get ISimplyRecorder instance from the Invoke method of the middleware.
// using Microsoft.Extensions.DependencyInjection;
public async Task Invoke(HttpContext httpContext)
{
ISimplyRecorder recoder = httpContext.RequestServices.GetRequiredService<ISimplyRecorder>();
}
EDIT:
The comment of Juergen is correct, I tried it out. You may also just write like this:
public async Task Invoke(HttpContext httpContext, ISimplyRecorder recorder)
{
// recorder is from DI
}
I am implementing a WCF service (Contract A) that will eventually make calls to a standalone service (Contract B) hosted by the client. At design-time when the client queries my service's WSDL to build its proxy, I'd like to include the WSDL for Contract B so the client can build its service around that. Unfortunately, I can't figure out how to inject Contract B into the WSDL emitted by the service. Since the contract is an interface and doesn't have the [DataContract] attribute I can't add it as a known type. Is there any other way to inject a contract into emitted WSDL?
Here's an example:
[ServiceContract]
public interface IServerService
{
[OperationContract]
void GiveTheServerMyServiceUri(string uri);
[OperationContract]
void TellAllClientsSomething(string message);
}
// THIS IS THE INTERFACE I WANT TO INCLUDE IN THE WSDL
[ServiceContract]
public interface IClientService
{
[OperationContract]
void ReceiveMessageFromServer(string message);
}
public class ServerService : IServerService
{
private List<string> knownClients;
public void GiveTheServerMyServiceUri(string uri)
{
knownClients.Add(uri);
}
public void TellAllClientsSomething(string message)
{
foreach (string clientUri in knownClients)
{
// 1. Create instance of ClientServiceProxy using client's uri
// 2. Call proxy.ReceiveMessageFromServer(message)
}
}
}
At first it seems that this is a textbook example of a duplex contract. However, for this particular application, for a variety of reasons, I need a bit more separation between client and server so I was hoping to just give the client an interface to implement (via the WSDL), let it host its own service, then just tell me the service's url.
I don't see that this makes sense. Unless your service is implementing the service contract of the other service, then don't do this.
On the other hand, your service can implement the other service contract, and become a client to the other service. It can then delegate calls to the other service contract to that other service.
I just tried this to make sure. I created a new WCF Service library project. This created a Service1 implementing IService1, with two operations. I modified the [ServiceContract] attribute to use a specific namespace (http://localhost/service1).
I then added a new service, which gave me Service2, implementing IService2, with a single operation (DoWork). I updated the [ServiceContract] to use http://localhost/service2/.
I then updated Service1 to implement IService2 as well as IService1, and to delegate IService2.DoWork to the Service2 service. I did also have to add a new endpoint implementing IService2, and I had to specify a relative address, so that the two would not conflict (since they were in the same project). Here's the result:
using System;
namespace WcfServiceLibrary1
{
public class Service1 : IService1, IService2
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public void DoWork()
{
Service2Reference.IService2 svc = null;
try
{
svc = new Service2Reference.Service2Client();
svc.DoWork();
}
finally
{
if (svc != null)
{
((IDisposable)svc).Dispose();
}
}
}
}
}