MVC - Interface as Controller Action parameter - model-view-controller

I want to pass one of a number of classes that implement an interface from my view back to my controller action. I use an ActionLink in my view passing the instance to my action, but it naturally fails because MVC cannot deal with interfaces via default model binding.
So :
<%=Html.ActionLink(flow.Source.Name, "Get", new {container=flow.Source})%>
is in a loop and each flow.Source conforms to IContainer.
public class Flow
{
public virtual IContainer Source { get; private set; }
}
public interface IContainer
{
//members here
}
public class File : IContainer
{}
public class Worksheet : IContainer
{}
Basically I want to call an action method :
public ActionResult Get(IContainer container)
{
// Do something
}
The reason being that I need to retrieve the state of the current container passed to my action method from the database. I use NHibernate and have entities mapped on a table per entity, so have one for File and one for Worksheet for example, so need to able to decide which data access class to use. Make sense? Probably not!
Can this be done without moving towards a base class Container? Can I stick with an interface being passed to my action method and resolve the subtype instance passed in place of the interface?
Any help with this would be gratefully appreciated.

An interface needs 'some' concrete implementation to reference when you would call your class. I think judging by your post you are aware of this : )
With that said there is 'kinda' of an approach handled here where you just create your own model binder that has to know about (or how) to map to and create a concrete type (either directly or by dependency injection)
ASP.NET MVC - Custom Model Binder on Interface Type

Related

How do you initialize application state at startup and access it from controllers in MVC 6?

Let's say I have a class called MySiteConfiguration in which I have a bunch of, you guessed it, configuration data. This data will not change over the course of the application's runtime after it has been loaded.
My goal would be to construct an instance of this class at startup and access it from my controller actions. I do not want to construct the class more than once, as this should not be needed.
To do this in WebApi 2 for instance, I would:
Instantiate the class in my application start method.
Store the instance on the HttpConfiguration.Properties
Create a ControllerBase class which inherits from ApiController.
Override the Initialize(HttpControllerContext) method in my ControllerBase class. This override would read the configuration instance from HttpControllerContext.Configuration.Properties and assign it to a property / field in ControllerBase.
Any controller needing access to the configuration instance would inherit ControllerBase and reference the base property. Not so bad...
With that said, this pattern does not work in the new framework from what I can tell. There is no initialize method to override on MVC 6's new Controller class. I'm also not familiar enough with the new Startup.cs patterns and middleware available to know where to start with this problem.
Thanks.
Use dependency injection. Register a singleton service that has your data, and then use constructor injection on your controllers to acquire the service instance.
First, define a service. A service can really be any class or interface.
public class MyConfigService {
// declare some properties/methods/whatever on here
}
In your Startup.cs do something like this:
services.AddSingleton<MyConfigService>();
(Note that there are other overloads of AddSingleton depending on your scenario.)
And then consume it in each controller:
public MyController : Controller {
public MyController(MyConfigService myService) {
// do something with the service (read some data from it, store it in a private field/property, etc.
}
}
How about using the application state to store your configuration data?
protected void Application_Start()
{
Application["MySiteConfiguration"] = new MySiteConfiguration();
}
You can then access your configuration data from inside your controllers.
public ActionResult Index()
{
var config = HttpContext.Application["MySiteConfiguration"] as MySiteConfiguration;
}

C#, MVC3, How to use the non-generic DBSet with a runtime defined type?

I'm new to MVC and the EF. My app is a simple code-first with several POCO classes and a DBContext like this:
public class ExpDefContext : DbContext
{
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
...
The problem: I need to add to my data model an entity-set that its type is built at runtime from user input, meaning I have no idea of its data structure.
I read the non-generic Dbset class is made just for this, so I added to the context:
public DbSet Log { get; set; }
...and created a constructor for the context that accepts the runtime-type and sets the new Dbset:
public ExpDefContext(Type LogRecType)
{
Log = Set(LogRecType);
}
(the type by the way is built using Reflection.Emit).
In the controller I create the type (named LogRec) and pass it to a new DBContext instance. Then I create a LogRec instance and try to Add it to the database:
Type LogRec;
LogRec = LogTypeBuilder.Build(dbExpDef, _experimentID);
var dbLog = new ExpDefContext(LogRec);
var testRec = LogRec.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
dbLog.Log.Add(testRec);
dbLog.SaveChanges();
and I get an exception from the dbLog.Log.Add(testRec):
The entity type LogRec is not part of the model for the current context
What am I doing wrong?
Is there a better way to do this (preferably without diving too deep into the Entity Framework)?
Thanks
I suspect that EF only reflects over the generic DbSet<T> properties in your derived DbContext and ignores any non-generic DbSet properties when the model is created in memory.
However, an alternative approach might be to use the Fluent API in OnModelCreating to add your dynamic type as an entity to the model.
First of all you can add a type to the model only when the model is built in memory for the first time your AppDomain is loaded. (A model is built only once per AppDomain.) If you had a default constructor of the context in addition to the overloaded constructor and had created and used a context instance using this default constructor your model would have been built with only the static types and you can't use the dynamic type as entity anymore as long as the AppDomain lives. It would result in exactly the exception you have.
Another point to consider is the creation of the database schema. If your type is unknown at compile time the database schema is unknown at compile time. If the model changes due to a new type on the next run of your application you will need to update the database schema somehow, either by recreating the database from scratch or by defining a custom database initializer that only deletes the LogRec table and creates a new table according to the new layout of the LogRec type. Or maybe Code-First Migrations might help.
About the possible solution with Fluent API:
Remove the DbSet and add a Type member instead to the context and override OnModelCreating:
public class ExpDefContext : DbContext
{
private readonly Type _logRecType;
public ExpDefContext(Type LogRecType)
{
_logRecType = LogRecType;
}
public DbSet<Experiment> Experiments { get; set; }
public DbSet<Research> Researches { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
entityMethod.MakeGenericMethod(_logRecType)
.Invoke(modelBuilder, new object[] { });
}
}
DbModelBuilder doesn't have a non-generic Entity method, hence dynamic invocation of the generic Entity<T> method is necessary.
The above code in OnModelCreating is the dynamic counterpart of...
modelBuilder.Entity<LogRec>();
...which would be used with a static LogRec type and that just makes the type as entity known to EF. It is exactly the same as adding a DbSet<LogRec> property to the context class.
You should be able to access the entity set of the dynamic entity by using...
context.Set(LogRecType)
...which will return a non-generic DbSet.
I have no clue if that will work and didn't test it but the idea is from Rowan Miller, member of the EF team, so I have some hope it will.

Constructor injection of a View Model instance used as an Action method parameter

When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).

