Circular Reference + IoC - asp.net-mvc

I have this situation:
Web Project - calling a Business class using Unity 3 IoC. The Web Project doesn't see Business Project. It just references Contracts Project.
namespace Biblioteca.Web.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
var Autor = Inject.Resolve<IAutorBO>();
}
}
}
Business Project - Here I use the Unity 3 IoC to point to AutorDO class which is in the Data Project (see below). The Business Project doesn't see Data Project.
namespace Biblioteca.Data
{
public sealed partial class AutorBO : IAutorBO
{
#region Atributos
private IAutorDO AutorDO = Inject.Resolve<IAutorDO>();
#endregion
#region Métodos Interface
public IQueryable<DTOAutor> GetAll()
{
return AutorDO.GetAll();
}
public DTOAutor GetById(int id)
{
return AutorDO.GetById(id);
}
void IAutorBO.Insert(DTOAutor dto)
{
AutorDO.Insert(dto);
}
void IAutorBO.Delete(DTOAutor dto)
{
AutorDO.Delete(dto);
}
void IAutorBO.Update(DTOAutor dto)
{
AutorDO.Update(dto);
}
//IQueryable<DTOAutor> IAutorBO.SearchFor(Expression<Func<Autor, bool>> predicate)
//{
// return AutorDO.SearchFor(predicate);
//}
IQueryable<DTOAutor> IAutorBO.GetAll()
{
return AutorDO.GetAll();
}
DTOAutor IAutorBO.GetById(int id)
{
return AutorDO.GetById(id);
}
#endregion
#region Outros Métodos
#endregion
}
}
Data Access Project - Here is my Data Project.
namespace Biblioteca.Data
{
public sealed partial class AutorDO : IAutorDO
{
#region Atributos
Repository<Autor> repository = new Repository<Autor>();
#endregion
#region Implementações Interface
/// <summary>
/// Implementação do método de interface Insert
/// </summary>
/// <param name="dto"></param>
void IAutorDO.Insert(Transport.DTOAutor dto)
{
Autor entity = AssemblerAutor.ToEntity(dto);
repository.Insert(entity);
}
/// <summary>
/// Implementação do método de interface Delete
/// </summary>
/// <param name="dto"></param>
void IAutorDO.Delete(Transport.DTOAutor dto)
{
Autor entity = AssemblerAutor.ToEntity(dto);
repository.Delete(entity);
}
/// <summary>
/// Implementação do método de interface Update
/// </summary>
/// <param name="dto"></param>
void IAutorDO.Update(Transport.DTOAutor dto)
{
Autor entity = AssemblerAutor.ToEntity(dto);
repository.Update(entity);
}
/// <summary>
/// Implementação do método de interface SearchFor
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
//IQueryable<Transport.DTOAutor> IAutorDO.SearchFor(Expression<Func<Autor, bool>> predicate)
//{
// IQueryable<Autor> list = repository.SearchFor(predicate);
// IQueryable<Transport.DTOAutor> dtoList = (IQueryable<Transport.DTOAutor>)AssemblerAutor.ToDTOs(list);
// return dtoList;
//}
/// <summary>
/// Implementação do método de interface GetAll
/// </summary>
/// <returns></returns>
IQueryable<Transport.DTOAutor> IAutorDO.GetAll()
{
IQueryable<Autor> list = repository.GetAll();
IQueryable<Transport.DTOAutor> dtoList = (IQueryable<Transport.DTOAutor>)AssemblerAutor.ToDTOs(list);
return dtoList;
}
/// <summary>
/// Implementação do método de interface GetById
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Transport.DTOAutor IAutorDO.GetById(int id)
{
Autor entity = new Autor();
Transport.DTOAutor dto = new Transport.DTOAutor();
using (var ctx = new BibliotecaContext())
{
entity = repository.GetById(id);
dto = AssemblerAutor.ToDTO(entity);
}
return dto;
}
#endregion
}
}
Both Business and Data Projects references the Contracts Project which has all Unity 3 IoC Interfaces, used for implementing IoC. Below are the interfaces used to implement IoC:
namespace Biblioteca.Contracts
{
public interface IAutorBO
{
#region Métodos CRUD
void Insert(DTOAutor dto);
void Delete(DTOAutor dto);
void Update(DTOAutor dto);
//IQueryable<DTOAutor> SearchFor(Expression<Func<Autor, bool>> predicate);
IQueryable<DTOAutor> GetAll();
DTOAutor GetById(int id);
#endregion
}
}
namespace Biblioteca.Contracts
{
public interface IAutorDO
{
#region Métodos CRUD
void Insert(DTOAutor dto);
void Delete(DTOAutor dto);
void Update(DTOAutor dto);
//IQueryable<DTOAutor> SearchFor(Expression<Func<Autor, bool>> predicate);
IQueryable<DTOAutor> GetAll();
DTOAutor GetById(int id);
#endregion
}
}
To complement, I use a generic repository, as below:
namespace Biblioteca.Data
{
/// <summary>
/// Interface para classe genérica para métodos CRUD
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IRepository<T>
{
void Insert(T entity);
void Delete(T entity);
void Update(T entity);
IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate);
IQueryable<T> GetAll();
T GetById(int id);
}
}
namespace Biblioteca.Data
{
/// <summary>
/// Classe genérica para métodos CRUD da camada Data
/// </summary>
/// <typeparam name="T"></typeparam>
public class Repository<T> : IRepository<T> where T : class
{
#region Attributes
protected DbSet<T> dbset;
protected DbContext ctx;
#endregion
#region Constructor
public Repository()
{ }
public Repository(DbContext ctx)
{
this.ctx = ctx;
dbset = ctx.Set<T>();
}
#endregion
#region IRepository<T> Members
public void Insert(T entity)
{
if (dbset.Contains(entity))
{
Update(entity);
}
else
{
dbset.Add(entity);
}
}
public void Delete(T entity)
{
dbset.Remove(entity);
}
public void Update(T entity)
{
using (ctx)
{
ctx.Set<T>().Attach(entity);
ctx.SaveChanges();
}
}
public IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate)
{
return dbset.Where(predicate);
}
public IQueryable<T> GetAll()
{
return dbset;
}
public T GetById(int id)
{
return dbset.Find(id);
}
#endregion
}
}
Notice that I have a commented method in all classes. This Method can not be implemented cause it will cause a circular dependency.
I have the Data Project referencing Contract Project. But I can not use the method "SearchFor" cause it needs the Entity Autor which is in Data Project.
Notice that both Business and Data needs to see Entity classes, cause I have the same method signature.
I need some way to permit use the IoC the way it is, where Web not references Business and Business not references Data, and be able to create other methods where I can pass Entity as parameter.
Any suggestion ? I have already tried to create a third project and points to it but I can't make it works. Is it possible to use Reflection ? If possible, how ?
I will appreciate any suggestion.

