MVC - The call is ambiguous between the following methods or properties - model-view-controller

I can't figure out why I'm getting this error. I only have one class with one method in my project that looks like:
public static class Extensions
{
public static string Chop(this string s, int length)
{
...
return "";
}
}
I'm calling this in my View like so: #item.PostContent.Chop(20)
It's giving me a compiler error:
The call is ambiguous between the following methods or properties: 'Extensions.Chop(string, int)' and 'Extensions.Chop(string, int)'
Any help is appreciated. Thank you.

I'm guessing that you have put your Extensions class file in an App_Code folder, is that correct? If so, move it outside of App_Code and get rid of the folder. App_Code has no place in MVC applications which by default are created as Web Application projects as opposed to Web Site projects.
Web Applications are compiled to a dll at build time, and anything in App_Code is also compiled into a separate dll at run time. Hence there being two methods and the ambiguity.

Related

How to add new DLL locations to WebAPI's controller discovery?

ASP.NET WebAPI has a much appreciated ability to discover ApiController classes in external DLLs even if those DLLs are not referenced. For example, I may have MyWebApiProject that has a set of ApiControllers. I could then create a completely separate project called MyApiProjectPlugin that contains ApiController classes also. I have been able to add the MyApiProjectPlugin.dll file to the bin folder with the first MyApiProject.dll and the original project will discover all the controllers in the plugin project. I really like that ability.
However, What I would like to do is be able to add the plugin project to a sub directory inside of the bin folder. Something like bin/plugins. When I tried this, the original MyApiProject was unable to discover the plugin's controllers.
Is there a simple way to get WebAPI to look for ApiController classes in the bin's subdirectories? If I can avoid rewriting a controller factory from scratch I would like to.
You can write an assembly resolver.
public class PluginsResolver : DefaultAssembliesResolver
{
public override ICollection<Assembly> GetAssemblies()
{
List<Assembly> assemblies = new List<Assembly>(base.GetAssemblies());
assemblies.Add(Assembly.LoadFrom(#"<Path>\MyApiProjectPlugin.dll"));
return assemblies;
}
}
In the Register method in WebApiConfig, register the resolver.
config.Services.Replace(typeof(IAssembliesResolver), new PluginsResolver());

Http.EnumRouteContraint must implement System.Web.Routing.IRouteConstraint

I'm using AttributeRouting in my Web API project. I've installed the AttributeRouting for Web API. I want to define an Enum route constraint so I setup my AttributeRoutingHttpConfig config as follows:
using System.Reflection;
using System.Web.Http;
using AttributeRouting.Web.Http.Constraints;
using AttributeRouting.Web.Http.WebHost;
using MyProject.Data.Models;
[assembly: WebActivator.PreApplicationStartMethod(typeof(PhantasyTour.AttributeRoutingHttpConfig), "Start")]
namespace MyProject
{
public static class AttributeRoutingHttpConfig
{
public static void RegisterRoutes(HttpRouteCollection routes)
{
routes.MapHttpAttributeRoutes(
config =>
{
config.AddRoutesFromAssembly(Assembly.GetExecutingAssembly());
config.InlineRouteConstraints.Add("ListType", typeof(EnumRouteConstraint<ListType>));
});
}
public static void Start()
{
RegisterRoutes(GlobalConfiguration.Configuration.Routes);
}
}
}
When I fire up my application I immediately receive the following error:
The constraint "AttributeRouting.Web.Http.Constraints.EnumRouteConstraint`1[[MyProject.Data.Models.ListType, MyProject.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]" must implement System.Web.Routing.IRouteConstraint
I've looked at the source code for the AttributeRouting.Web.Http.Constraints.EnumRouteConstraint and confirmed that it implements IHttpRouteConstraint which presumably is the WebAPI equivalent of IRouteConstraint in the MVC namespace.
Does anyone know what I'm doing wrong and how I can get this working?
UPDATE:
I attempted to create a completely blank Web Application and add only WebAPI and AttributeRouting for WebAPI references. Despite having absolutely no references to MVC assemblies, I still receive the same error message. I did discover however that there is another EnumRouteConstraint found in the AttributeRouting.Web.Constraints namespace which works perfectly. It doesn't appear to be MVC specific since it is located in the Core AttributeRouting assembly. I would love to know why there are two different EnumRouteConstraint classes when only one of them works. But that is a question for another time.
It is interesting that the exception you get refers to the MVC interface from the namespace System.Web.Routing.
I would take it as a clue and look at all the references in your project, any place in the config where MVC Routes and Http Routes could have been mixed up.
If possible and if you have any at all, try removing all references to MVC (or System.Web.Routing for a start), and MVC flavour of attribute routing (if it's a separate dll).

Make .dbml file in a class library use connectionstring from config file without subclassing it

I have a class library that is referenced from a web application. The class library defaults to using TestProject.Properties.Settings.Default.NFU_Custom_Website_DataConnectionString1. I would like it to get the connectionstring from ConfigurationManager.ConnectionStrings.
I can't change the .designer.cs file because it will get overwritten. If possible I would like to avoid creating a class which inherits from the .dbml file and setting the connection string in there. My boss recommends creating a web application project and deleting all of the default.aspx etc files from it and using that instead of a class library, is this a viable solution?
Thanks
Joe
You can do that. We had done that inadvertently and I got this problem when I switched to a class library. Rather than switch back I found the answer here: Point connectionstring in dbml to app.config
In summary:
Set the connection property of the DBML to (none) in the designer. This will remove the default constructor from the DB Context class.
Create a partial class for your DB Context with a default constructor:
using System.Configuration;
namespace TestProject
{
public partial class MyDBContext
{
public MyDBContext() : base(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString)
{
OnCreated();
}
}
}

MEF and MVC 3 - how to load embedded views dynamically from mef container?

I'm building an MVC 3 application where MEF is used. The main idea is to have plug-in mechanism where models, controllers and views are loaded dynamically during runtime from mef container.
Each plugin/module consists of two assemblies:
Module1.Data.dll (contains definitions of models)
Module1.Web.dll (contains controllers and views)
and are put in the Plugins directory inside web application bin:
WebApp/Bin/Plugins/Module1.Data.dll
WebApp/Bin/Plugins/Module1.Web.dll
WebApp/Bin/Plugins/Module2.Data.dll
WebApp/Bin/Plugins/Module2.Web.dll
WebApp/Bin/Plugins/ModuleCore.Data.dll
WebApp/Bin/Plugins/ModuleCore.Web.dll
etc...
There is also core module that is referenced by all other modules: ModuleCore.Data.dll and respectively ModuleCore.Web.dll.
Then, in Global.asax, container is build in the following way:
AggregateCatalog catalog = new AggregateCatalog();
var binCatalog = new DirectoryCatalog(HttpRuntime.BinDirectory, "Module*.dll");
var pluginsCatalot = new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins"), "Module*.dll");
catalog.Catalogs.Add(binCatalog);
catalog.Catalogs.Add(pluginsCatalot);
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
AppDomain.CurrentDomain.AppendPrivatePath(Path.Combine(HttpRuntime.BinDirectory, "Plugins"));
CustomViewEngine is created and registered and used for finding views in module assembly:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
controller factory for loading controllers from container:
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(_container));
and also custom virtual path provider for getting assemblies from container:
HostingEnvironment.RegisterVirtualPathProvider(new ModuleVirtualPathProvider());
Ok, so the whole infrastructure for handling pluggable models, controllers and views are ready. Now everything works... except one thing - strongly typed views.
To ilustrate the problem in more details, let's prepare the scene:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Data.UserDto)
Now we do the following:
Run the application and go to HOST/ShowUser/Index (action method Index is executed on ShowUserController and view Index.cshtml is fetched)
After the view Index.cshtml is fetched - compilation starts (by RazorBuildProvider)
Exceptions is thrown: "cannot find Data type in namespace Module1", in other words UserDTO couldn't be found during building the view dynamically
So it seems that compiler/builder didnt look through bin/Plugins folder for Module1.Data.dll, because when I copied this file into bin folder - it worded fine.
Question/problem: why builder didn't look into bin/Plugins folder even though this directory was added by AppDomain.CurrentDomain.AppendPrivatePath method?
How to add private paths for assembly builder once so that plugins folder will be taken into consideration??
I have managed to do some work around by creating CustomRazorBuildProvider that overrides standard one:
public class CustomRazorBuildProvider : RazorBuildProvider
{
public override void GenerateCode(System.Web.Compilation.AssemblyBuilder assemblyBuilder)
{
Assembly a = Assembly.LoadFrom(Path.Combine(HttpRuntime.BinDirectory, "Plugins", "Module1.Data.dll"));
assemblyBuilder.AddAssemblyReference(a);
base.GenerateCode(assemblyBuilder);
}
}
but the drawback of this solution is that everytime the view is compiled, references to all assemblies in Plugins folder need to be added, which can cause performance issues later on when lots of plugins will be used.
Any nicer solutions?
Here is a thought.
If you follow the View Model Pattern then instead of sending the DTO's straight to the view use a ViewModel that is would be located in the same assembly as the View.
So Instead of:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Data.UserDto)
You would have:
UserDTO model is located in Module1.Data.dll
ShowUserController.cs is located in Module1.Web.dll/Controllers/
UserVM located in Module1.Web.dll/ViewModels
Index.cshtml is located in Module1.Web.dll/Views/ShowUser (with declared #model Module1.Web.ViewModels.UserVM)
Have the Controller Map your DTO's to ViewModels
See AutoMapper to help with the Mapping

Testing If a class is being activated using WebActivator and if add a IModelBinder to ModelBinderProviders.BinderProviders.

Dear fellows from Stack Exchange.
I'm trying to test if my Custom Model Binder is being added to the ModelBinderProviders.BinderProviders collection.
I decided to activate this through WebActivator, to avoid messing global.asax,
Everything works fine, but the Test:
I tried using the WebActivator.ActivationManager.Run() method, but my things weren't loaded.
I've something like this in my test:
[TestMethod]
public void TemplateModelBinderProvider_Should_Be_Registered_In_BinderProviders()
{
WebActivator.ActivationManager.Run();
IModelBinderProvider templateModelBinderProvider = ModelBinderProviders.BinderProviders.
Where(x => x is TemplateModelBinderProvider).
FirstOrDefault();
Assert.IsNotNull(templateModelBinderProvider);
}
And this is my app_Start class:
[assembly: WebActivator.PreApplicationStartMethod(typeof(MVC.App_Start.MVCBindings), "Start")]
namespace MVC.App_Start
{
public static class MVCBindings
{
public static void Start()
{
ModelBinderProviders.BinderProviders.Add(new TemplateModelBinderProvider());
}
}
}
Sorry you have problems with the piece of code I wrote.
I don't have access to the source code right now but will take a look in the evening (UK time).
Do you think you could send me your solution so I could replicate it locally? My email is jkonecki at gmail.com
UPDATE
I have received your source code but unfortunately it contains references to libraries I cannot obtain so I cannot compile it.
I have created a separate solution (emailed to you) with MVC3 web app and unit test projects that uses your custom model binder provide. There are two tests that prove that WebActivatorManager.Run method properly registers a custom provider.
Try debugging your unit test to make sure that Run method calls your static Start method.
WebActivator source code is here - you might want to get it and step through.

Resources