Archive for the ‘.NET’ Category
Developing with Multiple Versions of Visual Studio
One of the SharePoint projects I am working on involves a Visual Studio 2008 solution that is used across several teams of developers. I want to use Visual Studio 2010, but converting the projects would cause problems for anyone still using VS2008, and if everyone has to upgrade to VS2010 at the same time it will never happen.
Visual Studio does not support opening projects in multiple versions, and a search only came up with ways to maintain seperate projects, either manually or with some sort of sync process, which seems far too complicated.
Fortunately, it turns out that if the projects are simple enough, you can use them in both VS2008 and VS2010. It may not work for web apps or test projects, but for class libraries (or WSPBuilder solution projects) you just need to make a couple of manual edits to each project file.
- Remove the web app guid {349c5851-65df-11da-9384-00065b846f21} from ProjectTypeIds – this can be useful for adding WebForms designer support to WSPBuilder projects, but isn’t really necessary.
- Change the ToolsVersion attribute on project element to 4.0 – This tells VS2010 that it is the current version and does not require upgrade, while VS2008 will see the invalid version number and fall back to 3.5 with only a warning.
Once the project files are updated, simply create a new solution file for use in VS2010 and add the existing project files, which should now load without triggering the conversion wizard.
Precompiled Razor for Shared Views and Mono
SharePoint isn’t the only platform I work with where I can’t quite use ASP.NET MVC 3 out of the box. I also have quite a bit of code on Mono. Mono will probably support MVC 3 at some point, but right now it only almost works – there is only one line of code that doesn’t work, but that line gets called at the start of every request.
Since the problem is in finding and parsing the cshtml files, it should be possible to get around the issue with precompiled views and a custom VirtualPathProvider or ViewEngine. As well as getting around issues with Mono, having views precompiled makes it a lot easier to include views in a shared library. I have shared aspx views working with embedded resources and a custom VirtualPathProvider, but there usually end up being some differences between the handling of embedded and regular views.
As a starting point I used Chris van de Steegs code (Compile your asp.net mvc Razor views into a seperate dll), though what I ended up with was quite different. The current implementation of BuildManager is one of the places where Mono and .NET are not the same (http://go-mono.com/forums/#nabble-td3219000), and there’s a lot of stuff in that path that is more complex than is necessary in this case.
My solution uses a custom ViewEngine, View and ViewPage base class. My first attempt used a virtual path provider to find compiled views using the standard WebViewPage base class, but even if you have the ViewEngine find the compiled view, WebViewPage calls BuildManager anyway to find layouts and check various things that don’t need checking when the view is precompiled.
The ViewEngine is fairly simple – a dictionary maps virtual paths to compiled types and CreateView creates an instance of the appropriate type. The dictionary is populated by finding all types in an assembly that implement ICompiledViewPage and are in a Views namespace.
The IView implementation also doesn’t do much – RenderView populates some context objects and calls ExecutePage.
The custom view base class, CompiledViewPage does the actual page rendering. It is a subclass of WebViewPage, but I ended up not being able to use that much from the base class due to some inconvenient internal/private fields. It is set up to work as a base class for ViewStart pages, layouts, and regular views. The basic flow for rendering a page is:
- If there is a ViewStart page, call its execute method and copy its context settings
- Call execute on the main page. This will produce a string for the page body and a dictionary mapping section names to actions that will write the content of the section.
- Call execute on the layout, which will include calls to RenderSection that run the actions set up by the main page execute method.
There are a few things in the code that can be cleaned up, particularly around the handling of writers and helpers. So far it’s working rather than really good code, but it works for everything I’ve tried it on (both .NET and Mono) and I’ll probably improve on it as it gets more real world use.
The other part of the system is generating the code files that will be compiled. I do this with a console app, GenerateViewsWithMvc, that is set up as an external tool in Visual Studio to generate code for all cshtml files in a project. It’s very similar to the one I use for Razor views in SharePoint – the main difference is that it uses WebPageRazorHost rather than RazorEngineHost so that it can use sections. It also uses a dynamic type parameter rather than an untyped base class for untyped views because I can’t have the base type extend both the typed and untyped versions of WebPageView without duplicating all the code.
The external tool settings are:
- Command: GenerateViewCodeWithMvc.exe
- Arguments (base class for views): CompiledViews.CompiledViewPage
- Initial Directory: $(ProjectDir)
Using the compiled views in a web app requires a few lines in Application_Start:
ViewEngines.Engines.Clear();
var engine = new CompiledRazorViewEngine();
CompiledRazorViewEngine.RegisterViewAssembly(typeof(CompiledRazorViewEngine).Assembly);
CompiledRazorViewEngine.RegisterViewAssembly(typeof(CompiledViews.Mvc3Test.Views._ViewStart).Assembly);
ViewEngines.Engines.Add(engine);
The first RegisterViewAssembly call adds views from a shared library and the second adds the views in the current assembly. If the same view exists in both assemblies, the last one added will be used. This code removes all other view engines – it should be possible to use it with other view engines, though there will obviously be some conflicts if the other view engine also looks for cshtml files in the Views folder.
ASP.NET MVC Controller Actions in SharePoint
Having got Razor views working in SharePoint, I needed a better way to use them – displaying the view is three lines of fairly clean code, but it’s surrounded by the all the event handler methods you need in a web forms page or control.
It turns out that with the right code in the base class, a web part can look a lot like an ASP.NET MVC controller.
public class ExampleWebPart : MvcWebPart
{
protected override ActionResult Get()
{
var m = new DocSection()
{
Title = "Test Model"
};
return View(new DocView(), m);
}
}
The ActionResult/ViewResult classes have a similar interface to the ones in ASP.NET MVC, though they are quite simple so far. The key functionality is in the Execute() method, which actually renders the view. The standard ASP.NET ViewResult writes to the Response, but since this is happening inside a SharePoint page we need to add the content as a control on the page instead.
public override void Execute()
{
View.Model = Model;
View.Execute();
ParentControl.Controls.Add(new LiteralControl(View.Result));
}
Getting an ActionResult From the subclass and executing it happens in CreateChildControls – I’m not totally sure that is the best place for it, but it seems to be working well so far.
protected override void CreateChildControls()
{
if (!_error)
{
try
{
base.CreateChildControls();
var r = Get();
r.Execute();
}
catch (Exception ex)
{
HandleException(ex);
}
}
}
The full code can be found on github.
ASP.NET MVC 3 Razor Views in SharePoint
ASP.NET MVC 3 has just been released, and I really like the new Razor syntax for views. However, I work with SharePoint, which is solidly based on WebForms and .NET 3.5. Razor requires .NET 4, and while you can host WebForms MVC pages in _layouts, it’s an ugly hack that gives you the worst features of both systems.
Razor is internally quite different from WebForms, and the core functionality doesn’t actually require MVC, so it should be possible to use it in a system that can’t use MVC.
My first attempt to make Razor work under SharePoint was to see if it would compile without .NET 4. That didn’t work, but I did notice that most of the dependencies on .NET 4 are in parsing the templates – you can’t build cshtml files without .NET 4, but the generated code doesn’t require anything special.
Starting with the code from http://razorengine.codeplex.com/ I was able to convert a cshtml file into a cs file that can be compiled in a .NET 3.5 project. My version of the code is at https://github.com/tqc/RazorEngine.
RazorEngine came pretty close to what I needed, only requiring fairly minor changes:
- Add methods to return the generated source code rather than compiling and running it
- Copy the template base classes into a .NET 3.5 library (RazorEngine.Run). This meant removing support for dynamic objects, but apart from that the code doesn’t require the latest version.
I set up a console app (GenerateViewCode) that will generate code files for all cshtml files in a project. I considered using a single file generator, but a console app is easier to debug and more likely to work properly on a build server. It is intended to be set up as an external tool in Visual Studio:
- Command: GenerateViewCode.exe
- Arguments (base class for views): RazorEngine.Templating.TemplateBase
- Initial Directory: $(ProjectDir)
After you run the tool on a project containing cshtml files and include the generated code in the project, you will be able to render the view from your code:
var view = new TestViewLibrary.Views.Shared.DocView() view.Model = newTestViewLibrary.Models.DocSection(); view.Execute(); ParentControl.Controls.Add(new LiteralControl(View.Result));
This replaces either trying to build html in code (with code that looks a lot like the generated output from Razor) or finding and loading a usercontrol in _layouts.
MVC isn’t just about the view though – more on that shortly.
Unit Testing ASP.NET MVC Auth Attribute with MVCFakes
In setting up a complete set of tests for an ASP.NET MVC application, I have been using MVCFakes to test actions that need to access context objects. This works quite nicely when all the code is inside the action method – just set the context and call the method. It won’t however test anything set up using attributes.
I found posts from Paul Brown and Josh Reed Schramm that come close to what I need, but I want to test that an unauthorized user can’t run the code rather than checking for presence of the attribute directly.
The below method will stop execution if the request is unauthorized and return the result of the action method otherwise. I also made versions for actions with parameters, but haven’t included that code as it isn’t much more than changing the type parameters.
public static ActionResult Invoke(this Controller c, Func f)
{
var methodInfo = f.Method;
var attributes = methodInfo.GetCustomAttributes(
typeof(AuthorizeAttribute), true);
if (attributes.Length == 1)
{
var aa = attributes[0] as AuthorizeAttribute;
var user = c.User;
if (user == null
|| user.Identity == null
|| !user.Identity.IsAuthenticated
|| (!string.IsNullOrEmpty(aa.Roles)
&& !aa.Roles.Split(',')
.Where(r => user.IsInRole(r.Trim()))
.Any()))
{
return new HttpUnauthorizedResult();
}
}
return f.Invoke();
}
The method can be called in tests as follows:
var controller=new HomeController();
controller.ControllerContext = new FakeControllerContext(
controller,
userid == null ? null : userid.ToString(),
roles);
var result1 = controller.Invoke(controller.Index);
Assert.That(result1 is HttpUnauthorizedResult);