I have a solution with around 30 projects in it, most of them uses Microsoft Unity as container.
For this test I'm using remote Azure SQL database in different region and in different network, so i expect delayed response but it will not affect this test.
Let's calculate data access time using Unity and direct data access using DbContext and here's the average calculation in milliseconds:
Unity Container
8749
5757
7225
7072
7256
8791
7016
7465
8449
10741
7852.1 (average)
DbContext
3599
2239
2902
2378
1898
1682
1692
1522
2773
2054
2273.9 (average)
So, accessing data using unity container took 7852.1 (average) milliseconds and at the same time accessing data using DbContext took 2273.9 (average) milliseconds. This is big performance bottleneck, don't you think ?
Let me share few code snippets, this will show how i'm using Unity in project.
Unity configuration in projects looks like below:
public class UnityConfig
{
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
public static void RegisterTypes(IUnityContainer container)
{
//// Repositories
container.RegisterType<ICartRepository, CartRepository>();
// .... total 50 repositories registrations ....
//// Services
container.RegisterType<ICartService, CartService>();
// .... total 72 services registrations ....
}
}
public static class UnityWebActivator
{
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
Here's a sample repository and a sample service being used with Unity:
public interface ICartRepository
{
Cart Get(string id);
IEnumerable<Cart> GetAll();
// more codes
}
public class CartRepository : ICartRepository
{
[Dependency]
public ApplicationData db { get; set; }
public Cart Get(string id)
{
return db.Carts.AsNoTracking().Where(i => i.Id == id && i.IsDeleted == 0).FirstOrDefault();
}
public IEnumerable<Cart> GetAll()
{
return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);
}
// more codes
}
public interface ICartService
{
Cart Get(string id, string orgid);
IEnumerable<Cart> GetAll(string orgid);
// more codes
}
public class CartService : ICartService
{
private ICartRepository cartRepository;
public CartService(ICartRepository _cartRepository)
{
cartRepository = _cartRepository;
}
public Cart Get(string id, string orgid)
{
return cartRepository.GetAll().Where(i => i.OrganizationId == orgid && i.Id == id).FirstOrDefault();
}
public IEnumerable<Cart> GetAll(string orgid)
{
return cartRepository.GetAll().Where(i => i.OrganizationId == orgid);
}
// more codes
}
In the projects I use them like:
public class HomeController : Controller
{
private ICartService cartService;
public HomeController(ICartService _cartService)
{
cartService = _cartService;
}
public ActionResult Index()
{
// through unity
var item = cartService.Get("id", "org_id");
// direct DbContext
ApplicationData data = new ApplicationData();
var item1 = data.Carts.AsNoTracking().Where(i => i.Id == "id" && i.OrganizationId == "org_id").FirstOrDefault();
// more code
return View();
}
}
This is all we using in the application. Do you see anything that can be changed to increase performance?
The reason is two ways you are comparing are completely different, and not because of Unity. First way does this:
var item = cartService.Get("id", "org_id");
Which is implemented as
return cartRepository.GetAll().Where(i => i.OrganizationId == orgid && i.Id == id).FirstOrDefault()
Where GetAll
is:
public IEnumerable<Cart> GetAll()
{
return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);
}
Because GetAll
return type is IEnumerable<Cart>
- cartRepository.GetAll().Where(...)
will not filter out carts in database. Instead, whole Cart
table is pulled into memory with SQL query like select * from Cart where IsDeleted = 0
. Then Where
is executed and finds target Cart (by organization and id) in memory. Of course this is very inefficient (because it transfers the whole table from your remote database to your machine) and takes much more time that another appoach , which does:
data.Carts.AsNoTracking().Where(i => i.Id == "id" && i.OrganizationId == "org_id").FirstOrDefault();
This one produces SQL you expect, like select top 1 * from Cart where IsDeleted = 0 and Id = @id and OrganizationId = @org_id
, and all filtering happens in database, which then transfers just one row over the network.
To fix - change your GetAll
(and other similar methods) to return IQueryable
:
public IQueryable<Cart> GetAll()
{
return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);
}