When you create a controller in MVC, you don't have to do any additional registration for it. Same goes with adding areas. As long as your global.asax has an AreaRegistration.RegisterAllAreas() call, no additional setup is necessary.
With AutoMapper, we have to register the mappings using some kind of CreateMap<TSource, TDestination> call. One can do these explicitly with the static Mapper.CreateMap, or by deriving from the AutoMapper.Profile class,overriding the Configure method, and calling CreateMap from there.
It seems to me like one should be able to scan an assembly for classes that extend from Profile like MVC scans for classes that extend from Controller. With this kind of mechanism, shouldn't it be possible to create mappings simply by creating a class that derives from Profile? Does any such library tool exist, or is there something built into automapper?
I don't know if such tool exists, but writing one should be pretty trivial:
public static class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => GetConfiguration(Mapper.Configuration));
}
private static void GetConfiguration(IConfiguration configuration)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var profiles = assembly.GetTypes().Where(x => x != typeof(Profile) && typeof(Profile).IsAssignableFrom(x));
foreach (var profile in profiles)
{
configuration.AddProfile((Profile)Activator.CreateInstance(profile));
}
}
}
}
and then in your Application_Start you could autowire:
AutoMapperConfiguration.Configure();
As a slight improvement to the answer from #Darin Dimitrov, in AutoMapper 5 you can give it a list of Assemblies to scan like this:
//--As of 2016-09-22, AutoMapper blows up if you give it dynamic assemblies
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => !x.IsDynamic);
//--AutoMapper will find all of the classes that extend Profile and will add them automatically
Mapper.Initialize(cfg => cfg.AddProfiles(assemblies));
Related
I am trying to create a generic controller like this:
[Route("api/[controller]")]
public class OrdersController<T> : Controller where T : IOrder
{
[HttpPost("{orderType}")]
public async Task<IActionResult> Create(
[FromBody] Order<T> order)
{
//....
}
}
I intend for the {orderType} URI segment variable to control the generic type of the controller. I'm experimenting with both a custom IControllerFactory and IControllerActivator, but nothing is working. Every time I try to send a request, I get a 404 response. The code for my custom controller factory (and activator) is never executed.
Evidently the problem is that ASP.NET Core expects valid controllers to end with the suffix "Controller", but my generic controller instead has the (reflection based) suffix "Controller`1". Thus the attribute-based routes it declares are going unnoticed.
In ASP.NET MVC, at least in its early days, the DefaultControllerFactory was responsible for discovering all the available controllers. It tested for the "Controller" suffix:
The MVC framework provides a default controller factory (aptly named DefaultControllerFactory) that will search through all the assemblies in an appdomain looking for all types that implement IController and whose name ends with "Controller."
Apparently, in ASP.NET Core, the controller factory no longer has this responsibility. As I stated earlier, my custom controller factory executes for "normal" controllers, but is never invoked for generic controllers. So there is something else, earlier in the evaluation process, which governs the discovery of controllers.
Does anyone know what "service" interface is responsible for that discovery? I don't know the customization interface or "hook" point.
And does anyone know of a way to make ASP.NET Core "dump" the names of all the controllers it discovered? It would be great to write a unit test that verifies that any custom controller discovery I expect is indeed working.
Incidentally, if there is a "hook" which allows generic controller names to be discovered, it implies that route substitutions must also be normalized:
[Route("api/[controller]")]
public class OrdersController<T> : Controller { }
Regardless of what value for T is given, the [controller] name must remain a simple base-generic name. Using the above code as an example, the [controller] value would be "Orders". It would not be "Orders`1" or "OrdersOfSomething".
Note
This problem could also be solved by explicitly declaring the closed-generic types, instead of generating them at run time:
public class VanityOrdersController : OrdersController<Vanity> { }
public class ExistingOrdersController : OrdersController<Existing> { }
The above works, but it produces URI paths that I don't like:
~/api/VanityOrders
~/api/ExistingOrders
What I had actually wanted was this:
~/api/Orders/Vanity
~/api/Orders/Existing
Another adjustment gets me the URI's I'm looking for:
[Route("api/Orders/Vanity", Name ="VanityLink")]
public class VanityOrdersController : OrdersController<Vanity> { }
[Route("api/Orders/Existing", Name = "ExistingLink")]
public class ExistingOrdersController : OrdersController<Existing> { }
However, although this appears to work, it does not really answer my question. I would like to use my generic controller directly at run-time, rather than indirectly (via manual coding) at compile-time. Fundamentally, this means I need ASP.NET Core to be able to "see" or "discover" my generic controller, despite the fact that its run-time reflection name does not end with the expected "Controller" suffix.
What happens by default
During the controller discovery process, your open generic Controller<T> class will be among the candidate types. But the default implementation of the IApplicationFeatureProvider<ControllerFeature> interface, DefaultControllerTypeProvider, will eliminate your Controller<T> because it rules out any class with open generic parameters.
Why overriding IsController() doesn't work
Replacing the default implementation of the IApplicationFeatureProvider<ControllerFeature> interface, in order to override DefaultControllerTypeProvider.IsController(), will not work. Because you don't actually want the discovery process to accept your open generic controller (Controller<T>) as a valid controller. It is not a valid controller per se, and the controller factory wouldn't know how to instantiate it anyway, because it wouldn't know what T is supposed to be.
What needs to be done
1. Generate closed controller types
Before the controller discovery process even starts, you need to generate closed generic types from your open generic controller, using reflection. Here, with two sample entity types, named Account and Contact:
Type[] entityTypes = new[] { typeof(Account), typeof(Contact) };
TypeInfo[] closedControllerTypes = entityTypes
.Select(et => typeof(Controller<>).MakeGenericType(et))
.Select(cct => cct.GetTypeInfo())
.ToArray();
We now have closed TypeInfos for Controller<Account> and Controller<Contact>.
2. Add them to an application part and register it
Application parts are usually wrapped around CLR assemblies, but we can implement a custom application part providing a collection of types generated at runtime. We simply need to have it implement the IApplicationPartTypeProvider interface. Therefore, our runtime-generated controller types will enter the controller discovery process like any other built-in type would.
The custom application part:
public class GenericControllerApplicationPart : ApplicationPart, IApplicationPartTypeProvider
{
public GenericControllerApplicationPart(IEnumerable<TypeInfo> typeInfos)
{
Types = typeInfos;
}
public override string Name => "GenericController";
public IEnumerable<TypeInfo> Types { get; }
}
Registration in MVC services (Startup.cs):
services.AddMvc()
.ConfigureApplicationPartManager(apm =>
apm.ApplicationParts.Add(new GenericControllerApplicationPart(closedControllerTypes)));
As long as your controller derives from the built-in Controller class, there is no actual need to override the IsController method of the ControllerFeatureProvider. Because your generic controller inherits the [Controller] attribute from ControllerBase, it will be accepted as a controller in the discovery process regardless of its somewhat bizarre name ("Controller`1").
3. Override the controller name in the application model
Nevertheless, "Controller`1" is not a good name for routing purposes. You want each of your closed generic controllers to have independent RouteValues. Here, we will replace the name of the controller with that of the entity type, to match what would happen with two independent "AccountController" and "ContactController" types.
The model convention attribute:
public class GenericControllerAttribute : Attribute, IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
Type entityType = controller.ControllerType.GetGenericArguments()[0];
controller.ControllerName = entityType.Name;
}
}
Applied to the controller class:
[GenericController]
public class Controller<T> : Controller
{
}
Conclusion
This solution stays close to the overall ASP.NET Core architecture and, among other things, you will keep full visibility of your controllers through the API Explorer (think "Swagger").
It has been tested successfully with both conventional and attribute-based routing.
Short Answer
Implement IApplicationFeatureProvider<ControllerFeature>.
Question and Answer
Does anyone know what "service" interface is responsible for [discovering all available controllers]?
The ControllerFeatureProvider is responsible for that.
And does anyone know of a way to make ASP.NET Core "dump" the names of all the controllers it discovered?
Do that within ControllerFeatureProvider.IsController(TypeInfo typeInfo).
Example
MyControllerFeatureProvider.cs
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace CustomControllerNames
{
public class MyControllerFeatureProvider : ControllerFeatureProvider
{
protected override bool IsController(TypeInfo typeInfo)
{
var isController = base.IsController(typeInfo);
if (!isController)
{
string[] validEndings = new[] { "Foobar", "Controller`1" };
isController = validEndings.Any(x =>
typeInfo.Name.EndsWith(x, StringComparison.OrdinalIgnoreCase));
}
Console.WriteLine($"{typeInfo.Name} IsController: {isController}.");
return isController;
}
}
}
Register it during startup.
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.ConfigureApplicationPartManager(manager =>
{
manager.FeatureProviders.Add(new MyControllerFeatureProvider());
});
}
Here is some example output.
MyControllerFeatureProvider IsController: False.
OrdersFoobar IsController: True.
OrdersFoobarController`1 IsController: True.
Program IsController: False.
<>c__DisplayClass0_0 IsController: False.
<>c IsController: False.
And here is a demo on GitHub. Best of luck.
Edit - Adding Versions
.NET Version
> dnvm install "1.0.0-rc2-20221" -runtime coreclr -architecture x64 -os win -unstable
NuGet.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear/>
<add key="AspNetCore"
value="https://www.myget.org/F/aspnetvnext/api/v3/index.json" />
</packageSources>
</configuration>
.NET CLI
> dotnet --info
.NET Command Line Tools (1.0.0-rc2-002429)
Product Information:
Version: 1.0.0-rc2-002429
Commit Sha: 612088cfa8
Runtime Environment:
OS Name: Windows
OS Version: 10.0.10586
OS Platform: Windows
RID: win10-x64
Restore, Build, and Run
> dotnet restore
> dotnet build
> dotnet run
Edit - Notes on RC1 vs RC2
This might not be possible is RC1, because DefaultControllerTypeProvider.IsController() is marked as internal.
Application Feature Providers examine application parts and provide features for those parts. There are built-in feature providers for the following MVC features:
Controllers
Metadata Reference
Tag Helpers
View Components
Feature providers inherit from IApplicationFeatureProvider, where T is the type of the feature. You can implement your own feature providers for any of MVC's feature types listed above. The order of feature providers in the ApplicationPartManager.FeatureProviders collection can be important, since later providers can react to actions taken by previous providers.
By default, ASP.NET Core MVC ignores generic controllers (for example, SomeController). This sample uses a controller feature provider that runs after the default provider and adds generic controller instances for a specified list of types (defined in EntityTypes.Types):
public class GenericControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
{
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
{
// This is designed to run after the default ControllerTypeProvider,
// so the list of 'real' controllers has already been populated.
foreach (var entityType in EntityTypes.Types)
{
var typeName = entityType.Name + "Controller";
if (!feature.Controllers.Any(t => t.Name == typeName))
{
// There's no 'real' controller for this entity, so add the generic version.
var controllerType = typeof(GenericController<>)
.MakeGenericType(entityType.AsType()).GetTypeInfo();
feature.Controllers.Add(controllerType);
}
}
}
}
The entity types:
public static class EntityTypes
{
public static IReadOnlyList<TypeInfo> Types => new List<TypeInfo>()
{
typeof(Sprocket).GetTypeInfo(),
typeof(Widget).GetTypeInfo(),
};
public class Sprocket { }
public class Widget { }
}
The feature provider is added in Startup:
services.AddMvc()
.ConfigureApplicationPartManager(p =>
p.FeatureProviders.Add(new GenericControllerFeatureProvider()));
By default, the generic controller names used for routing would be of the form GenericController`1[Widget] instead of Widget. The following attribute is used to modify the name to correspond to the generic type used by the controller:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
namespace AppPartsSample
{
// Used to set the controller name for routing purposes. Without this convention the
// names would be like 'GenericController`1[Widget]' instead of 'Widget'.
//
// Conventions can be applied as attributes or added to MvcOptions.Conventions.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class GenericControllerNameConvention : Attribute, IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
if (controller.ControllerType.GetGenericTypeDefinition() !=
typeof(GenericController<>))
{
// Not a GenericController, ignore.
return;
}
var entityType = controller.ControllerType.GenericTypeArguments[0];
controller.ControllerName = entityType.Name;
}
}
}
The GenericController class:
using Microsoft.AspNetCore.Mvc;
namespace AppPartsSample
{
[GenericControllerNameConvention] // Sets the controller name based on typeof(T).Name
public class GenericController<T> : Controller
{
public IActionResult Index()
{
return Content($"Hello from a generic {typeof(T).Name} controller.");
}
}
}
Sample: Generic controller feature
To get a list of controllers in RC2, just get ApplicationPartManager from DependencyInjection and do this:
ApplicationPartManager appManager = <FROM DI>;
var controllerFeature = new ControllerFeature();
appManager.PopulateFeature(controllerFeature);
foreach(var controller in controllerFeature.Controllers)
{
...
}
I've got a ConfigurationReader class that I'm trying to wire up using StructureMap or AutoFac (I haven't settled on which container I'm using).
public class ConfigurationReader {
private string _filePath;
public ConfigurationReader(string filePath){
this._filePath = filePath;
}
public IList<Baz> ListStuff(){
//do something with _filePath;
}
}
There will be 1..n to instances of this class based on how the app is configured (web.config will contain a delimited list of files). I'm looking for an extension point in either IoC container that would allow me to leverage them to create instances of ConfigurationReader.
Well, in AutoFac you can just register each one in the Container (during Application_Start for example).
Whenever you need to read all configurations you can add a dependency to IEnumerable<ConfigurationReader> (or IConfigurationReader if you decide to extract an interface) and it will provide you with all of them.
Something like this:
var builder = new ContainerBuilder();
foreach(var file in ConfigurationManager.AppSettings[yourKey].Split(','))
{
var fileName = file;
builder.Register(c => new ConfigurationReader(fileName));
}
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
If you extract interfaces, then you may want to register by adding the .AsImplementedInterfaces() or .As<IConfigurationReader>() at end as well.
We're trying to use MEF 2 with ASP.NET MVC 4 to support an extensible application. There are really 2 parts to this question (hope that's okay SO gods):
How do we use Microsoft.Composition and the MVC container code (MEF/MVC demo source) to replace Ninject as our DI for ICoreService, ICoreRepository, IUnitOfWork, and IDbContext?
It looks like we can't use both Ninject and the MVC container at the same time (I'm sure many are saying "duh"), so we'd like to go with MEF, if possible. I tried removing Ninject and setting [Export] attributes on each of the relevant implementations, spanning two assemblies in addition to the web project, but Save() failed to persist with no errors. I interpreted that as a singleton issue, but could not figure out how to sort it out (incl. [Shared]).
How do we load multiple assemblies dynamically at runtime?
I understand how to use CompositionContainer.AddAssemblies() to load specific DLLs, but for our application to be properly extensible, we require something more akin to how I (vaguely) understand catalogs in "full" MEF, which have been stripped out from the Microsoft.Composition package (I think?); to allow us to load all IPluggable (or whatever) assemblies, which will include their own UI, service, and repository layers and tie in to the Core service/repo too.
EDIT 1
A little more reading solved the first problem which was, indeed, a singleton issue. Attaching [Shared(Boundaries.HttpRequest)] to the CoreDbContext solved the persistence problem. When I tried simply [Shared], it expanded the 'singletonization' to the Application level (cross-request) and threw an exception saying that the edited object was already in the EF cache.
EDIT 2
I used the iterative assembly loading "meat" from Nick Blumhardt's answer below to update my Global.asax.cs code. The standard MEF 2 container from his code did not work in mine, probably because I'm using the MEF 2(?) MVC container. Summary: the code listed below now works as desired.
CoreDbContext.cs (Data.csproj)
[Export(typeof(IDbContext))]
[Shared(Boundaries.HttpRequest)]
public class CoreDbContext : IDbContext { ... }
CoreRepository.cs (Data.csproj)
[Export(typeof(IUnitOfWork))]
[Export(typeof(ICoreRepository))]
public class CoreRepository : ICoreRepository, IUnitOfWork
{
[ImportingConstructor]
public CoreRepository(IInsightDbContext context)
{
_context = context;
}
...
}
CoreService.cs (Services.csproj)
[Export(typeof(ICoreService))]
public class CoreService : ICoreService
{
[ImportingConstructor]
public CoreService(ICoreRepository repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
}
...
}
UserController.cs (Web.csproj)
public class UsersController : Controller
{
[ImportingConstructor]
public UsersController(ICoreService service)
{
_service = service;
}
...
}
Global.asax.cs (Web.csproj)
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
CompositionProvider.AddAssemblies(
typeof(ICoreRepository).Assembly,
typeof(ICoreService).Assembly,
);
// EDIT 2 --
// updated code to answer my 2nd question based on Nick Blumhardt's answer
foreach (var file in System.IO.Directory.GetFiles(Server.MapPath("Plugins"), "*.dll"))
{
try
{
var name = System.Reflection.AssemblyName.GetAssemblyName(file);
var assembly = System.Reflection.Assembly.Load(name);
CompositionProvider.AddAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
}
}
If I understand you correctly, you're looking for code that will load all assemblies from a directory and load them into the container; here's a skeleton for doing that:
var config = new ContainerConfiguration();
foreach (var file in Directory.GetFiles(#".\Plugins", "*.dll"))
{
try
{
var name = AssemblyName.GetAssemblyName(file);
var assembly = Assembly.Load(name);
config.WithAssembly(assembly);
}
catch
{
// You'll need to craft exception handling to
// your specific scenario.
}
}
var container = config.CreateContainer();
// ...
Hammett discusses this scenario and shows a more complete version in F# here: http://hammett.castleproject.org/index.php/2011/12/a-decent-directorycatalog-implementation/
Note, this won't detect assemblies added to the directory after the application launches - Microsoft.Composition isn't intended for that kind of use, so if the set of plug-ins changes your best bet is to detect that with a directory watcher and prompt the user to restart the app. HTH!
MEF is not intended to be used as DI framework. Which means that you should separate your "plugins" (whatever they are) composition from your infrastructure dependencies, and implement the former via MEF and the latter via whatever DI framework you prefer.
I think there are a little misunderstandings on what MEF can and can't do.
Originally MEF was conceived as purely an extensibility architecture, but as the framework evolved up to its first release, it can be fully supported as a DI container also. MEF will handle dependency injection for you, and does so through it's ExportProvider architecture. It is also entirely possible to use other DI frameworks with MEF. So in reality there are a number of ways things could be achieved:
Build a NinjectExportProvider that you can plug into MEF, so when MEF is searching for available exports, it will be able to interrogate your Ninject container.
Use an implementation of the Common Services Locator pattern to bridge between MEF and Ninject or vice versa.
Because you are using MEF for the extensibility, you'll probably want to use the former, as this exposes your Ninject components to MEF, which in turn exposes them to your plugins.
The other thing to consider, which is a bit disappointing, is in reality there isn't a lot of room for automagically plugging in of features ala Wordpress on ASP.NET. ASP.NET is a compiled and managed environment, and because of that you either resort to late-binding by loading assemblies manually at runtime, or you restart the application to pick up the new plugins, which sort of defeats the object of being able to plug new extensions in through the application.
My advice, is plan your architecture to pick up any extensibility points as startup and assume that any core changes will require a deployment and application restart.
In terms of the direct questions asked:
The CompositionProvider accepts in instance of ContainerConfiguration which is used internally to create the CompositionContainer used by the provider. So you could use this as the point by which you customise how you want your container to be instantiated. The ContainerConfiguration supports a WithProvider method:
var configuration = new ContainerConfiguration().WithProvider(new NinjectExportDescriptorProvider(kernel));
CompositionProvider.SetConfiguration(configuration);
Where NinjectExportDescriptorProvider might be:
public class NinjectExportDescriptorProvider: ExportDescriptorProvider
{
private readonly IKernel _kernel;
public NinjectExportDescriptorProvider(IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(
CompositionContract contract, DependencyAccessor dependencyAccessor)
{
var type = contract.ContractType;
if (!_kernel.GetBindings(type).Any())
return NoExportDescriptors;
return new[] {
new ExportDescriptorPromise(
contract,
"Ninject Kernel",
true, // Hmmm... need to consider this, setting it to true will create it as a shared part, false as new instance each time,
NoDependencies,
_ => ExportDescriptor.Create((c, o) => _kernel.Get(type), NoMetadata)) };
}
}
}
Note: I have not tested this, this is all theory, and is based on the example AppSettingsExportDescriptorProvider at: http://mef.codeplex.com/wikipage?title=ProgrammingModelExtensions
It's different from using the standard ExportProvider, because using the CompostionProvider is built around lightweight composition. But essentially you're wrapping up access to your Ninject kernel and making it available to your CompositionContainer.
As with adding a specific new provider (see above), you can use the ContainerConfiguration to read the available assemblies, probably something like:
var configuration = new ContainerConfiguration().WithAssemblies(AppDomain.GetAssemblies())
Again, I haven't tested all of this, but I hope it at least points you in the right direction.
A multitenancy application is an app that is shared by multiple organizations (medical practices, law offices..) and each organization, in turn, has it's own users. They all log on a centralized environment.
To be identified within the application, the organization must be expressed in the URL. There are two major URL forms for that. Subdomains and folders:
[tenancy_name].appname.com/projects/view/123
www.appname.com/[tenancy_name]/projects/view/123
At first I tried the second because this solution does not involve dealing with DNSs. But then the problem: Everytime the developer needs to express an url (#Html.Action or #Url.Action) it has to explicitly pass the [tenancy_name]. This adds an unwanted overhead to the development. A possible workaround would be to implement custom versions of these HTML helpers that automatically take into account the tenancy name. I'm considering this option but looking for something more straitghtforward. I also realized ASP.NET MVC automatically passes route values for outgoing URLs but only when the controller and action are the same as the current. It would be nice if route values were always passed.
To implement the first option, the subdomain one, I think, I would need some third party DNS manager. I heard of DynDNS and took a look at it but I thought it unclear how they work just looking at their site. Would I need to trigger a web-service to tell them to create another subdomain everytime a new tenancy is created? Do they support wildcards in the DNS? Do they work on Windows Azure or shared hostings?
I'm here looking for directions. Which way should I go?
look this project on codeplex, the "baseRoute" maybe can help you.
http://mvccoderouting.codeplex.com/
Regards.
Following made View resolution trivial in our app:
How to use:
For views that you need to overload for a particular tenant - treat them same way as custom display modes:
Following will work:
Index.cshtml
Index.cust2.mobile.cshtml
or
Partials/CustomerAgreement.cust1.cshtml
Partials/CustomerAgreement.cust2.cshtml
as far as I remember display/editor templates also work same way
Known issues:
1. You have to create Layouts for all combinations of primary+secondary (for whatever MVC-reason)
2. Regardless of what resharper is saying about its support of display modes - it does not support "." as part of the display mode name (here's an issue to track progress http://youtrack.jetbrains.com/issue/RSRP-422413)
//put in application start --------
DisplayModeProvider.Instance.Modes.Clear();
foreach (var displayMode in GetDisplayModes())
{
DisplayModeProvider.Instance.Modes.Add(displayMode);
}
private IEnumerable<IDisplayMode> GetDisplayModes()
{
return new CompoundDisplayModeBuilder()
.AddPrimaryFilter(_ => dependencyResolver.GetService(typeof(IResolveCustomerFromUrl)).GetName(),
"cust1",
"cust2")
.AddSecondaryFilter(ctx => ctx.Request.Browser.IsMobileDevice, "mobile")
.BuildDisplayModes();
}
//end of application start part
//and the mode builder implementation:
public class CompoundDisplayModeBuilder
{
private readonly IList<DefaultDisplayMode> _primaryDisplayModes = new List<DefaultDisplayMode>();
private readonly IList<DefaultDisplayMode> _secondaryDisplayModes = new List<DefaultDisplayMode>();
//NOTE: this is just a helper method to make it easier to specify multiple tenants in 1 line in global asax
//You can as well remove it and add all tenants one by one, especially if resolution delegates are different
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, string> contextEval, params string[] valuesAsSuffixes)
{
foreach (var suffix in valuesAsSuffixes)
{
var val = suffix;
AddPrimaryFilter(ctx => string.Equals(contextEval(ctx), val, StringComparison.InvariantCultureIgnoreCase), val);
}
return this;
}
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_primaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public CompoundDisplayModeBuilder AddSecondaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_secondaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public IEnumerable<IDisplayMode> BuildDisplayModes()
{
foreach (var primaryMode in _primaryDisplayModes)
{
var primaryCondition = primaryMode.ContextCondition;
foreach (var secondaryMode in _secondaryDisplayModes)
{
var secondaryCondition = secondaryMode.ContextCondition;
yield return new DefaultDisplayMode(primaryMode.DisplayModeId + "." + secondaryMode.DisplayModeId){
ContextCondition = ctx => primaryCondition(ctx) && secondaryCondition(ctx)
};
}
}
foreach (var primaryFilter in _primaryDisplayModes)
{
yield return primaryFilter;
}
foreach (var secondaryFilter in _secondaryDisplayModes)
{
yield return secondaryFilter;
}
yield return new DefaultDisplayMode();
}
}
I am finding difficult to test EntityFramework 4 .I am using it using the database first approach,too late now to move to poco.Needed to deliver pretty quickly,no time to learn properly as usual.
I have implemented the repository pattern with unit of work but I am finding difficult to inject a repository into my Service layer so that I can test the behaviour of my business layer service ,validation etc... without hitting the db.
but I am incurring in many little problems.
In order to inject the Repository into the service(constructor) the calling layer need to have a reference to the DAL (EF Entities) . I dont want this
If i have many repositories EG CustomerRepository ,EmployeeRepository than I need to have as many constructors as repositories so that I can inject the repository.
3.Not sure where to go from here. I have not found any example on the net where they inject the repository into the service using EF4. All the examples I have seen they mock the repository on it's own,which is not good to me.
I need to test my service layer/BizLayer without hitting the database.
The all thing is just not testable and adds so many dependencies and problems.
Noddy example I have put together
public class DepartmentServiceLibrary
{
private readonly IDepartmentRepository _departmentRepository;
public DepartmentServiceLibrary(IDepartmentRepository departmentRepository)
{
_departmentRepository = departmentRepository;
}
public List<DepartmentDto> GetDepartments()
{
return DeparmentBiz.GetDepartments();
}
private DeparmentBL _departmentBiz;
private DeparmentBL DeparmentBiz
{
get
{
return _departmentBiz ?? new DeparmentBL(_departmentRepository);
}
}
}
//internal class
internal class DeparmentBL
{
private readonly IDepartmentRepository _departmentRepository;
public DeparmentBL(IDepartmentRepository departmentRepository)
{
_departmentRepository = departmentRepository;
}
public List<DepartmentDto> GetDepartments()
{
using (var ctx = new AdventureWorksContext())
{
var uow = new UnitOfWork(ctx);
_departmentRepository.UnitOfWork = uow;
var query = _departmentRepository.GetAll();
return query.Select(dpt => new DepartmentDto
{
DepartmentId = dpt.DepartmentID,
Name = dpt.Name,
GroupName = dpt.GroupName
}).ToList();
}
}
}
The following TestMethod requires me to add a ref to the dal which defeats the point
[TestMethod]
public void Should_be_able_to_call_get_departments()
{
var mock = new Mock<IDepartmentRepository>();
var expectedResult = new List<Department>(); //Dependency to DAL as Department is a EF Entity generated by EF.
mock.Setup(x => x.GetAll()).Returns(expectedResult);
var companyService = new MyCompanyBL(mock.Object); //InternalVisibileTO
var departments = companyService.GetAll();
//assert removed for brevity
Any suggestions or examples out there that shows how to do it?
thanks
}
The short answer is - since you're not using POCOs, all your layers will have a reference to your DAL.
Without POCOs, you use code generation, which means EF creates the model classes in the Model.edmx.designer.cs file.
An option (haven't tried this - off the top of my head) is to manually project the EF entities into DTOs.
So your Repository might do this:
public List<OrderDTO> GetOrdersForCustomer(int customerId)
{
return _ctx.Orders
.Where(x => x.CustomerId == customerId)
.ToList()
.Select(x => new OrderDTO { // left to right copy });
}
The OrderDTO class could be in a separate assembly, which the repository references, as well as your other projects. So the other projects would work off the DTO assembly, and wouldn't require a reference to the Repository.
But here you're projecting into classes everywhere (basically doing POCO, but manually, and with more work) left to right copying of properties - very painful.
However, that is an option.
Honestly - it does not take long to move to POCOs.
There is a T4 template which will generate the POCOs for you - you could be up and running in a matter of minutes.
And since you're already using dependency injection and repository, you should either bite the bullet and change to POCOs, or keep the reference to the DAL.
Something similar in terms of code can be seen here in GitHub
and detail explanation can be found in TechNet