I am new to autofac DI. I have followed the official documentation of autofac scopes from below URL.
https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#thread-scope
Here it is mentioned that to create a thread scope the example given was
var builder = new ContainerBuilder();
builder.RegisterType<MyThreadScopedComponent>()
.InstancePerLifetimeScope();
var container = builder.Build();
How do I create MyThreadScopedComponent class?
In whichever class in which you need to use MyThreadScopedComponent, inject it into the constructor like so:
public class Test
{
private MyThreadScopedComponent MyComponent { get; }
public Test(MyThreadScopedComponent myComponent)
{
MyComponent = myComponent;
}
public void Foo()
{
MyComponent.DoStuff();
}
}
Related
Is there a way to configure my Azure Cloud storage in ConfigureServices of the Startup class, the same way I configure my DatabaseContext?
var connection = Configuration.GetConnectionString("myDatabaseConnectionString");
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(connection));
I know I could access the connection string from within a controller by adding the IConfiguration instance to the service collection as a singleton object in ConfigureServices and then inject IConfiguration in the controller.
But how do I access this in the following class or is this a bad practice and there is a better way (e.g. in the services.Add ...) ?
public class ClassifiedsToProcess
{
public static void AddMessage(string message)
{
var storageConnectionString = Configuration.Get("AppSettings:ConnectionString");
storageAccount = CloudStorageAccount.Parse(storageConnectionString);
...
}
}
Thanks !
Here is how I solved my problem using IOptions as suggested by Martin Brandl
Controller code
private string makeJson;
private MyAzureQueue MyAzureQueue;
public AdminController(IOptions<AzureOptions> optionsAccessor)
{
MyAzureQueue = new MyAzureQueue(optionsAccessor);
}
public IActionResult Index()
{
MyAzureQueue.AddMessage("my message");
return View();
}
MyAzureQueue class
public interface IMessageRepository
{
void AddMessage(string message);
}
public class MyAzureQueue: IMessageRepository
{
private readonly CloudQueue _queue;
public ClassifiedsToProcess(IOptions<AzureOptions> optionsAccessor)
{
CloudStorageAccount storageAccount = AzureStorageHelper.getStorageAccount(optionsAccessor.Value.StorageConnectionString);
_queue = AzureStorageHelper.GetQueue(storageAccount, "mystorageaccount");
}
public void AddMessage(string message)
{
CloudQueueMessage cloudQueueMessage = new CloudQueueMessage(message);
_queue.AddMessageAsync(cloudQueueMessage);
}
}
Azure Options class
public class AzureOptions
{
public AzureOptions()
{
}
public string StorageConnectionString { get; set; }
}
In the Startup.cs ConfigureServices method I added the following two lines
services.AddOptions();
services.Configure<AzureOptions>(Configuration.GetSection("Azure"));
And this is what my appsettings.json looks like
{
"Azure": {
"StorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=mystorageaccount;AccountKey=XXXXXXXXXXXXXXXXXXXXX;EndpointSuffix=core.windows.net"
}
}
You should try to avoid using IConfiguration directly and use the Option pattern in
ASP.NET Core instead.
In your example, I would e. g. implement an IMessageRepository which uses the storage account to persist the messages.
Your ClassifiedsToProcess class would have a constructor dependency to it and your AddMessage method would used it (it shouldn't be static since you want to access the repository).
I'm trying to strongly type (such as it is) some URLs for a web app when I build a viewmodel.
So I have something like:
new MyModel {
Text = "Foo",
Url = new UrlHelper(Request.RequestContext).Action("MyAction")
}
This works just fine in a controller method, but I have another situation where I am not receiving the Request.Context because it's being called in another class.
Is there another way to do this so that I'm not using "magic strings" and/or relying on the context object?
Use Reference
HttpContext.Current
which is derived from system.web. There for following code will work anywhere in your application.
UrlHelper objUrlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
objUrlHelper.Action("About");
Example:
public class MyViewModel
{
public int ID { get; private set; }
public string Link
{
get
{
UrlHelper objUrlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
return objUrlHelper.Action("YourAction", "YourController", new { id = this.ID });
}
}
public MyViewModel(int id)
{
this.ID = id;
}
}
I am trying to learn IOC principle from this screencast
Inversion of Control from First Principles - Top Gear Style
I tried do as per screencast but i get an error while AutomaticFactory try create an object of AutoCue. AutoCue class has contructor which takes IClock and not SystemClock. But my question is , in screencast IClock is resolved with SystemClock while inside AutomaticFactory .But in my code , IClock does not get resolved . Am i missing something ?
class Program
{
static void Main(string[] args)
{
//var clarkson = new Clarkson(new AutoCue(new SystemClock()), new Megaphone());
//var clarkson = ClarksonFactory.SpawnOne();
var clarkson = (Clarkson)AutomaticFactory.GetOne(typeof(Clarkson));
clarkson.SaySomething();
Console.Read();
}
}
public class AutomaticFactory
{
public static object GetOne(Type type)
{
var constructor = type.GetConstructors().Single();
var parameters = constructor.GetParameters();
if (!parameters.Any()) return Activator.CreateInstance(type);
var args = new List<object>();
foreach(var parameter in parameters)
{
var arg = GetOne(parameter.ParameterType);
args.Add(arg);
}
var result = Activator.CreateInstance(type, args.ToArray());
return result;
}
}
public class Clarkson
{
private readonly AutoCue _autocue;
private readonly Megaphone _megaphone;
public Clarkson(AutoCue autocue,Megaphone megaphone)
{
_autocue = autocue;
_megaphone =megaphone;
}
public void SaySomething()
{
var message = _autocue.GetCue();
_megaphone.Shout(message);
}
}
public class Megaphone
{
public void Shout(string message)
{
Console.WriteLine(message);
}
}
public interface IClock
{
DateTime Now { get; }
}
public class SystemClock : IClock
{
public DateTime Now { get { return DateTime.Now; } }
}
public class AutoCue
{
private readonly IClock _clock;
public AutoCue(IClock clock)
{
_clock = clock;
}
public string GetCue()
{
DateTime now = _clock.Now;
if (now.DayOfWeek == DayOfWeek.Sunday)
{
return "Its a sunday!";
}
else
{
return "I have to work!";
}
}
}
What you basically implemented is a small IoC container that is able to auto-wire object graphs. But your implementation is only able to create object graphs of concrete objects. This makes your code violate the Dependency Inversion Principle.
What's missing from the implementation is some sort of Register method that tells your AutomaticFactory that when confronted with an abstraction, it should resolve the registered implementation. That could look as follows:
private static readonly Dictionary<Type, Type> registrations =
new Dictionary<Type, Type>();
public static void Register<TService, TImplementation>()
where TImplementation : class, TService
where TService : class
{
registrations.Add(typeof(TService), typeof(TImplementation));
}
No you will have to do an adjustment to the GetOne method as well. You can add the following code at the start of the GetOne method:
if (registrations.ContainsKey(type))
{
type = registrations[type];
}
That will ensure that if the supplied type is registered in the AutomaticFactory as TService, the mapped TImplementation will be used and the factory will continue using this implementation as the type to build up.
This does mean however that you now have to explicitly register the mapping between IClock and SystemClock (which is a quite natural thing to do if you're working with an IoC container). You must make this mapping before the first instance is resolved from the AutomaticFactory. So you should add the following line to to the beginning of the Main method:
AutomaticFactory.Register<IClock, SystemClock>();
I want to access the calling Service from inside the ServiceRunner OnBeforeRequest()method in order to get to an object in the calling service class. In MVC, I can create a class BaseController that overrides OnActionExecuting() and I can get to Data easily. However, using ServiceRunner, since it's not derived from Service, I don't see a way to get to the Service object.
Sample service:
public class ProductsService : Service
{
private MyData _data = new MyData();
public MyData Data
{
get { return _data; }
}
public object Get(GetProduct request)
{
// ...
return product;
}
}
In my custom ServiceRunner, how do I retrieve the ProductsService object from OnBeforeRequest() so I can get to Data?
public class MyServiceRunner<T> : ServiceRunner<T>
{
public override void OnBeforeExecute(IRequestContext requestContext, T request)
{
// var productService = ?
base.OnBeforeExecute(requestContext, request);
}
}
After much digging, it looks like this cannot be done. The Service action is available in the ServiceRunner as an unnamed lamdba delegate. There is no reference to the Service.
I have instead found a workaround. I first registered MyData in AppHost.Configure() using
container.RegisterAutoWired<MyData>();
I moved the MyData declaration to a filter attribute like this:
public class UseMyDataAttribute : RequestFilterAttribute
{
public MyData Data { get; set; } // injected by Funq IoC.
public override void Execute(IHttpRequest req, IHttpResponse res, object responseDto)
{
Data.SessionID = req.GetSessionId();
}
}
This way I can apply [UseMyData] to the ProductsService class and be able to set the Session ID to Data.
I am having a WCF application hosted on IIS.
I am initializing an IoC container in a custom ServiceHostFactory.
Basically what I would like is to be able to "inject" that IoC Container to each Service instance created within the scope of that ServiceHost.
How would you accomplish that?
Ariel
Like you said you will need a custom ServiceHostFactory that should be used in order to create your services. Something like this:
public class SessionPerCallServiceHostFactory : ServiceHostFactory
{
public SessionPerCallServiceHostFactory()
{
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new SessionPerCallServiceHost(serviceType, baseAddresses);
}
}
You will also need a ServiceHost responsible for creating the necessary service:
public class SessionPerCallServiceHost : ServiceHost
{
public SessionPerCallServiceHost()
{
}
public SessionPerCallServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
Description.Behaviors.Add(new SessionPerCallServiceBehavior());
base.OnOpening();
}
}
With a custom implementation of IServiceBehavior that will be able to provider the InstanceProvider used to create the service instances:
public class SessionPerCallServiceBehavior : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new SessionPerCallInstanceProvider(serviceDescription.ServiceType);
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters)
{
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
And finally the instance provider that will allow you to use the IoC to inject whatever you want in the requested service instance:
public class SessionPerCallInstanceProvider : IInstanceProvider
{
private readonly Type _serviceType;
public SessionPerCallInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
//I'm creating it without any tricks but you could use your IoC container here
return Activator.CreateInstance(_serviceType);
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance)
{
}
}
Hope it helps!