If you have a circular dependency, it is a clue you should modify your architecture.
If you need to "have the Data Project referencing Contract Project", then Contract cannot reference Data to get the Entity. You say "Business and Data needs to see Entity classes", so why not keep your entity Autor in an Entities project.
Data
-> references Contract
-> references Entities
Contract
-> references Entities
Or as an alternative, define your entities in the Contract project instead of Data.

Related

How to configure Ninject in call scope binding within Azure Web Job?

I am facing a problem when try to write a web job scheduler. I am using Ninject scope binding using EF Repository pattern. But Only InSingletonScope() work as expected. How to configure it in RequestScope Or Call Scope ?
//Register Context
Kernel.Bind<MyDbContext>().ToSelf().InSingletonScope();
Kernel.Bind<IUnitOfWork<MyDbContext>>().To<UnitOfWork<MyDbContext>>().InSingletonScope();
Problem Solved From One of My Previous Post
I am posting Step By Step Solution
(1.) NinjectJobActivator
public class NinjectJobActivator : IJobActivator
{
#region Variable Declaration
private readonly IResolutionRoot _resolutionRoot;
#endregion
#region CONSTRUCTOR
/// <summary>
///
/// </summary>
/// <param name="kernel"></param>
// public NinjectJobActivator(IKernel kernel)
public NinjectJobActivator(IResolutionRoot resolutionRoot)
{
_resolutionRoot = resolutionRoot;
}
#endregion
#region CreateInstance
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T CreateInstance<T>()
{
return _resolutionRoot.Get<T>(new CallScopedParameter());
}
#endregion
}
(2) NinjectBindings
using Ninject.Extensions.Conventions;
using Ninject.Extensions.NamedScope;
public class NinjectBindings : NinjectModule
{
public override void Load()
{
//Register Context
Kernel.Bind<MyDbContext>().ToSelf()
.When(x => x.Parameters.OfType<CallScopedParameter>().Any())
.InCallScope(); // For Scheduler
Kernel.Bind<IUnitOfWork<MyDbContext>>().To<UnitOfWork<MyDbContext>>();
//Register Repository
Kernel.Bind(x => x
.FromAssemblyContaining<MyDbContext>()
.SelectAllClasses()
.InheritedFrom(typeof(IRepository<>))
.BindDefaultInterface());
}
}
(3) Program.cs
static void Main()
{
using (IKernel kernel = new StandardKernel(new NinjectBindings()))
{
var config = new JobHostConfiguration()
{
JobActivator = new NinjectJobActivator(kernel)
};
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
// Timer Trigger
config.UseTimers();
var host = new JobHost(config);
//// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
}
(4) CallScopedParameter
public sealed class CallScopedParameter : IParameter
{
/// <summary>
///
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is CallScopedParameter;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="target"></param>
/// <returns></returns>
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
/// <summary>
///
/// </summary>
public string Name
{
get { return typeof(CallScopedParameter).Name; }
}
/// <summary>
/// this is very important
/// </summary>
public bool ShouldInherit
{
get { return true; }
}
}
(5) Azure Web Job Function
public void DoSomething([TimerTrigger("*/30 * * * * *")] TimerInfo timer, TextWriter log)
{
try
{
var tempdetails = _sampleRepository.SearchFor(x=> DateTime.UtcNow > x.DateTo);
foreach (var detail in tempdetails)
{
if (detail.ID == 2)
{
detail.ID = 5;
}
_sampleRepository.Update(detail);
}
_unitOfWork.Commit();
}
catch (Exception ex)
{
log.WriteLine(ex.Message);
}
}

ASP.NET MVC dependency injection?

UnitOfWorkManager
public class UnitOfWorkManager : IUnitOfWorkManager
{
private bool _isDisposed;
private readonly BEMContext _context;
public UnitOfWorkManager(IBEMContext context)
{
// http://stackoverflow.com/questions/3552000/entity-framework-code-only-error-the-model-backing-the-context-has-changed-sinc
Database.SetInitializer<BEMContext>(null);
_context = context as BEMContext;
}
/// <summary>
/// Provides an instance of a unit of work. This wrapping in the manager
/// class helps keep concerns separated
/// </summary>
/// <returns></returns>
public IUnitOfWork NewUnitOfWork()
{
return new UnitOfWork(_context);
}
/// <summary>
/// Make sure there are no open sessions.
/// In the web app this will be called when the injected UnitOfWork manager
/// is disposed at the end of a request.
/// </summary>
public void Dispose()
{
if (!_isDisposed)
{
_context.Dispose();
_isDisposed = true;
}
}
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly BEMContext _context;
private readonly IDbTransaction _transaction;
private readonly ObjectContext _objectContext;
/// <summary>
/// Constructor
/// </summary>
public UnitOfWork(BEMContext context)
{
_context = context;
// In order to make calls that are overidden in the caching ef-wrapper, we need to use
// transactions from the connection, rather than TransactionScope.
// This results in our call e.g. to commit() being intercepted
// by the wrapper so the cache can be adjusted.
// This won't work with the dbcontext because it handles the connection itself, so we must use the underlying ObjectContext.
// http://blogs.msdn.com/b/diego/archive/2012/01/26/exception-from-dbcontext-api-entityconnection-can-only-be-constructed-with-a-closed-dbconnection.aspx
_objectContext = ((IObjectContextAdapter)_context).ObjectContext;
if (_objectContext.Connection.State != ConnectionState.Open)
{
_objectContext.Connection.Open();
_transaction = _objectContext.Connection.BeginTransaction();
}
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void Commit()
{
_context.SaveChanges();
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
// http://blog.oneunicorn.com/2011/04/03/rejecting-changes-to-entities-in-ef-4-1/
foreach (var entry in _context.ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Modified:
entry.State = EntityState.Unchanged;
break;
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Deleted:
// Note - problem with deleted entities:
// When an entity is deleted its relationships to other entities are severed.
// This includes setting FKs to null for nullable FKs or marking the FKs as conceptually null (don’t ask!)
// if the FK property is not nullable. You’ll need to reset the FK property values to
// the values that they had previously in order to re-form the relationships.
// This may include FK properties in other entities for relationships where the
// deleted entity is the principal of the relationship–e.g. has the PK
// rather than the FK. I know this is a pain–it would be great if it could be made easier in the future, but for now it is what it is.
entry.State = EntityState.Unchanged;
break;
}
}
}
public void Dispose()
{
if (_objectContext.Connection.State == ConnectionState.Open)
{
_objectContext.Connection.Close();
}
}
}
LanguageRepository
public class LanguageRepository : ILanguageRepository
{
private readonly BEMContext _context;
public LanguageRepository(IBEMContext context)
{
_context = context as BEMContext;
}
public Language Insert(Language language)
{
_context.Language.Add(language);
return language;
}
}
LocalizationService
public class LocalizationService : ILocalizationService
{
private readonly ILanguageRepository _languageRepository;
public LocalizationService(ILanguageRepository languageRepository)
{
_languageRepository = languageRepository;
}
public void CreateLanguage(Language language)
{
_languageRepository.Insert(language);
}
}
BaseController
public class BaseController : Controller
{
protected readonly IUnitOfWorkManager _unitOfWorkManager;
public BaseController(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
LocalizationController
public class LocalizationController : BaseController
{
private readonly ILocalizationService _localizationService;
public LocalizationController(ILocalizationService localizationService, IUnitOfWorkManager unitOfWorkManager)
: base(unitOfWorkManager)
{
_localizationService = localizationService;
}
public ActionResult AddLanguage()
{
return View();
}
[HttpPost]
public ActionResult AddLanguage(LanguageModel model)
{
try
{
if (ModelState.IsValid)
{
using (var UnitOfWork = _unitOfWorkManager.NewUnitOfWork())
{
try
{
var language = Mapper.Map<LanguageModel, Language>(model);
_localizationService.CreateLanguage(language);
UnitOfWork.Commit();
}
catch (Exception ex)
{
UnitOfWork.Rollback();
throw new Exception(ex.Message);
}
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return View(model);
}
}
I cant add language to DB. Because service and unitOfWork classes use different context. Service change the context, but UnitOfWork save changes on another context. I mean following:
context in _localizationService.CreateLanguage(language);
context in UnitOfWork
Of cource, change does not effect db(I cant add entity to db). Because there are two different DbContex. I hope, I can explain, but I dont know how can I ask this question. How Can I solve this problem.
EDIT
I use Unity.MVC4 for IOC like following
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IBEMContext, BEMContext>();
container.RegisterType<ILocalizationService, LocalizationService>();
container.RegisterType<ILanguageRepository, LanguageRepository>();
container.RegisterType<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
container.RegisterType<ILocaleResourceValueRepository, LocaleResourceValueRepository>();
container.RegisterType<IUnitOfWorkManager, UnitOfWorkManager>();
}
}
Unity is creating new instances of the dependencies, which means that as both IUnitOfWorkManager and ILocalizationService are instantiated, their dependencies are instantiated separately. Find a way to share the dependency on the context; ideally only the unit of work should be really aware of it, and anything else should go through the unit of work to make changes to the context, I think.
My solution is:
/// <summary>
/// Bind the given interface in request scope
/// </summary>
public static class IOCExtensions
{
public static void BindInRequestScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
}
public static void BindInSingletonScope<T1, T2>(this IUnityContainer container) where T2 : T1
{
container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
}
}
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
container.BindInRequestScope<IBEMContext, BEMContext>();
container.BindInRequestScope<ILocalizationService, LocalizationService>();
container.BindInRequestScope<ILanguageRepository, LanguageRepository>();
container.BindInRequestScope<ILocaleResourceKeyRepository, LocaleResourceKeyRepository>();
container.BindInRequestScope<ILocaleResourceValueRepository, LocaleResourceValueRepository>();
container.BindInRequestScope<IUnitOfWorkManager, UnitOfWorkManager>();
}
}

mvc 4 mef import/export confusion

I'm having a difficult time wrapping my head around Mef and how imports and exports work. My project structure is as follows.
Projects:
MefMVPApp (Main MVC 4 app)
MefMVCFramework.Common(Interfaces shared between the projects)
MefMVCDemo.Plugins.OrderStatus (pluggable area.)
MefMVCDemo.Plugins.Data (Repository for OrderStatus)
OrderStatus.Models(domain models shared between the projects)
The goal of the main Mvc App will be to host plug-able areas via mef.
The OrderStatus Area has a controller called OrderStatusController and is decorated with the Export Attribute and a ImportingConstructor.
[Export(typeof(IController))]
[ExportMetadata("controllerName", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private readonly IRepository<OrderStatusApp.OrderStatusResponse>_repository ;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View();
}
}
IRepository is a class in the MefMVCFramework.Common assembly and will be used for generic CRUD operations.
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Add(T entity);
int SaveOrUpdate(T entity);
bool Delete(T entity);
bool Delete(int id);
}
The MefMVCDemo.Plugins.Data assembly contains a Class called OrderManagementRepository that inherents for the generic repository and is marked with an Export Attribute.
[Export(typeof(IRepository<OrderStatusApp.OrderStatusResponse>))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderManagementRepository : IRepository<OrderStatusApp.OrderStatusResponse>
{
private readonly JsonServiceClient _client;
public OrderManagementRepository()
{
_client = new JsonServiceClient("http://localhost:52266");
}
public IEnumerable<OrderStatusApp.OrderStatusResponse> GetAll()
{
throw new NotImplementedException("Can not get all");
}
public OrderStatusApp.OrderStatusResponse GetById(int id)
{
throw new NotImplementedException();
}
public void Add(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
}
Using Mefx tool I am able to see my parts and there are no rejection.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /parts
MefMVCDemo.Plugins.Data.OrderManagementRepository
mefMVCDemo.Plugins.OrderStatus.Controllers.OrderStatusController
MefMVCDemo.Plugins.OrderStatus.Verbs.OrderStatusVerb
I can see my import.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /imports
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
Now when browse my main mvc site with the /orderstatus uri I get the following error:
No parameterless constructor defined for this object.
Adding a default constructor to the OrderStatusController that takes no overloads doesn't seem to work.
I guess the question is what am I doing wrong? Why does my interface in the constructor all way end up being null and why is there an mvc error about the "No parameterless constructor defined for this object".
The default controller factory in MVC tries to create the controller using a parameterless constructor. If you want to change this behavior, then you need to create your own custom controller factory.
Here is an example of a ControllerFactory using imports/exports on controllers
I'm using MEF for importing some parts to my app but my controllers are not imported/exported, so I created the following controller factory
public class ControllerFactory : IControllerFactory
{
private readonly CompositionContainer _container;
private IControllerFactory _innerFactory;
/// <summary>
/// Constructor used to create the factory
/// </summary>
/// <param name="container">MEF Container that will be used for importing</param>
public ControllerFactory(CompositionContainer container)
{
_container = container;
_innerFactory = new DefaultControllerFactory();
}
/// <summary>
/// Method used for create the controller based on the provided name. It calls the
/// constructor of the controller passing the MEF container
/// </summary>
/// <param name="requestContext">Context of the request</param>
/// <param name="controllerName">Name of the controller provided in the route</param>
/// <returns>The controller instance</returns>
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = FindControllerByName(controllerName);
var args = new object[] { this._container };
var controller = (IController)Activator.CreateInstance(controllerType, args);
return controller;
}
/// <summary>
/// This methods looks into the current Assembly for the Controller type
/// </summary>
/// <param name="name">The controller name provided in the route</param>
/// <returns>The controller type</returns>
private static Type FindControllerByName(string name){
var a = Assembly.GetAssembly(typeof(ControllerFactory));
var types = a.GetTypes();
Type type = types.Where(t => t.Name == String.Format("{0}Controller", name)).FirstOrDefault();
return type;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return System.Web.SessionState.SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
Thank you pollirrata for pointing me in the right direction.
I had to change a few things to get this to work.
1.) I added an interface called INameMetadata to my MefMVCFramework.Common project.
public interface INameMetadata
{
string Name { get; }
}
2.) Modified My ExportMetadata Tag on my controller export to be Name, OrderStatus.
[Export(typeof(IController))]
[ExportMetadata("Name", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private IRepository<OrderStatusApp.OrderStatusResponse> _repository;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetById(47985);
return View(model);
}
}
3.) Created the MefControllerFactory (based off what pollirrata posted but modified to look for the Metadata)
public class MefControllerFactory : IControllerFactory
{
private string _pluginPath;
private readonly DirectoryCatalog _catalog;
private readonly CompositionContainer _container;
private DefaultControllerFactory _defaultControllerFactory;
public MefControllerFactory(string pluginPath)
{
_pluginPath = pluginPath;
_catalog = new DirectoryCatalog(pluginPath);
_container = new CompositionContainer(_catalog);
_defaultControllerFactory = new DefaultControllerFactory();
}
public MefControllerFactory(CompositionContainer compositionContainer)
{
_container = compositionContainer;
_defaultControllerFactory = new DefaultControllerFactory();
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//IController controller = null;
var controller = _container.GetExports<IController,INameMetadata>()
.Where(e=>e.Metadata.Name.Equals(controllerName))
.Select(e=>e.Value).FirstOrDefault();
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
public SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
4.) I created a Class called MefConfig in Main MVC app and moved it to the App_Start Dir.
public static class MefConfig
{
public static void RegisterMef()
{
//var builder = new RegistrationBuilder();
//builder.ForTypesDerivedFrom<IRepository<OrderStatusApp.OrderStatusResponse>>().Export<IRepository<IRepository<OrderStatusApp.OrderStatusResponse>>>();
var directoryCatalog = new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll");
var container = new CompositionContainer(directoryCatalog, true);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
//Working
//ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(HostingEnvironment.MapPath("~/bin")));
// Install MEF dependency resolver for MVC
var resolver = new MefDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver = resolver;
var d = container.GetExportedValues<IRepository<OrderStatusApp.OrderStatusResponse>>();
//Mefx.
try
{
//var ci = new CompositionInfo(aggregateCatalog, container);
var ci = new CompositionInfo(directoryCatalog, container);
var partDef = ci.GetPartDefinitionInfo(typeof(IRepository<OrderStatusApp.OrderStatusResponse>));
//var possibleCauses = partDef.FindPossibleRootCauses();
var stringWriter = new StringWriter();
CompositionInfoTextFormatter.Write(ci, stringWriter);
var compStatString = stringWriter.ToString();
}
catch
{
}
MvcApplication.ActionVerbs = container.GetExports<IActionVerb, IActionVerbMetadata>();
}
}
5.) Load the Mefconfig from the global.asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//Register Mef
MefConfig.RegisterMef();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}

