Advice on web api unity and lifetime of repositories - unity-container

I'm just running some tests with web api and unity, I have created a simple product repository and registered it with unity
public class ProductRepository : IProductRepository
{
private List<Product> products = new List<Product>();
private int _nextId = 1;
public ProductRepository()
{
Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
}
public IEnumerable<Product> GetAll()
{
return products;
}
... etc
container.RegisterType<IProductRepository, ProductRepository>((new HierarchicalLifetimeManager()));
when instantiated the product repository populates itself with 3 products so calls to products
api/Products returns all products.
The controller is initialised
private IProductRepository _repository;
public ProductsController(IProductRepository repository)
{
_repository = repository;
}
by using the HierarchicalLifetimeManager I was expecting that if I put to the controller and then subsequently get that the previously put item would exist, this is not the case and the repository is initialized with every call, I'm only prototyping at the moment so don't want a dbcontext but want the repositories state to persist with multiple calls sort of singleton I suppose. any pointers on how this works or what I should do?

Try using the ContainerControlledLifetimeManager instead. From http://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx#sec34.
The default lifetime manager for the RegisterType method is the TransientLifetimeManager and the default lifetime manager for the RegisterInstance method is the ContainerControlledLifetimeManager. If you want the container to create or return a singleton instance of a type when you call the Resolve method, you can use the ContainerControlledLifetimeManager type when you register your type or instance.

Related

IConfigureOptions<T> is not creating scoped options

Typically Options are singleton. However i am building options from the database, and one of the Options property is password which keep changing every month. So i wanted to create Scoped instance of Options. I am using IConfigureOptions<T> like below to build Options from the database
public class MyOptions
{
public string UserID {get;set;}
public string Password {get;set;
}
public class ConfigureMyOptions : IConfigureOptions<MyOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureMyOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(MyOptions options)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = provider.GetRequiredService<MyDBContext>())
{
options.Configuration = dbContext.MyOptions
.SingleOrDefault()
.Select(x => new MyOptions()
{
UserID = x.UserID,
Password = x.Password
});
}
}
}
}
Use it in controller
public class HomeController : BaseController
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> option)
{
_options = option.Value;
}
[HttpGet]
[Route("home/getvalue")]
public string GetValue()
{
// do something with _options here
return "Success";
}
}
I want to create an instance of MyOptions for every new request so register it as Scoped in startup.cs
services.AddScoped<IConfigureOptions<MyOptions>, ConfigureMyOptions>();
However, when i put debugger inside ConfigureMyOptions's Configure method it only gets hit once for the first request. For next request onward the container returns the same instance (like singleton).
How do i set the scope here so MyOptions will get created for each request?
Use IOptionsSnapshot instead of IOptions in your controller and it will recreate options per request.
Why doesn't work with IOptions:
.AddOptions extension method of Configuration API registers the OptionsManager instance as a singlethon for IOptions<>
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
and OptionsManager class uses caching internally:
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
The following issue on github helped to find above: OptionsSnapshot should always be recreated per request

Asp.net Identity DbContext / Repository Issue

I am using Asp.Net identity within my MVC app. I can see that this has it's own ApplicationDbContext - albeit it is connected to the same SQL db as my own DbContext I am using elsewhere.
So I am trying to access some of my own data via my own code within the AccountController - it does not seem to work I presume because of some confusion over which DBContext it thinks is active?
My Code :
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private PostageManager postmgr;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, PostageManager _postmgr)
{
UserManager = userManager;
SignInManager = signInManager;
postmgr = _postmgr;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//create select list items for countries drop down
List<SelectListItem> countries;
countries = postmgr.GetCountries().Select(item => new SelectListItem
{
Value = item.Country,
Text = item.Country
}).ToList();
countries.Insert(0, new SelectListItem { Value = string.Empty, Text = "Select delivery country or region...", Selected = true });
RegisterViewModel mode = new RegisterViewModel
{
Countries = countries
};
return View();
}
}
}
PostageManager is just a class that sits over my DAL to fetch some data (which uses repository pattern) - I'm using just a kind of pass through method to grab a list of countries, and using it in exactly the same way I have in other controllers which works fine. Underneath that class is my repository code that is linked to my default connection string (DBContext). It's balking at the following line with a null reference exception, I think postmgr is null :
countries = postmgr.GetCountries().Select(item => new SelectListItem
In reverse to get access to the identity data within my own controllers I have done the following :
public BasketController(BasketManager _mgr, PostageManager _postmgr, ProductManager _prodmgr)
{
mgr = _mgr;
postmgr = _postmgr;
prodmgr = _prodmgr;
shopper = Cart.GetShopperId();
this.applicationDbContext = new ApplicationDbContext();
this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.applicationDbContext));
}
protected ApplicationDbContext applicationDbContext { get; set; }
protected UserManager<ApplicationUser> userManager { get; set; }
Which as far as I understand it points the identity code to use the right DbContext - I looked at doing this in reverse in my AccountController but can't fathom it out.
I basically just want to be able to use my own code that grabs my own data from within the Identity controllers to help pass extra data etc through to the views.
I might be wrong but most probably postmgr field is not initialized from constructor and that is why you have this error.
Explanation:
By default Asp will try to create controller instance by constructor without parameters. If Asp can't find constructor without parameters it will try to call constructor with parameters, but to make it possible you have to configure IoC in your app. As your controler has constructor without parameters it will be selected by Asp. So all 3 fields are empty.
But in properties SignInManager and UserManager you try to take value from field or from OwinContext. As field is empty your code will take value from OwinContext. OwinContext is quite complex and smart tool that create its context automatically based on configuration provided in Startup.Auth.cs file or any other file under App_Start folder.
I think I have figured it out - added the following to my NinjectControllerFactory :
ninjectKernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication); //.InRequestScope();
ninjectKernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
ninjectKernel.Bind<UserManager<ApplicationUser>>().ToSelf();
ninjectKernel.Bind<IRoleStore<IdentityRole, string>>().To<RoleStore<IdentityRole, string, IdentityUserRole>>();
ninjectKernel.Bind<RoleManager<IdentityRole>>().ToSelf();
And changed my constructor to :
public AccountController(PostageManager _postmgr)
{
postmgr = _postmgr;
}

