I am trying to refactor a solution that includes 2 MVC projects: a "User-Experience" project and a corresponding "Content Management System project"
I have set up an independent C# library -- "Services" -- project in the same solution space to do many things. I want to share Html extension methods used by both web projects by placing them in this "neutral" project so that there are no dependencies between the two MVC projects.
I have a problem with using the RouteValueDictionary object (to.MergeAttributes) in this class library. I've included a reference to Sytem.Web.Routing in the Services project, but when I attempt to include a using statement at the top of the helper-class file, VStudio refuses to recognize the declaration ("Cannot resolve symbol 'Routing'").
This makes intuitive sense -- only a web project that can engage in URL routing should need a RouteValueDictionary.
But it appears common to use the RouteValueDictionary object inside htmlhelper classes because it can take an anonymous object consisting of name-value pairs and convert it to a Dictionary in its constructor like this:
tag.MergeAttributes(new RouteValueDictionary(htmlAttributes));
...thus providing a way of passing in any number of tag attribute name-value pairs into a tag and a relatively simple syntax for calling the helper in the view:
<%:Html.SomeTag(Model.Id, new {#class = "myClass", title = "myTitle"} %>
So if I can't use the RouteValueDictionary in my helper class, I'm forced with a more cumbersome syntax for my views, like this:
<%:Html.SomeTag(Model.Id, new Dictionary<string, string>{ {"class", "myClass"}, {"title", "myTitle} }%>
QUESTION: So, is there a way to re-write the helper class to get the simpler view syntax without using RouteValueDictionary, or is there a way to allow the use of RouteValueDictionary in a C# library project?
What project target have you given your class? The only thing I can think of is that you have your project target set to .NET 3.5 Client Profile which doesn't support any web stuff.
UPDATE: Based on your comments, the final resolution was to add a reference to System.Web.dll to your project.
In .NET 4.0, the System.Web.Routing.dll is type-forwarded to System.Web.dll. This is because the functionality in System.Web.Routing.dll from .NET 3.5 was migrated to the core System.Web assembly. The System.Web.Routing.dll (v4.0) exists to satisfy type-forwarding (to maintain backwards compatibility when migrating projects).
When you add a reference to System.Web.Routing.dll to your .NET 4.0 project, you also need to add a reference to System.Web.dll.
Related
I am building a class library in C# with .NET 6 (possibly 7) for use with ASP.NET Core sites.
I would like this class library to (among other things), contain a configuration UI (kinda like how Swashbuckle builds up an OpenAPI UI - I checked out the source code and couldn't quite wrap my mind around that portion).
I figured creating controllers/views in the class library would be the way to go.
The controllers are working automatically (although, I'm mildly concerned about routing conflicts -- what if I have a /foo/bar route in my class library and the project using this library also has a /foo/bar route?).
The views, however, do not seem to be added automatically. I've tried embedding and following the folder conventions, but I still get the error:
An unhandled exception occurred while processing the request.
InvalidOperationException: The view 'Index' was not found. The
following locations were searched: /Views/Foo/Index.cshtml
/Views/Shared/Index.cshtml
Here's the relevant portion of the class library:
What do I have to do to get these Views loaded/parsed/working from a class library? (Or is there a better alternative to doing what I'm trying to do without Views?)
ApplicationParts is the answer, but I found the documentation a little hard to follow, so here's what I did:
var assembly = typeof(FooController).Assembly;
#this.AddControllersWithViews()
.AddApplicationPart(assembly)
.AddRazorRuntimeCompilation();
#this.Configure<MvcRazorRuntimeCompilationOptions>(options =>
{ options.FileProviders.Add(new EmbeddedFileProvider(assembly)); });
The key for me was .AddRazorRuntimeCompilation(); and the following line to add a file provider.
Does ASP.NET Core implement IConfiguration access to config values?
Most likely my question arose because I don't understand what exactly ASP.NET Core is. Well, I know it's a web framework, Not sure, but looks like it is a namespace in .NET, or a package... I know in php, a framework could be a set of classes (a namespace) or compiled library which is provided as an extension so I presume a similar approach in .NET.
Initially, I didn't intend to wrap my head around ASP.NET Core yet. I needed to store some config for my simple console C# application (VS Code and .NET Core). I've found a lot of topics (for example here: How to read values from config.json in Console Application) that to read JSON (recommended) config. Given that, I added three necessary nugget packages:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.FileExtensions;
using Microsoft.Extensions.Configuration.Json;
I need to use:
new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json").Build();
This returns an object that implements the IConfigurationRoot/IConfiguration interface. But all the examples are given in an ASP.NET Core context. I have a really simple app and I don't need any of ASP.NET functionality yet.
So I've tried to access IConfigurationRoot without ASP.NET. The resulting object stores values from config file, but does not have all methods of its interface to access them.
How to explain this in context of .NET namespaces? Does ASP.NET Core implement methods to access values from IConfiguration like Get<T>()?
If Microsoft.Extensions.Configuration is part of or heavily dependent on Microsoft.AspNetCore.App, why is it in different namespace?
If I add ASP.NET Core (NuGet package and namespaces), will it be an overkill?
Maybe I should use soemthing other than ConfigurationBuilder to read JSON?
Microsoft.Extensions.Configuration, like other packages in the Microsoft.Extensions namespace (e.g. Options, or DependencyInjection), are packages that were created as part of the ASP.NET Core framework. The way ASP.NET Core and all its related packages were built however is in a very modular way, so all the libraries can be used within the ASP.NET Core context, or without.
You have to understand those packages just as libraries. They are included in ASP.NET Core since the framework builds on top of them, but if you do not need the ASP.NET Core web framework, you can still use those libraries separately without any mention of ASP.NET Core. That’s actually why they live inside the Microsoft.Extensions namespace instead of Microsoft.AspNetCore: They are completely separate projects. Of course, development of those packages is done by the ASP.NET Core team and the design decisions of ASP.NET Core do affect how those extension packages evolve; but the team is very careful with these packages so that the general use is not affected.
So that all being said, how do you use these packages? Just like any other library, you just add a NuGet reference to it. Since Microsoft.Extensions.Configuration is the base library which does not come with any facility to load files, you also need Microsoft.Extensions.Configuration.Json if you want to load JSON files.
But then it’s really straight forward:
var configuration = new ConfigurationBuilder()
.AddJsonFile("config.json")
.Build();
// retrieve configuration values
Console.WriteLine(configuration["foo"]); // bar
Console.WriteLine(configuration["baz:bar"]); // qux
For this example, the config.json looked like this:
{
"foo": "bar",
"baz": {
"bar": "qux"
}
}
So you can just load the configuration like this. Be sure to still check the documentation though. It may be about the configuration used inside of ASP.NET Core but the underlying concepts still apply (e.g. how configuration paths look like, or how binding works).
Finally, note that this is really just meant for configuration. Loading data from JSON is just one of many configuration sources you can with Microsoft.Extensions.Configuration. But regardless of what provider you will use, you will end up with the same configuration format that has the concepts of sections and key paths.
If you came to the package while looking how to parse JSON, then it’s likely that you are looking at the wrong tool. If you want to parse JSON to retrieve a proper data structure, like you would use when using JSON as a way to serialize data, then you should look at something different. The most common solution for parsing JSON (serializing too) is using Json.NET which is a very powerful and flexible tool to deal with any kind of JSON data.
What I'm Doing Now
As it is now, every time I have a new project I need to work on (all of these projects use a pretty generalized base of functionality, but each have their custom functionality, quirks, etc.), I have to just copy the base project, and then modify the copied base project, to add the custom functionality.
The Idea
(I'm using ASP.NET MVC4, with Visual Studio 2012)
Basically, I'd like to have a "Base Project":
Base Project
-Controllers
-Models
-Views
Custom Project
-Controllers
-Models
-Views
that serves as the base for my MVC projects. Then, when I go to create a new project (spun off of the base project), I'd include the base project as a subproject to the solution, and in my new project, only have to write controllers/models/views that are different from the base (I'd like to just override those, or something similar).
This way, when I change the base project, the base projects of all of my different custom projects would be updated as well, this way I could update/bugfix/etc the base project, and not have to manually update each custom project to include this new base functionality.
The Problem
I understand that what I want is a little weird for MVC to handle, but I think I could get the whole routing engine to work the way I want, but I was just wondering if there were any solutions for this, that are a bit easier to implement. I feel like this is a fairly normal thing to want to do. Or am I thinking about the whole problem wrong?
What I've Tried
So far, I've created a base project, and a custom project, and I can get base functionality to appear in the custom project, if no Action is present in the custom controller, but the base project uses the custom project's View for that Action, whereas I need the base project to use it's own Action and View if they don't exist in the custom project.
Basically, if I go to:
http://mytest.com/Home/Index
And the base project has a HomeController, with an Index Action, but the custom project has neither of those, the routing engine will be unable to find a matching View for the base controller, because the views from the base project are not being copied to the output directory.
Conclusion
Basically I'm just looking for some general guidance. Am I looking at this the wrong way? Is it worth doing?
Also, if you find any misconceptions, please do correct me!
Thanks for your time.
This isn't weird for MVC at all. In fact, it's highly common. Just create a class library and put your models, controllers, helper classes, etc. in there. Then, you just add this class library as a reference in your other projects. The only complicated bit is sharing views, since Razor views are not compiled by default. However, for that, you can use Razor Generator.
I have an HtmlHelper extension method in an assembly separate from my MVC application assembly. Within the extension method I would like to get the version number of the MVC application assembly. Is this possible?
The calling assembly is the razor view dynamic assembly so that doesn't help. Is there some object nested within the HtmlHelper that can provide me with the version number of the MVC application assembly? I've been exploring the HtmlHelper class documentation but so far haven't found a solution to my problem.
Thanks!
This is a notoriously evil thing - because unfortunately there's no one specific reliable way to do it.
Since it's an MVC application, however, the chances are that it has a Global.asax.cs - therefore it has a locally defined HttpApplication class.
From within an html helper you can get to this:
public static string AppVersion(this HtmlHelper html)
{
var appInstance = html.ViewContext.HttpContext.ApplicationInstance;
//given that you should then be able to do
var assemblyVersion = appInstance.GetType().BaseType.Assembly.GetName().Version;
//note the use of the BaseType - see note below
return assemblyVersion.ToString();
}
Note
You might wonder why the code uses the BaseType of the application instance, and not simply the type. That's because the Global.asax.cs file is the primary type of the MVC application, but then Asp.Net dynamically compiles another HttpApplication type that inherits from that via the Global.asax.
As I said earlier; this works in most MVC sites because they should all have an application class defined in a Global.asax.cs file by convention (because that's the way the project is set up).
Just in case anyone comes across this, here is what worked for me (MVC5 VS2013). Enter straight into the view:
#ViewContext.HttpContext.ApplicationInstance.GetType().BaseType.Assembly.GetName().Version.ToString();
Just search for the assembly which should be the source for your version number
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name.Equals("MyDll")).First().GetName().Version.ToString();
I'm going to create a new PayPal project. Should I just create a regular Class Library project then add the reference to the WSDL? We are not using WCF. I just want to know what the best project type / template I should use if I'm going to share this project with lets say another WAP web project. I simply want to create wrappers for some of the WSDL that we'll be using in part of the PayPal API.
Yes a class library project seems the right thing to use if you're wrapping the code that consumes a web service.
Unless it's going to be tiny (say, just one class) in which case you might want to include it in an existing common project that is already used by both of your consuming projects, just to keep everything a little simpler.