What are the best usage of the following resource files.
Properties → Resources (Phil used this resource for localization in DataAnnotation)
App_GlobalResources folder
App_LocalResources folder
I also would like to know what is the difference between (1) and (2) in asp.net mvc application.
You should avoid App_GlobalResources and App_LocalResources.
Like Craig mentioned, there are problems with App_GlobalResources/App_LocalResources because you can't access them outside of the ASP.NET runtime. A good example of how this would be problematic is when you're unit testing your app.
K. Scott Allen blogged about this a while ago. He does a good job of explaining the problem with App_GlobalResources in ASP.NET MVC here.
If you go with the recommended solution (1) (i.e. as in K. Scott Allen's blog):
For those of you trying to use explicit localization expressions (aka declarative resource binding expressions), e.g. <%$ Resources, MyResource:SomeString %>
public class AppResourceProvider : IResourceProvider
{
private readonly string _ResourceClassName;
ResourceManager _ResourceManager = null;
public AppResourceProvider(string className)
{
_ResourceClassName = className;
}
public object GetObject(string resourceKey, System.Globalization.CultureInfo culture)
{
EnsureResourceManager();
if (culture == null)
{
culture = CultureInfo.CurrentUICulture;
}
return _ResourceManager.GetObject(resourceKey, culture);
}
public System.Resources.IResourceReader ResourceReader
{
get
{
// Not needed for global resources
throw new NotSupportedException();
}
}
private void EnsureResourceManager()
{
var assembly = typeof(Resources.ResourceInAppToGetAssembly).Assembly;
String resourceFullName = String.Format("{0}.Resources.{1}", assembly.GetName().Name, _ResourceClassName);
_ResourceManager = new global::System.Resources.ResourceManager(resourceFullName, assembly);
_ResourceManager.IgnoreCase = true;
}
}
public class AppResourceProviderFactory : ResourceProviderFactory
{
// Thank you, .NET, for providing no way to override global resource providing w/o also overriding local resource providing
private static Type ResXProviderType = typeof(ResourceProviderFactory).Assembly.GetType("System.Web.Compilation.ResXResourceProviderFactory");
ResourceProviderFactory _DefaultFactory;
public AppResourceProviderFactory()
{
_DefaultFactory = (ResourceProviderFactory)Activator.CreateInstance(ResXProviderType);
}
public override IResourceProvider CreateGlobalResourceProvider(string classKey)
{
return new AppResourceProvider(classKey);
}
public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
return _DefaultFactory.CreateLocalResourceProvider(virtualPath);
}
}
Then, add this to your web.config:
<globalization requestEncoding="utf-8" responseEncoding="utf-8" fileEncoding="utf-8" culture="en-US" uiCulture="en"
resourceProviderFactoryType="Vendalism.ResourceProvider.AppResourceProviderFactory" />
Properties → Resources can be seen outside of your views and strong types are generated when you compile your application.
App_* is compiled by ASP.NET, when your views are compiled. They're only available in the view. See this page for global vs. local.
Related
I need to create a nuget package which will contain shared views, controllers, js, and css files to be used across multiple other projects. Essentially a modular set of things like checkout or search pages that can be dropped into other site projects.
All the research I've done so far points to utilizing precompiled views with RazorGenerator but doesn't say much about the controllers, js, and css files.
Ideally a module's views and other files should be able to be overridden by the consuming host project but the files themselves should not be directly editable within the host project. Much like dlls referenced when other nuget packages are added.
The answers and posts about this type of subject I've found so far seem a bit dated.
Is there a cleaner, modern solution for creating an ASP.NET MVC module nuget package so that fully working pages are able to be shared across projects?
Controllers
Use area's and register those area's. Possibly this is not natively supported and you might need to overwrite some parts in mvc4. look at:
How do I register a controller that has been created in an AREA
http://netmvc.blogspot.be/2012/03/aspnet-mvc-4-webapi-support-areas-in.html
As long as the dll's are loaded in you can always register all classes that are subclasses of Controller with reflection (on application startup).
Razor
Precompiling is possible, but only really adviseable in dotnet core since it is a first class citizen there.
You could also add the views as content which is injected into the project.
Downside:
On update it overwrites the views (if you changed them you lost changes)
Pros:
On update you can merge both changes in git
Easily change the already existing razor pages
I found a solution that works for us. We have not yet fully implemented this so some unforeseen issues may still arise.
First, create an MVC project with the needed views, controllers, javascript, etc needed to match your package requirements. Each static file and view must be set as embedded resources in the project.
Then, add a class to serve these files on a virtual path provider. This will allow a consuming project to access the static files and views as if they were within the same project.
To enable custom routing an implementation of the RouteBase class will be required. This implementation needs to accept a string property for which virtual routes are based to allow the host to apply whichever route prefix is desired. For our example, the property will default to Booking with the associated architecture of views within our project to match.
Both the RouteBase implementation and the VirtualPath class will be instantiated within a setup method. This will allow a consuming project to call a single method to setup the booking engine. This method will take in the sites Route Collection and dynamic route property to append the custom routes. The method will also register the VirtualPathProvider to the HostingEnvironment object.
The consuming host can also override views and any other static file by simply having a file in a location within the host project that matches the path of the file or view in the booking engine.
Some Code Examples
RouteBase method which returns correct route values if an incoming route matches a virtual route.
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
// Trim the leading slash
var path = httpContext.Request.Path.Substring(1);
// Get the page that matches.
var page = GetPageList(httpContext)
.Where(x => x.VirtualPath.Equals(path))
.FirstOrDefault();
if (page != null)
{
result = new RouteData(this, new MvcRouteHandler());
// Optional - make query string values into route values.
AddQueryStringParametersToRouteData(result, httpContext);
result.Values["controller"] = page.Controller;
result.Values["action"] = page.Action;
}
// IMPORTANT: Always return null if there is no match.
// This tells .NET routing to check the next route that is registered.
return result;
}
RouteBase Virtual Route to NuGet Package Route mapping. New PageInfo objects created with dynamic virtual path string and references to real controller and action names. These are then stored in the http context cache.
private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext)
{
string key = "__CustomPageList";
var pages = httpContext.Cache[key];
if (pages == null)
{
lock (synclock)
{
pages = httpContext.Cache[key];
if (pages == null)
{
pages = new List<PageInfo>()
{
new PageInfo()
{
VirtualPath = string.Format("{0}/Contact", BookingEngine.Route),
Controller = "Home",
Action = "Contact"
},
};
httpContext.Cache.Insert(
key: key,
value: pages,
dependencies: null,
absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration: TimeSpan.FromMinutes(1),
priority: System.Web.Caching.CacheItemPriority.NotRemovable,
onRemoveCallback: null);
}
}
}
return (IEnumerable<PageInfo>)pages;
}
Booking Engine class setup method which does all the instantiating needed for the assembly.
public class BookingEngine
{
public static string Route = "Booking";
public static void Setup(RouteCollection routes, string route)
{
Route = route;
HostingEnvironment.RegisterVirtualPathProvider(
new EmbeddedVirtualPathProvider());
routes.Add(
name: "CustomPage",
item: new CustomRouteController());
}
}
EmbeddedVirtualFile
public override CacheDependency GetCacheDependency(string virtualPath, virtualPathDependencies, DateTime utcStart)
{
string embedded = _GetEmbeddedPath(virtualPath);
// not embedded? fall back
if (string.IsNullOrEmpty(embedded))
return base.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
// there is no cache dependency for embedded resources
return null;
}
public override bool FileExists(string virtualPath)
{
string embedded = _GetEmbeddedPath(virtualPath);
// You can override the embed by placing a real file at the virtual path...
return base.FileExists(virtualPath) || !string.IsNullOrEmpty(embedded);
}
public override VirtualFile GetFile(string virtualPath)
{
// You can override the embed by placing a real file at the virtual path...
if (base.FileExists(virtualPath))
return base.GetFile(virtualPath);
string embedded = _GetEmbeddedPath(virtualPath);
if (string.IsNullOrEmpty(embedded))
return null;
return new EmbeddedVirtualFile(virtualPath, GetType().Assembly
.GetManifestResourceStream(embedded));
}
private string _GetEmbeddedPath(string path)
{
if (path.StartsWith("~/"))
path = path.Substring(1);
path = path.Replace(BookingEngine.Route, "/");
//path = path.ToLowerInvariant();
path = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + path.Replace('/', '.');
// this makes sure the "virtual path" exists as an embedded resource
return GetType().Assembly.GetManifestResourceNames()
.Where(o => o == path).FirstOrDefault();
}
Nested Virtual File Class
public class EmbeddedVirtualFile : VirtualFile
{
private Stream _stream;
public EmbeddedVirtualFile(string virtualPath,
Stream stream) : base(virtualPath)
{
if (null == stream)
throw new ArgumentNullException("stream");
_stream = stream;
}
public override Stream Open()
{
return _stream;
}
}
A lot of the code we are using comes from the following links
Embedded Files - https://www.ianmariano.com/2013/06/11/embedded-razor-views-in-mvc-4/
RouteBase implementation - Multiple levels in MVC custom routing
I am trying to create own filter attribute in order to support multilinguality.
The idea is simple. URL stands for language.
*http://host.ext/en/rest_of_the_url* will open in English and
*http://host.ext/hy/rest_of_the_url* will open in Armenian.
The problem is that at run it says that MultilingualActionFilterAttribute
Here is the error text "The given filter instance must implement one or more of the following filter interfaces: IAuthorizationFilter, IActionFilter, IResultFilter, IExceptionFilter."
Here I am using it as global filter.
namespace TIKSN.STOZE.WebApp
{
public class FilterConfig
{
public static void RegisterGlobalFilters(System.Web.Mvc.GlobalFilterCollection filters)
{
filters.Add(new TIKSN.STOZE.Common.MultilingualActionFilterAttribute());
filters.Add(new System.Web.Mvc.HandleErrorAttribute());
}
}
}
Here I am defining it.
namespace TIKSN.STOZE.Common
{
public class MultilingualActionFilterAttribute : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
string language = System.Convert.ToString(filterContext.RouteData.Values["language"]);
System.Diagnostics.Debug.Print("Requested language is '{0}'", language);
language = Helper.PickUpSupportedLanguage(language);
System.Diagnostics.Debug.Print("Supported language is '{0}'", language);
if (language == string.Empty)
{
filterContext.HttpContext.Response.RedirectToRoutePermanent(new { language = Common.Properties.Settings.Default.DefaultLanguageCode });
}
language = Helper.TryToPickUpSupportedLanguage(language);
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(language);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.GetCultureInfo(language);
}
}
}
if you are using web api then the problem may happen because of implementing wrong interface, as IActionFilter is defined in both System.Web.Http.Filters and System.Web.Mvc namespaces.
The problem was that I updated to MVC 5, so I had to update web.config files too.
Look here.
This one works:
Please add your filter to the webApiconfig instead of filter config file.
From A. Murray:
https://stackoverflow.com/a/32518692/4853768
Well, I'm making my foray into this fantastic site with a question about the correct way to inject configuration settings into application components. So, the overview is : I have an application written in C# .Net 3.5. It consists of 3 assemblies - a Core, a Data and a Service. The data & service assemblies require settings retrieved from the app.config, which is done via a settings file, eg.
Code :
public static String RequestQueueConnectionString
{
get { return ConnectionSettings.Default.RequestQueueConnectionString; }
}
Config :
<applicationSettings>
<MyNamespace.Data.ConnectionSettings>
<setting name="RequestQueueConnectionString" serializeAs="String">
...
Now, the assemblies are all setup using StructureMap for IoC - which to my mind should provide the answer to what I am looking for, but I just can't quite see it!
IoC :
public static void ConfigureStructureMap(IContainer container)
{
container.Configure(x => ...
...
What I want to be able to do is to inject a configuration class already populated into the IoC container such that those settings are used for that assembly, NOT those specified in the settings file / app.config. So perhaps :
public static void ConfigureStructureMap(IContainer container, MyConfigClass config)
{
container.Configure(x => x.For<DataConfig>()
.Singleton()
.Use ???
...
I hope I have provided enough details here - forgive a newbie if I have not and please let me know what else would be helpful in answering this!
So, after a lot of searching and trial and error, I was presented with #default.kramer's link, which I duely followed! With a little bit of trial and error, again (best way in my opinion), I managed to get the solution I was looking for. Now, whilst you can follow the link (and I would highly suggest doing so), I am going to post the solution to my question as I implemented it. Hopefully this might help someone with a similar problem.
So, I now have my configuration setup class like so :
public static class DispatchConfiguration
{
public static void ConfigureStructureMap(IContainer container, IDispatchConfiguration dispatchConfig)
{
DispatchProcessBatchSize = dispatchConfig.DispatchProcessBatchSize;
ServiceIsActive = dispatchConfig.ServiceIsActive;
...
}
Now, before I was using a settings file to retrieve the configuration out of the app.config file. This was obviously good for ensuring I had flexibility in changing my config settings, but it left me with the problem of not being able to easily test those settings. Say 9/10 tests required the service to be active, but 1 test wanted to test "ServiceIsActive = false;", now I'm in trouble.
Now, however, I am able to inject the configuration from the test :
[Given(#"Config\.IsServiceActive returns false")]
public void GivenConfig_IsServiceActiveReturnsFalse()
{
var settings = new DispatchSettings
{
ServiceIsActive = false,
DispatchProcessBatchSize = 100,
UpdatedBy = "Unit Test"
};
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, settings);
}
And then in the real world I am able to get the settings from app.config :
public void Start(String[] args)
{
var dispatchConfig = this.GetDispatchConfiguration();
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, dispatchConfig);
...
}
private IDispatchConfiguration GetDispatchConfiguration()
{
var config = (DispatchSettings)ConfigurationManager.GetSection("DispatchSettings");
return config;
}
And then the actual config class looks like :
[XmlRoot(ElementName = "DispatchSettings", Namespace = "")]
public sealed class DispatchSettings : IDispatchConfiguration
{
public Int32 DispatchProcessBatchSize { get; set; }
public Boolean ServiceIsActive { get; set; }
...
}
For the sake of completeness the interface looks like so :
public interface IDispatchConfiguration
{
Int32 DispatchProcessBatchSize { get; }
Boolean ServiceIsActive { get; }
...
}
And finally, the config file looks like this :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="DispatchSettings" type="MyNamespace.XmlConfigurator, MyNamespace.Core" />
</configSections>
<DispatchSettings type="MyNamespace.DispatchSettings, MyNamespace.Core">
<ServiceIsActive>True</ServiceIsActive>
<DispatchProcessBatchSize>100</DispatchProcessBatchSize>
</DispatchSettings>
Now, anyone with a keen eye will spot "MyNamespace.XmlConfigurator". I found this on one of my Google journeys, and the code allows you to deserialize an Xml config into a class of your desire (as shown in this example). So, to ensure you have the complete code to make this technique work, below is the code for the XmlConfigurator. I cannot remember where I came across it, but a big thanks to the person who wrote it!!
public sealed class XmlConfigurator : IConfigurationSectionHandler
{
public XmlConfigurator()
{
}
public object Create(object parent, object configContext, XmlNode section)
{
XPathNavigator navigator = null;
String typeName = null;
Type sectionType = null;
XmlSerializer xs = null;
XmlNodeReader reader = null;
try
{
Object settings = null;
if (section == null)
{
return settings;
}
navigator = section.CreateNavigator();
typeName = (string)navigator.Evaluate("string(#type)");
sectionType = Type.GetType(typeName);
xs = new XmlSerializer(sectionType);
reader = new XmlNodeReader(section);
settings = xs.Deserialize(reader);
return settings;
}
finally
{
xs = null;
}
}
}
And there you have it! I hope this allows anyone with a similiar issue to resolve it and is clear enough to follow!
I want to load a dictionary at startup in my console app from my app.config.
I know that I could use an xml library or linq to XML to load it to parse and traverse it. My question is there a BUILT IN way of doing it.
Isn't there some way to add an application configuration section into the app.config and then have it loaded automagically using ConfigurationManager class in the System.Configuration namespace?
Any example? BTW, I am in NET20.
EDIT
Sorry, I should have clarified. I want to load the dictionary WITHOUT using AppSettings. I know how to do that already. Of course, the downside of using AppSettings is that I have to change my code to add new values to my dictionary. That is why I am looking for a way to do it automatically.
You will need to add an <appSettings> section to your app.config file. It will look something like:
<appSettings>
<add key="foo" value="fooValue" />
<add key="bar" value="barValue" />
<add key="baz" value="bazValue" />
</appSettings>
From within your app, you can grab these values with System.Configuration.ConfigurationManager.AppSettings, which is a NameValueCollection, which is essentially a dictionary from string to string.
string myFoo = System.Configuration.ConfigurationManager.AppSettings["foo"];
You can use the appSettings section the way you describe, but that section easily gets polluted with a variety of needs, and so I usually avoid it. You can make custom sections to deal with this.
Imagine you have a class called "PluginSpec," you can write code like this:
[ConfigurationCollection(typeof(PluginSpec), AddItemName = "Plugin",
CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class PluginCollection : ConfigurationElementCollection
{
//This collection is potentially modified at run-time, so
//this override prevents a "configuration is read only" exception.
public override bool IsReadOnly()
{
return false;
}
protected override ConfigurationElement CreateNewElement()
{
return new PluginSpec();
}
protected override object GetElementKey(ConfigurationElement element)
{
PluginSpec retVal = element as PluginSpec;
return retVal.Name;
}
public PluginSpec this[string name]
{
get { return base.BaseGet(name) as PluginSpec; }
}
public void Add(PluginSpec plugin){
this.BaseAdd(plugin);
}
}
The above code can be used from a member of another config class, like this:
[ConfigurationProperty("", IsDefaultCollection = true)]
public PluginCollection Plugins
{
get
{
PluginCollection subList = base[""] as PluginCollection;
return subList;
}
}
The above would be a member in a class that derives from ConfigurationElement or ConfigurationSection.
How can I have a view render a partial (user control) from a different folder?
With preview 3 I used to call RenderUserControl with the complete path, but whith upgrading to preview 5 this is not possible anymore.
Instead we got the RenderPartial method, but it's not offering me the functionality I'm looking for.
Just include the path to the view, with the file extension.
Razor:
#Html.Partial("~/Views/AnotherFolder/Messages.cshtml", ViewData.Model.Successes)
ASP.NET engine:
<% Html.RenderPartial("~/Views/AnotherFolder/Messages.ascx", ViewData.Model.Successes); %>
If that isn't your issue, could you please include your code that used to work with the RenderUserControl?
In my case I was using MvcMailer (https://github.com/smsohan/MvcMailer) and wanted to access a partial view from another folder, that wasn't in "Shared." The above solutions didn't work, but using a relative path did.
#Html.Partial("../MyViewFolder/Partials/_PartialView", Model.MyObject)
If you are using this other path a lot of the time you can fix this permanently without having to specify the path all of the time. By default, it is checking for partial views in the View folder and in the Shared folder. But say you want to add one.
Add a class to your Models folder:
public class NewViewEngine : RazorViewEngine {
private static readonly string[] NEW_PARTIAL_VIEW_FORMATS = new[] {
"~/Views/Foo/{0}.cshtml",
"~/Views/Shared/Bar/{0}.cshtml"
};
public NewViewEngine() {
// Keep existing locations in sync
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NEW_PARTIAL_VIEW_FORMATS).ToArray();
}
}
Then in your Global.asax.cs file, add the following line:
ViewEngines.Engines.Add(new NewViewEngine());
For readers using ASP.NET Core 2.1 or later and wanting to use Partial Tag Helper syntax, try this:
<partial name="~/Views/Folder/_PartialName.cshtml" />
The tilde (~) is optional.
The information at https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-3.1#partial-tag-helper is helpful too.
For a user control named myPartial.ascx located at Views/Account folder write like this:
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>
I've created a workaround that seems to be working pretty well. I found the need to switch to the context of a different controller for action name lookup, view lookup, etc. To implement this, I created a new extension method for HtmlHelper:
public static IDisposable ControllerContextRegion(
this HtmlHelper html,
string controllerName)
{
return new ControllerContextRegion(html.ViewContext.RouteData, controllerName);
}
ControllerContextRegion is defined as:
internal class ControllerContextRegion : IDisposable
{
private readonly RouteData routeData;
private readonly string previousControllerName;
public ControllerContextRegion(RouteData routeData, string controllerName)
{
this.routeData = routeData;
this.previousControllerName = routeData.GetRequiredString("controller");
this.SetControllerName(controllerName);
}
public void Dispose()
{
this.SetControllerName(this.previousControllerName);
}
private void SetControllerName(string controllerName)
{
this.routeData.Values["controller"] = controllerName;
}
}
The way this is used within a view is as follows:
#using (Html.ControllerContextRegion("Foo")) {
// Html.Action, Html.Partial, etc. now looks things up as though
// FooController was our controller.
}
There may be unwanted side effects for this if your code requires the controller route component to not change, but in our code so far, there doesn't seem to be any negatives to this approach.
The VirtualPathProviderViewEngine, on which the WebFormsViewEngine is based, is supposed to support the "~" and "/" characters at the front of the path so your examples above should work.
I noticed your examples use the path "~/Account/myPartial.ascx", but you mentioned that your user control is in the Views/Account folder. Have you tried
<%Html.RenderPartial("~/Views/Account/myPartial.ascx");%>
or is that just a typo in your question?
you should try this
~/Views/Shared/parts/UMFview.ascx
place the ~/Views/ before your code
Create a Custom View Engine and have a method that returns a ViewEngineResult
In this example you just overwrite the _options.ViewLocationFormats and add your folder directory
:
public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
{
var controllerName = context.GetNormalizedRouteValue(CONTROLLER_KEY);
var areaName = context.GetNormalizedRouteValue(AREA_KEY);
var checkedLocations = new List<string>();
foreach (var location in _options.ViewLocationFormats)
{
var view = string.Format(location, viewName, controllerName);
if (File.Exists(view))
{
return ViewEngineResult.Found("Default", new View(view, _ViewRendering));
}
checkedLocations.Add(view);
}
return ViewEngineResult.NotFound(viewName, checkedLocations);
}
Example: https://github.com/AspNetMonsters/pugzor
Try using RenderAction("myPartial","Account");