Get customers number of orders

How can I get customer number of orders on customer order page on public store. I have seen that already exist on admin page bu is there any setting that should be activated as this can be shown on public store.
Assuming the controller you are using has the _storeContext, _workContext and _orderService variables injected, this will do it:
var orderCount = _orderService.SearchOrders(
storeId: _storeContext.CurrentStore.Id,
customerId: _workContext.CurrentCustomer.Id)
.Count();
If any of those variables don't exist, manually add them to your controller. Toy need to add the private variables:
private readonly IStoreContext _storeContext;
private readonly IOrderService _orderService;
private readonly IWorkContext _workContext;
Extend the constructor of the controller and add in the code to save the injected values:
public YourController(/* other parameters */
IOrderService orderService,
IWorkContext workContext,
IStoreContext storeContext)
{
//snip
this._orderService = orderService;
this._shipmentService = shipmentService;
this._workContext = workContext;
this._storeContext = storeContext;
}

Creating an EF CodeFirst DbContext using Roslyn

Just a little idea I'm playing with, not sure if it's viable or has much of a use.
I'm trying to generate a very basic EF Code First database using the Roslyn CTP.
Code:
var scriptEngine = new ScriptEngine(new[] { "System", "System.Core", typeof(DbContext).Assembly.Location });
var session = Roslyn.Scripting.Session.Create();
var t = scriptEngine.CompileSubmission<DbContext>(#"
using System.Data.Entity;
public class Car {
public int Id {get; set;}
public string Name {get; set; }
}
public class Context : DbContext {
public DbSet<Car> Cars {get; set; }
}
new Context();
", session);
t.Execute();
When executed I get the following exception
Exception:
The type 'Submission#0+Car' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
Looking through the list of possible issues, I'm guessing that Roslyn is making a nested class as part of the code gen. This makes sense otherwise the "new Context();" call would need to be wrapped into a class/method of some sort. I could emit an assembly, which would confirm the above but likely wouldn't have any clues on how to write it correctly.
I also went down the route of Syntax.ClassDeclaration, but ended up with a few hundred lines of code just to make a class with 1 property and no obvious way how to instantiate that class.
Question
Is there an easy way to create a class in Roslyn that is publicly accessible (eg not nested in another class)?
You can use Roslyn to create actual DLL library that contains your type based on your source code and then use that from your script:
var classCode = #"
using System.Data.Entity;
public class Car {
public int Id { get; set; }
public string Name { get; set; }
}
public class Context : DbContext {
public DbSet<Car> Cars { get; set; }
}";
var syntaxTree = SyntaxTree.ParseCompilationUnit(classCode);
var compilation = Compilation.Create(
"car",
new CompilationOptions(assemblyKind: AssemblyKind.DynamicallyLinkedLibrary))
.AddReferences(
new AssemblyFileReference(typeof(object).Assembly.Location), // mscorlib
new AssemblyFileReference(typeof(Uri).Assembly.Location), // System
new AssemblyFileReference(typeof(IOrderedQueryable<>).Assembly.Location), // System.Data
new AssemblyFileReference(typeof(DbContext).Assembly.Location) // EntityFramework
)
.AddSyntaxTrees(syntaxTree);
var dllPath = "car.dll";
using (var stream = File.OpenWrite(dllPath))
{
compilation.Emit(stream);
}
var code = #"new Context();";
var scriptEngine = new ScriptEngine(new[] { new FileInfo(dllPath).FullName, "EntityFramework" });
var context = scriptEngine.Execute<DbContext>(code);

Using Moq to verify that a method was called

I have a class like:
public class Store
{
public Store()
{
Products = new List<Product>();
}
public IList<Product> Products {get; private set;}
public void AddProduct(int id, string productCode)
{
Product p = new Product();
p.Id = id;
p.ProductCode = productCode;
//Validate before adding
Products.Add(p); //How can i verify that this was called
}
}
Using Moq how can i verify that the Add method of the Products List was called? Can someone provide a simple example?
Well you can't really mock anything at the moment as the Products list is set up in the default constructor?
The easiest thing to do would be to assert against your products collection manually (i.e. just verify there's a product in there with the specified ID and Code) then you don't have to worry about mocking at all.
If you really want to use Moq to test this you need to provide a means to inject your Mock and get around your constructor, as an example you can provide two constructors
public class Store {
public Store() : this(new List<Product>()) {
}
public Store(IList<Product> productList) {
Products = productList
}
//Implementation
}
You can then write a test against your add method as follows
[Test]
public AddProduct_WithIdAndProductCode_AddsProductToCollection() {
int productId = 0;
string productCode = "a";
var productListMock = new Mock<IList<Product>>();
Store store = new Store(productListMock.Object);
store.AddProduct(productId, productCode);
productListMock.Verify(pl =>
pl.Add(It.Is<Product>(p =>
p.Id == productId && p.ProductCode == productCode)));
}
Do you need to test that add was called or that the list now has the expected number of items?
Assert.True(store.Products.Count == 1);

Resources