S#arp Architecture 1.9 + Multi Tenant Custom View Engine - s#arp-architecture

What is the easiest way to create a Custom AreaViewEngine that S#arp uses?
I want to add another directory to search for view for the current tenant, the tenant directory would be part of the domain name (e.g. hm.com), so i would like my view engine to check:
/Tenants/hm.com/Views/Home/Index.aspx
I tried to create a new TenantViewEngine.cs inherited from the AreaViewEngine to add my new paths but I can't get access to the HttpContext to get the host name.
Any ideas?
Thanks.
Paul

HttpContext.Current should be available. Also, ControllerContext passed as a parameter to Find* and Create* methods has a HttpContext property.
You might also want to look at the VirtualPathProviderViewEngine class.

Related

Check if curent user has access to specified MVC path

I need to extend asp:Menu to support linking to MVC routes (my project has a mix of MVC and non-MVC pages). My menu is generated using a custom class which determines if a user should be shown a node based on their priveleges to the file it referes to.
MVC pages are restricted using the AuthorizeAttribute. Avoiding mocking (if possible) I want to
Determine if the path refers to an MVC page or a standard page
If MVC, determine if the user has the rights to access it
Here's my method signature inside the menu generation class:
Private Function CanAccessPage(path As String) As Boolean
Here's the algorithm I used for this,
Based on #SLaks answer here, I was able to determine if the path referred to an MVC route.
If it was MVC, I grabbed the controller type (this required knowing what namespace my controller's were in)
Got the action method by controllerType.GetMethods(actionMethodName) (if you have multiple methods with the same name, you must pick the one your link refers to. Probably the one with an HttpGet attribute).
Used actionMethodInfo.GetCustomAttributes(GetType(AuthorizationAttribute), False) to get a collection of all authorization filters for the specified action
Called OnAuthorization with the fake context info I build in step 1 for each attribute.
Check if TypeOf filterContext.Result Is HttpUnauthorizedResult and return accordingly

Asp.net MVC RouteBase and IoC

I am creating a custom route by subclassing RouteBase. I have a dependency in there that I'd like to wire up with IoC. The method GetRouteData just takes HttpContext, but I want to add in my unit of work as well....somehow.
I am using StructureMap, but info on how you would do this with any IoC framework would be helpful.
Well, here is our solution. Many little details may be omitted but overall idea is here. This answer may be a kind of offtop to original question but it describes the general solution to the problem.
I'll try to explain the part that is responsible for plain custom HTML-pages that are created by users at runtime and therefore can't have their own Controller/Action. So the routes should be either somehow built at runtime or be "catch-all" with custom IRouteConstraint.
First of all, lets state some facts and requirements.
We have some data and some metadata about our pages stored in DB;
We don't want to generate a (hypothetically) whole million of routes for all of existing pages beforehand (i.e. on Application startup) because something can change during application and we don't want to tackle with pushing the changes to global RouteCollection;
So we do it this way:
1. PageController
Yes, special controller that is responsible for all our content pages. And there is the only action that is Display(int id) (actually we have a special ViewModel as param but I used an int id for simplicity.
The page with all its data is resolved by ID inside that Display() method. The method itself returns either ViewResult (strongly typed after PageViewModel) or NotFoundResult in case when page is not found.
2. Custom IRouteConstraint
We have to somewhere define if the URL user actually requested refers to one of our custom pages. For this we have a special IsPageConstraint that implements IRouteConstraint interface. In the Match() method of our constraint we just call our PageRepository to check whether there is a page that match our requested URL. We have our PageRepository injected by StructureMap. If we find the page then we add that "id" parameter (with the value) to the RouteData dictionary and it is automatically bound to PageController.Display(int id) by DefaultModelBinder.
But we need a RouteData parameter to check. Where we get that? Here comes...
3. Route mapping with "catch-all" parameter
Important note: this route is defined in the very end of route mappings list because it is very general, not specific. We check all our explicitly defined routes first and then check for a Page (that is easily changeable if needed).
We simply map our route like this:
routes.MapRoute("ContentPages",
"{*pagePath}",
new { controller = "Page", action = "Display" }
new { pagePath = new DependencyRouteConstraint<IsPageConstraint>() });
Stop! What is that DependencyRouteConstraint thing appeared in mapping? Well, thats what does the trick.
4. DependencyRouteConstraint<TConstraint> class
This is just another generic implementation of IRouteConstraint which takes the "real" IRouteConstraint (IsPageConstraint) and resolves it (the given TConstraint) only when Match() method called. It uses dependency injection so our IsPageConstraint instance has all actual dependencies injected!
Our DependencyRouteConstraint then just calls the dependentConstraint.Match() providing all the parameters thus just delegating actual "matching" to the "real" IRouteConstraint.
Note: this class actually has the dependency on ServiceLocator.
Summary
That way we have:
Our Route clear and clean;
The only class that has a dependency on Service Locator is DependencyRouteConstraint;
Any custom IRouteConstraint uses dependency injection whenever needed;
???
PROFIT!
Hope this helps.
So, the problem is:
Route must be defined beforehand, during Application startup
Route's responsibility is to map the incoming URL pattern to the right Controller/Action to perform some task on request. And visa versa - to generate links using that mapping data. Period. Everything else is "Single Responsibility Principle" violation which actually led to your problem.
But UoW dependencies (like NHibernate ISession, or EF ObjectContext) must be resolved at runtime.
And that is why I don't see the children of RouteBase class as a good place for some DB work dependency. It makes everything closely coupled and non-scalable. It is actually impossible to perform Dependency Injection.
From now (I guess there is some kind of already working system) you actually have just one more or less viable option that is:
To use Service Locator pattern: resolve your UoW instance right inside the GetRouteData method (use CommonServiceLocator backed by StructureMap IContainer). That is simple but not really nice thing because this way you get the dependency on static Service Locator itself in your Route.
With CSL you have to just call inside GetRouteData:
var uow = ServiceLocator.Current.GetService<IUnitOfWork>();
or with just StructureMap (without CSL facade):
var uow = ObjectFactory.GetInstance<IUnitOfWork>();
and you're done. Quick and dirty. And the keyword is "dirty" actually :)
Sure, there is much more flexible solution but it needs a few architectural changes. If you provide more details on exactly what data you get in your routes I can try to explain how we solved our Pages routing problem (using DI and custom IRouteConstraint).

how can i use structure map asp.net 3.5

I am new to the structure map but i want to use it in my asp.net site for dependency injection
can any one suggest me simple example to use structure map for the dependency injection
you will need to do something like this:-
StructureMapConfiguration
.ForRequestedType<IResourceA>()
.TheDefaultIsConcreteType<ResourceB>()
.CacheBy(InstanceScope.Singleton);
This tells StructureMap to inject ResourceB when there is a request for ResourceA.
Structure Map
You can configure programatically or via configuration file.
Programatical example (there are other ways):
StructureMap.StructureMapConfiguration.ForRequestedType<ISomething>().TheDefaultIsConcreteType<ConcreteSomething>();
then you can get an instance of the configured type using this sort of code:
//The concrete type will be ConcreteSomething
ISomething instance = ObjectFactory.GetInstance<ISomething>();
You can do it in a config file:
<StructureMap MementoStyle="Attribute">
<DefaultInstance PluginType="Blah.ISomething, Blah.SomethingDLL" PluggedType="Blah.Concrete.ConcreteSomething,Blah.ConcreteDLL"/>
</StructureMap>
and in the main method or Global.asax you can set this config by saying:
StructureMap.ObjectFactory.Initialize(x => { x.PullConfigurationFromAppConfig = true; });
and use it the same way as above:
ISomething instance = ObjectFactory.GetInstance<ISomething>();
If the concrete class has a constructor that needs instances injected in it, and you have those configured, the concrete types will get injected by the framework.
There are ways of passing parameters to constructors, dealing with Gereric types, creating named instances that are configured with specific constructor/property values. I use this framework and like it very much.

How do I access GetGlobalResourceObject function from a class that is not a page?

I have a class in my asp.net proj, I would like to get access GetGlobalResourceObject (that page exposes), from anywhere in the site, possible?
In other words I wanna access the global resources from a class that is not a page I don't care how.
Answer:
Yes, as following pseudo:
Resources.<The name of the resources file name>.<your resource key>;
Example:
lblTitle.Text = Resources.MySettings.WebsiteTitle;
Resources is an Visual-Studio auto generated namespace that exposes all the global resource classes and props in the project.
You should use
HttpContext.GetGlobalResourceObject("myResourceKey")
...because that way it will still work when using a custom ResourceProvider. The default type-generator for Resource files explicitely uses the Resx provider and won't work if you implement something like a database provider.
On some farms you'll need to wrap the call to
HttpContext.GetGlobalResourceObject("myResourceKey")
inside a try/catch block to get it over the "Could not find any resources appropriate for the specified culture or the neutral culture" error.
If you are in the site you have access to HttpContext and can use:
HttpContext.GetGlobalResourceObject("myResourceKey")
I kinda took this from the resource designer,
ResourceManager temp =
new ResourceManager("Resources.<<resource name>>",
System.Reflection.Assembly.Load("App_GlobalResources"));

Is there any way to use XmlSiteMapProvider within WinForm/Console/VSTest application?

I wonder whether there is a workaround for using the standard XmlSiteMapProvider within a non asp.net application, like WinForm/Console or, in my case, VS Unit Test one.
The following code fails, because it cannot create a path to the .sitemap file inside a private GetConfigDocument method.
XmlSiteMapProvider provider = new XmlSiteMapProvider();
NameValueCollection providerAttributes = new NameValueCollection();
providerAttributes.Add("siteMapFile", "Web.sitemap");
provider.Initialize("XmlSiteMapReader", providerAttributes);
provider.BuildSiteMap();
I feel the right solution is to write another provider.
I do not see why not. It is just a provider that implements an interface. You may not need many of the features, but you can access the API for what it provides you. Your WinForms screens can simply use the Urls for identification so that you can determine your place in the hierarchy.
What you may have to do is create a custom implementation of the provider because it will use the HttpContext to get the Url of the current web request to identify current placement while you will need to get that value differently. That is what could be tricky because your WinForm application could be displaying multiple windows at time. If you know there is only one window showing at a time you could use a static value which is set prior to accessing the SiteMap API.
Now you have to question the value of using an API if you have to do all of the work. There may not be enough benefit to make it worthwhile.

Resources