Map the physical file path in asp.net mvc - asp.net-mvc

I am trying to read an XSLT file from disk in my ASP.Net MVC controller. What I am doing is the following:
string filepath = HttpContext.Request.PhysicalApplicationPath;
filepath += "/Content/Xsl/pubmed.xslt";
string xsl = System.IO.File.ReadAllText(filepath);
However, half way down this thread on forums.asp.net there is the following quote
HttpContext.Current is evil and if you
use it anywhere in your mvc app you
are doing something wrong because you
do not need it.
Whilst I am not using Current, I am wondering what is the best way to determine the absolute physical path of a file in MVC? For some reason (I don't know why!) HttpContext doesn't feel right for me.
Is there a better (or recommended/best practice) way of reading files from disk in ASP.Net MVC?

string filePath = Server.MapPath(Url.Content("~/Content/Xsl/"));
I disagree with the idea that HttpContext.Current is "evil." It's not the hammer for every problem, but it is certainly better than, e.g., Session for stuff that it can do OK.

If you're using WebApi or not specifically within a controller class, you can use the following as an alternative:
HostingEnvironment.MapPath("/Content/Xsl/pubmed.xslt")

I would have the site root path injected into the controller constructor by the DI framework:
public class HomeController: Controller
{
private readonly string _siteRoot;
public HomeController(string siteRoot)
{
_siteRoot = siteRoot;
}
public ActionResult Index()
{
string filePath = Path.Combine(_siteRoot, #"Content\Xsl\pubmed.xslt");
return File(filePath, "text/xml");
}
}
As far as the site root path is concerned it can be expressed with the HostingEnvironment.ApplicationPhysicalPath static property.

Related

How grails has implemented handler mappings?

I have been trying to mock grails' convention over configuration paradigm in a spring mvc application and i am finding it difficult to automate handler mappings.
Basically i have a controller like this.
#Controller
#RequestMapping(value = {"/", "/contact"})
public class ContactController {
#Autowired
private ContactService contactService;
#RequestMapping(value = {"/","/index"})
public String listContacts(Map<String, Object> map){
map.put("contact", new Contact());
map.put("contactList", contactService.listContact());
return "contact";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addContact(#ModelAttribute("contact")Contact contact, BindingResult result){
contactService.addContact(contact);
return "redirect:/contact/index";
}
#RequestMapping(value = "/delete/{contactId}", method = RequestMethod.GET)
public String removeContact(#PathVariable("contactId")Long contactId){
contactService.removeContact(contactId);
return "redirect:/contact/index";
}
}
Now, i have tried "ControllerClassNameHandlerMapping", but it seems to have a limitation with three part url.
I basically want all the requests to be automatically mapped to the appropriate controller and actions, without using #RequestMapping
Any pointers will be great help
That won't be easy. Grails framework has written bunch of ASTTransformers where they inject annotations and other properties in to the class which is controller. Take a look at the source code for grails-plugin-controllers and you cans see the code under compiler.web and see what they are doing. Also if you take a look at ControllersGrailsPlugin you will see that its watching the directory grails-app under the parent folder. It won't be that easy to do. Hope this helps
UPDATE
Making AST transformations via ASM in your case, is probably the fastest way, maybe post-compilation step?
Hope it helps
It won't be that simple but what you want is likely to perform the following :
Write an annotation processor(scan classpath from classloader) to collect Controllers annotations and matching class
Hook into Spring mvc API to inject the Request Mapping based on your conventions.
Then you would also need to collect the method parameters to auto-inject pathVariables, etc.
If Spring MVC is not expecting to play with the RequestMapping annotation as soon as the Controller annotation is found (or defaults to something), then you're good to go (Post Bean Definition processors or similar mechanisms)...
Not that simple, but definitely possible...

ASP.NET MVC Database localization

I'm developing an multilingual enterprise web site and I would like to store the localization in database.
I have read the following article which is very good but I personally think that is a an overhead and I can achieve the same much easy:
Extending the ASP.NET 2.0 Resource-Provider Model
I have already setup some ground but I'm not sure if my approach is fine. Basically I have created a service that is registers with DI.
public interface ILocalizedStringProvider
{
string GetLocalizedString(string key);
string GetLocalizedString(string key, string deafultValue);
}
Also i have created a Html helper like this
public static MvcHtmlString LocalizedString(this HtmlHelper helper, string key, string defaultValue)
{
if (string.IsNullOrEmpty(defaultValue)) return new MvcHtmlString("");
if (string.IsNullOrEmpty(key)) return new MvcHtmlString(defaultValue);
ILocalizedStringProvider localizedStringProvider = DependencyResolver.Current.GetService<ILocalizedStringProvider>();
if (localizedStringProvider == null)
{
return MvcHtmlString.Create(defaultValue);
}
string val = localizedStringProvider.GetLocalizedString(key, defaultValue);
if (string.IsNullOrEmpty(val))
{
return MvcHtmlString.Create(defaultValue);
}
return MvcHtmlString.Create(val);
}
Then the helper is simply invoked from the view.
First I want to know if this approach is good and if is not an anti-pattern.
Second my concern is this line:
ILocalizedStringProvider localizedStringProvider = DependencyResolver.Current.GetService<ILocalizedStringProvider>();
Is it maybe better to resolve the service ILocalizedStringProvider in the controller with constructor injection and let the controller populate the ViewBag with the localization's?
Thanks!
You can use my Griffin.MvcContrib project. It contains a ready to use MS SqlServer implementation for storing localization in the database.
Introduction: http://www.codeproject.com/Articles/352583/Localization-in-ASP-NET-MVC-with-Griffin-MvcContri
Administration
There is also an adminstration area which you can use to manage the localizations:
SQL Server setup
https://github.com/jgauffin/griffin.mvccontrib/wiki/SqlServer
Source code
The project is available at github: https://github.com/jgauffin/griffin.mvccontrib
Here is a very good one :
http://west-wind.com/westwind.globalization/
It offers :
DB storage
Resx Import/Export
Strong Type Class generation
it is rapidly added to your projects via Nuget, and you have the Full Source Code.. Awesome stuff

Is it legal to extend an entity model with functionality in ASP.NET MVC

first of all here is my situation. I am programming an intranet application using ASP.NET MVC 3 with Entity Framework 4.1. My application has been developed using the "Unit of Work" and "Repository" design patterns.
How ever in my opinion it should go the way that my application has an unit of work that provides a central access to all the repositories which further provide access to the entities.
Lets say I have a entity called "ProductApprovalDocument" with the properties "id", "creationDate" and "approvalDecission" stored in the database. Now I want the user to be able to access a PDF file of the document thats shortly described by the entity. Because the files are stored in a central directory on a file server using the URL format "[fileServerDirectoryPath]/[ProductApprovalDocument.id].pdf", I do not want to save an extra property for that filepath on the database. What I would like to do, is give the entity an extra property called "filepath" that automatically constructs the path with the given information and returns it.
Now the Problem:
I use an interface called FileService to abstract file access from the rest of the application. Now in my case I would have to access the UnitOfWork object out of the entity model, to retrieve the current FileService implementetion and get the preconfigured filepath. I think that's the totaly wrong way because to me an entity model should only be used as a data container not more or less.
Now the Question:
How do I handle such a situation. I would not like to always set the filepath property through the controller because ist more or less static and therefore could be done somehow automatic by the model.
Edit (final solution):
Thanks to the answer of Andre Loker I gained another point of view to my problem.
What was the central target I wanted to reach?
I wanted the user to gain access to a file stored on a fileserver.
Do I have to provide every displayed entity with the total filepath?
No! Think about the principle of MVC! User actions get processed by the controller just in time. You don't have to provide information untill it really get's used.
So the solution is just to render all data as usual but instead of displaying a static html link to the files, you have to include an ActionLink to the Controller which calculates the filepath on the fly and automatically redirects the user to the file.
In the View do this:
#Html.ActionLink(Model.ID.ToString(), "ShowProductApprovalDocumentFile", "ProductApprovalDocument", new { ProductApprovalDocumentID = Model.ID }, null)
instead of this:
#Model.ID
And add an corresponding Action to the controller:
public ActionResult ShowProductApprovalDocumentFile(int ProductApprovalDocumentID )
{
return Redirect(_unitOfWork.FileService.GetFilePathForProductApprovalDocument(ProductApprovalDocumentID));
}
Thanks to the guys that took the time to give me an answer and special thanks to Andre who lead me to the satisfying answer! :)
If I understand the property correctly, there are several options:
1) Make the FilePath property use a service locator to find the FileService:
public string FilePath {
get {
FileService fileService = DependencyResolver.Current.GetService<FileService>();
return fileService.GetFilePathForDocument(this);
}
}
While I'm not a hugh fan of static service locators as they make testing more difficult, this could be a viable option. To make it more easily testable you can make the file service locator injectable:
private static readonly Func<FileService> defaultFileServiceLocator = ()=>DependencyResolver.Current.GetService<FileService>():
private Func<FileService> fileServiceLocator = defaultFileServiceLocator;
public Func<FileService> FileServiceLocator {
get { return fileServiceLocator; }
set { fileServiceLocator = value ?? defaultFileServiceLocator; }
}
And then use this in FilePath
public string FilePath {
get {
FileService fileService = fileServiceLocator();
return fileService.GetFilePathForDocument(this);
}
}
This way you can inject your own file service locator during testing.
2) Explicitly require the FileService when retrieving the file path. Instead of a FilePath property you'd have:
public string GetFilePath(FileService service){
service.GetFilePathForDocument(this);
}
The problem with this is of course that now the caller of GetFilePath needs to have a FileService. This isn't much of a problem for controllers, because if you use an IoC you can inject a FileService into the controller constructor. This approach is the cleaner one as it doesn't depend on service locators, but as you see it is slightly more inconvenient for the caller.
3) Inject the FileService into the document class itself.
Instead of using a file service locator you'd inject the file service itself when you construct your ProductApprovalDocument. With this approach you can use a simple FilePath property again. The main problem is that this often doesn't play too well with ORMs, as they often construct the objects using a default constructor and you'd have to somehow hook into the object construction process to inject the dependencies. Also, I'm not a big fan of injection services into domain objects.
4) You set the FilePath from outside the entity. As you said this should be done somewhat automatically as you don't want to do it manually every time. This would require some layer through which all entities need to pass which sets up the FilePath property.
5) Don't make FilePath a property of ProductApprovalDocument at all. This would be a reasonable choice, too. ProductApprovalDocument doesn't know anything about its FilePath, so why should it be a property? Its the FileService that calculates the value. You can still have a distinct view model version of ProductApprovalDocument which does have a FilePath property. You'd set the property when you create your view model:
var model = new ProductApprovalDocumentViewModel();
mapper.Map(realDocument, model); // map common properties with AutoMapper or so
model.FilePath = fileService.GetFilePathForDocument(realDocument);
However, if ProductApprovalDocument needs to do something with its FilePath (why would it?) this approach doesn't work anymore.
Personally I'd go with solution 5, 2 or 1 in that order of precedence, where applicable.
Whilst I would be hesitant to rely on being able to calculate the filepath and I would prefer to store it as part of the entity (in case it ever needs to change for some reason), in your situation if I was adamant I wanted to do it the way you've said, I think I would extend the FileService/ViewModel to have a Filepath property which was derived in the fashion you have stated.
e.g. if I wanted to create a download link I'd do this in the ViewModel
public string FilePath
{
get
{
return String.Format(#"thehardcodedbit{0}.pdf",ID);
}
}
EDIT: If you have an Entity generated by EF4.x then it will have been generated as a partial class so you could always extend it like this (I have done this sort of thing and it works okay):
Say the generated entity looks like this:
Namespace Da_Wolf.Model.Entities.File
{
public partial class UploadedFile
{....}
}
Then you could create a partial class like this:
Namespace Da_Wolf.Model.Entities.File
{
public partial class UploadedFile
{
public string FilePath
{
get
{
return String.Format(#"thehardcodedbit{0}.pdf",ID);
}
}
}
}
Now you have the property you desire available everywhere without adding anything to the ViewModels.

Using IoC in extension methods

I am working on an ASP MVC 3 app and I'm writing a custom html helper. It's nothing special or hugely complex, but it will need an instance of an interface from structure map. I know I can simply call into structuremaps' object factory from inside the method, but since the rest of the app uses IoC rather than service location I'd like to keep it that way.
Is there a way to inject interfaces into extension methods from inside and asp net mvc app?
UPDATE
An example of what I'm doing might help:
public static class ShowUrl
{
public static string ForShow(this UrlHelper url, int showId)
{
var service = ObjectFactory.GetInstance<IPerformanceService>();
var showName = service.GetPerformanceTitle(showId);
return url.Action(MVC.Performance.Details(showId, showName.ToFriendlyUrl()));
}
}
Which is used like this:
<a href='<%= Url.ForShow(1)%>'>
Essentially I am trying to build a URL with a slug from an entity id. Maybe I'm just going about this in a really daft way.
I would not recommend doing this. Extension methods are generally best used for simple, well-known operations directly on a type. If your extension method is dependent on having an instance of another type, it is likely that it shouldn't be an extension method to begin with.
Consider making an actual service class that performs this functionality, and injecting it where it's needed. If you really need this in an extension method, consider wrapping the functionality your extension method requires in another static class/method, and avoid using any kind of injection or location.
Sharing some code might shed more light on your specific situation.
There is no way to inject dependencies into an extension method.
For ASP.NET MVC helpers, you are going to have to do some sort of service location - whether you bury that with some sort of abstraction is up to you.
You should NOT be calling structuremap directly in your extension method. Also, you should create a testable version that takes an IPerformanceService argument like below:
public static class ShowUrl
{
public static string ForShow(this UrlHelper url, int showId)
{
//Use the MVC DependencyResolver NOT Structuremap directly (DependencyResolver is using structuremap)
return url.ForShow(showId, DependencyResolver.Current.GetService<IPerformanceService>())
}
//For Unit Testing
public static string ForShow(this UrlHelper url, int showId, IPerformanceService performanceService)
{
var showName = performanceService.GetPerformanceTitle(showId);
return url.Action(MVC.Performance.Details(showId, showName.ToFriendlyUrl()));
}
}
Now you can pass in a concrete implementation of IPerformanceService in your unit test method.
Assert.Equal("TheUrl", url.ForShow(8, new PerformanceService());
More info on mocking UrlHelper: ASP.NET MVC: Unit testing controllers that use UrlHelper

ASP.Net MVC 3 Display-templates and Editor-Template Custom Location, how to?

i am going Nuts,
i am using MVCContrib, to create pluggable site using Portable Areas, and everything is working well so far, except that when i started using MVC Templates, what is happening is if i put the The templates in the respective folder of the View it works, examples
HostApplication/Views/Home/DisplayTemplates/FirstName.cshtml
HostApplication/Areas/PortableArea_Blog/Views/Home/DisplayTemplates/Auther.cshtml
but what i want really is the ability to create common templates Set and utilize it from either Host Application or Portable Area, so to do that i created a new Portable Area Called DisplayTemplates(to utilize MVCContrib Ability to compile Views), here is the portable Area structure
DisplayTemplates
|-Views
|-CommentTemplate.cshtml
now in my host Application i have created a Test Model and added UIHint Attribute
public class HostModel
{
[UIHint("~/Areas/DisplayTemplates/Comment.cshtml")]
public string Name { get; set; }
}
but it is not working, so i thought it has something to do with Partial Views Location so i created a CustomView Engine to find Partial Views in that Location and registerd it in Global.asax, here is a short idea about so i wont bore you with full code
public class AreaViewEngine : RazorViewEngine
{
public AreaViewEngine()
{
// {0} = View name
// {1} = Controller name
// View locations
ViewLocationFormats = new[]
{
"~/Areas/DisplayTemplates/{0}.cshtml"
};
PartialViewLocationFormats = ViewLocationFormats;
AreaPartialViewLocationFormats = ViewLocationFormats;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new RazorView(controllerContext, partialPath, null, true, new[] { "cshtml" });
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new RazorView(controllerContext, viewPath, masterPath, true, new[] { "cshtml" });
}
}
what is even more weird, is that it seems that that UIHint with Explicit location to Display Template, does not work, here is an example
public class HostModel
{
//this works
[UIHint("FirstName")]
//this does not work
[UIHint("~/Views/Home/DisplayTemplates/FirstName.cshtml")]
public string Name { get; set; }
}
and yes
FirstName.cshtml is in HostApplication/Views/Home/DisplayTemplates/FirstName.cshtml
again sorry for the long post, but i gave up on finding a solution, so any help would be totally appreciated.
Danny is correct. The Templates are found the same way that Partial Views are found.
By default the WebFormViewEngine and RazorViewEngine are going to search the following locations for a template.
For display templates:
~/Views/{controller}/DisplayTemplates
~/Views/Shared/DisplayTemplates
For editor templates:
~/Views/{controller}/EditorTemplates
~/Views/Shared/EditorTemplates
I think the name of the sub-directories (i.e., "DisplayTemplates" and "EditorTemplates") are hard-coded into MVC somewhere (I know it's open source and I could find it, but I'm not going to).
I think the easiest way to change the location somewhat is to override the ViewEngine. My custom ViewEngine is pretty complicated at this point, but I suspect you could get away with the following.
Let's say you want your templates to be in ~/Views/Templates.
Create a class that inherits from the view engine you're using now (probably WebFormViewEngine or RazorViewEngine). Add an empty constructor. It should looks like this:
namespace MySite
{
public class MySiteViewEngine : RazorViewEngine // <<-- or WebFormViewEngine
{
public MySiteViewEngine()
{
// We'll put some code here in a later step
}
}
}
Now, add the following lines to the Application_Start method of Global.asax.cs:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MySiteViewEngine());
At this point, compile and run your application. Everything should be running exactly like it is running now. You're basically using the same view engine you were using before.
But now, we want to add a location to search when looking for PartialViews. This is simply done by adding to the PartialViewLocationFormats. So, now in the constructor of your custom view engine, you want to add to the base class' array like so:
base.PartialViewLocationFormats = new string[] {
"~/Views/Templates/{0}.cshtml"
}.Union(base.PartialViewLocationFormats).ToArray<string>();
A couple of notes about the above:
The entry above will make it so that your view engine looks for the String display template at ~/Views/Templates/DisplayTemplates/String.cshtml.
The location format in these view engines includes the file extension, so if you're using Razor/C# use "cshtml", Razor/VB use "vbhtml", WebForms add "aspx" and "ascx".
The way I'm doing it above, I'm adding my location format to the top of the list but keeping all the default locations. You might consider removing those.
Watch the current formats and you'll see that you will also get a controller in the {1} position in the format, so if you wanted to have a Templates directory underneath every controller you could.
Careful, once you get started moving things around with a view engine, it gets addictive. You might find yourself moving everything around.
Good luck.
Instead of creating a new ViewEngine you can easily modify the existing ones at runtime:
private void FixMvcTemplateAreaBug(string areaName)
{
foreach (BuildManagerViewEngine viewEngine in ViewEngines.Engines)
{
List<string> viewLocations =
new List<string>(viewEngine.PartialViewLocationFormats);
foreach (var extension in viewEngine.FileExtensions)
viewLocations.Add("~/Areas/" + areaName +
"/Views/Shared/{0}." + extension);
viewEngine.PartialViewLocationFormats = viewLocations.ToArray();
}
}
Place the above in an appropriate location (like area registration) and you'll be fine.
I think you've had no answers, because there isn't one :-(
I've searched high and low the last few days trying to find a solution to this (since as you mentioned, it's had a lot of views). Unfortunately I can't find any way to override this.
I think you're stuck :(
Edit: Came across this post for reading views from a database instead of the disk:
ASP.NET MVC load Razor view from database
I wonder whether the DisplayTemplates are read this way. If so, you could try hijacking them and reading them from another location (instead of DB)?
Pardon if I missed something above, but is there a reason that the override of Html.DisplayFor that allows the template to be specified won't work here? For example:
#Html.DisplayFor(m => m.MyItem, "~/Views/Controller/DisplayTemplates/MyItem.cshtml")
Cheers,
It might be a little late :) but this is now possible in MVC 4. The generic templates has to be placed in ~/Views/Shared/EditorTemplates
Source

Resources