WCF services, loading data from database

I have some problem with WCF services in asp.net mvc application.
Help me please!
When I run my project some data for some entities are loading from database, but some data are not loading(Internal server error).
Code and description of exceptions:
EntityTypeController:
public HttpResponseMessage GetAll()
{
//for debug
var t = EntityTypeService.GetAll();
//some exception here!
/*
The request channel timed out while waiting for a reply after 00:00:59.9979999.
Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding.
The time allotted to this operation may have been a portion of a longer timeout.
*/
//SendTimeout is 00:30:00 for all services.
or
/*The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.*/
or
/*An error occurred while receiving the HTTP response to http://localhost:18822/Services/Department.svc.
This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an
HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.*/
return CreateResponse<IEnumerable<EntityType>>(EntityTypeService.GetAll);
}
EntityTypeService:
public class EntityTypeService : BaseService<Base.Entities.EntityType>, IEntityTypeService
{
public IQueryable<Base.Entities.EntityType> GetAll()
{
var t = Repository.Get();
return t;
}
}
Repository:
public class Repository<TEntity>: IRepository<TEntity> where TEntity: BaseEntity
{
[Inject]
public IDataContext Context { get; set; }
[Inject]
public IDatabase DataSource { get; set; }
/// <summary>
/// Get entities from repository
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="orderBy">Order by property</param>
/// <param name="includeProperties"></param>
/// <returns></returns>
public virtual IQueryable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
var query = Context.Set<TEntity>().AsQueryable();
// Apply filter
filter.Do((s) => {
query = query.Where(filter);
});
// Apply includes
foreach (var includeProperty in includeProperties.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
// Apply order by if need
var result = orderBy
.With(x => x(query))
.Return(x => x, query);
return result;
}
/// <summary>
/// Find entity by key
/// </summary>
/// <param name="id">Identificator</param>
/// <returns>Entity</returns>
public virtual TEntity GetById(object id)
{
//throw new NotImplementedException();
return Context.Set<TEntity>().Find(id);
}
/// <summary>
/// Add new entity to repository
/// </summary>
/// <param name="entity">New entity</param>
public virtual void Insert(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
/// <summary>
/// Updated entity in repositoy
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Update(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
/// <summary>
/// Remove entity from rpository
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Delete(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Set<TEntity>().Remove(entity);
Context.Entry(entity).State = EntityState.Deleted;
Context.SaveChanges();
}
/// <summary>
/// Return models error
/// </summary>
/// <returns></returns>
public IEnumerable<object> GetValidationModelErrors()
{
return Context.GetValidationErrors();
}
/// <summary>
/// Attach entity
/// </summary>
/// <param name="entity"></param>
protected void AttachIsNotAttached(TEntity entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
{
var attached = Context.Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();
attached.Do((s) => {
Context.Entry(s).State = EntityState.Detached;
});
Context.Set<TEntity>().Attach(entity);
}
}
}
By default the wcf config has many limitations like the max size of array, and the exception doesnt exactly what happens,maybe in your case it's due to the default wcf config, I advice you to use svcTraceViewer, it will help you a lot to understand what happen exactly with an explicit messages.

Question about Interfaces and DI?

I am using the Service/Repository/EF/POCO pattern in a MVC app, and had a couple questions about the Interfaces.
1) Should I make an Interface per Service?
2) Should I make an Interface per Repository?
Or, should I have a generic interface per layer (IService(Of T), IRepository(Of T)).
What I dont understand is how in the controller say, it takes a IService(Of Category) interface in it's constructor, how do I implements the methods in the concrete class?
Public Class HomeController
Inherits System.Web.Mvc.Controller
Private _Service As IService(Of Category)
Public Sub New(ByVal Service As IService(Of Category))
_Service = Service
End Sub
Function Index() As ActionResult
Return View()
End Function
End Class
The _Service does not have the methods of my concrete CategoryService class?
Make any sense?
Use concrete interface for service. If your services can be described by generic interface you most probably don't need them at all. Generic interface is often used for repositories because repositories usually offer same core methods.
For myself, I use a generic session object that is strongly type, this class is in my domain project witch contains all my domain classes. You should take a look at this post : http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx using Code-First approach.
Hope it helps!
Here my code for my Session class :
public class EFSession : ISession
{
DbContext _context;
public EFSession(DbContext context)
{
_context = context;
}
public void CommitChanges()
{
_context.SaveChanges();
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new()
{
var query = All<T>().Where(expression);
foreach (var item in query)
{
Delete(item);
}
}
public void Delete<T>(T item) where T : class, new()
{
_context.Set<T>().Remove(item);
}
public void DeleteAll<T>() where T : class, new()
{
var query = All<T>();
foreach (var item in query)
{
Delete(item);
}
}
public void Dispose()
{
_context.Dispose();
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new()
{
return All<T>().FirstOrDefault(expression);
}
public IQueryable<T> All<T>() where T : class, new()
{
return _context.Set<T>().AsQueryable<T>();
}
public void Add<T>(T item) where T : class, new()
{
_context.Set<T>().Add(item);
}
public void Add<T>(IEnumerable<T> items) where T : class, new()
{
foreach (var item in items)
{
Add(item);
}
}
/// <summary>
/// Do not use this since we use EF4, just call CommitChanges() it does not do anything
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
public void Update<T>(T item) where T : class, new()
{
//nothing needed here
}
}

Resources