ASP.NET MVC 3: Validating model when information external to the model is required

What's a good way to validate a model when information external to the model is required in order for the validation to take place? For example, consider the following model:
public class Rating {
public string Comment { get; set; }
public int RatingLevel { get; set; }
}
The system administrator can then set the RatingLevels for which a comment is required. These settings are available through a settings service.
So, in order to fully validate the model I need information external to it, in this case the settings service.
I've considered the following so far:
Inject the service into the model. The DefaultModelBinder uses System.Activator to create the object so it doesn't go through the normal dependency resolver and I can't inject the service into the model without creating a new model binder (besides which, that doesn't feel like the correct way to go about it).
Inject the service into an annotation. I'm not yet sure this is possible but will investigate further soon. It still feels clumsy.
Use a custom model binder. Apparently I can implement OnPropertyValidating to do custom property validation. This seems the most preferable so far though I'm not yet sure how to do it.
Which method, above or not, is best suited to this type of validation problem?
Option 1 doesn't fit. The only way it would work would be to pull in the dependency via the service locator anti-pattern.
Option 2 doesn't work. Although I couldn't see how this was possible because of the C# attribute requirements, it is possible. See the following for references:
Resolving IoC Container Services for Validation Attributes in ASP.NET MVC
NInjectDataAnnotationsModelValidatorProvider
Option 3: I didn't know about this earlier, but what appears to be a very powerful way to write validators is to use the ModelValidator class and a corresponding ModelValidatorProvider.
First, you create your custom ModelValidatorProvider:
public class CustomModelValidatorProvider : ModelValidatorProvider
{
public CustomModelValidatorProvider(/* Your dependencies */) {}
public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
{
if (metadata.ModelType == typeof(YourModel))
{
yield return new YourModelValidator(...);
}
}
}
ASP.NET MVC's IDependencyResolver will attempt to resolve the above provider, so as long as it's registered with your IoC container you won't need to do anything else. And then the ModelValidator:
public class EntryRatingViewModelValidatorMvcAdapter : ModelValidator
{
public EntryRatingViewModelValidatorMvcAdapter(
ModelMetadata argMetadata,
ControllerContext argContext)
: base(argMetadata, argContext)
{
_validator = validator;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (/* error condition */)
{
yield return new ModelValidationResult
{
MemberName = "Model.Member",
Message = "Rating is required."
};
}
}
}
As the provider is retrieved through the IDependencyResolver and the provider has full control over the returned ModelValidators I was easily able to inject the dependencies and perform necessary validation.
You could try fluent validation. It supports asp.net mvc and DI so you can inject external services into your validators.
Assuming that you want both client and server-side validation of the model based upon the values returned from the service, I would opt for 2., Inject the service into an annotation.
I give some sample code in my response to this question about adding validators to a model. The only additional step in your case is that you will need to inject your service into your class inheriting from DataAnnotationsModelValidatorProvider.
What about just simply using IValidateableObject and in that method determine if validation is appropriate or not and setting the errors there?
How do I use IValidatableObject?

How can I load multiple controller factories and pass control on to the next one?

I've created a generic controller factory to load entities from the database by parsing out the url:
entity/products/123456.htm
However, I'd like to be able to load an actual controller if the entity is not found, or to override the default entity behavior if necessary by creating a physical controller, instead of a "virtual" one created by the URL pattern.
Right now, in global.asax.cs I'm doing:
ControllerBuilder.Current.SetControllerFactory(typeof(EntityControllerFactory));
How can I, either in EntityControllerFactory, or here in global.asax.cs, pass control on to another factory, in the event that I'd like MVC's default controller/action scheme to take over?
You could create a composite IControllerFactory implementation:
public class EntityControllerFactory : IControllerFactory {
private IControllerFactory defaultFactory = new DefaultControllerFactory();
public IController CreateController(RequestContext requestContext, string controllerName) {
if(needsCustomLogic) {
// do your custom logic here and return appropriate result
} else {
return defaultFactory.CreateController(requestContext, controllerName);
}
}
// same for the other methods on IControllerFactory
}
This works because by default the value of ControllerBuilder.Current.GetControllerFactory() is an instance of DefaultControllerFactory.
You might also consider making your factory more future-proof (in case a new version of MVC starts returning a different type from GetControllerFactory; unlikely but it could happen) by getting the default instance and passing it into your factory:
// in Global.asax
var defaultFactory = ControllerBuilder.Current.GetControllerFactory();
ControllerBuilder.Current.SetFactory(new EntityControllerFactory(defaultFactory));

Resources