How can I debug projects that extend Umbraco 7? - visual-studio

I'm writing some code that integrates with Umbraco v7, at the moment specifically with the Umbraco Forms extension but in the near future with the CMS itself. I have nuget'd the Umbraco assemblies into my VS project and have a working instance with all the relevant packages and customisations in place by following the steps described in this article.
I have a second project, let's call it Project2 in the solution which will generate the DLL I'll be dropping into Umbraco's bin folder in production to allow the CMS to auto-hook-up to the additional functionality.
So far all well and good, but I have hit a problem - debugging. If I drop the Project2 dll into Umbraco I can use the Umbraco logger to generate output messages but this is woefully inadequate for debugging more complex code, not to say frustratingly time-consuming. But I can't see how I can connect the Visual Studio debugger to Project2 at runtime. Can anyone suggest a technique I could utilise?

I've sort of figured it out with the aid of this post which at least allows me to hook into things like events like Published with my own code which can be debugged in VS.
The technique is this:
Edit the Global.ascx file in the main Umbraco project so that it uses a custom Global.cs file in Project2. Of course you have to add a reference to Project2 in the main Umbraco project in order to do this. This is your hook.
<%# Application Inherits="Project2.Global" Language="C#" %>
And in this file (which is in Project2) you attach custom handlers for the various Umbraco events. And from this point in all the hooks are in place to allow you to debug your custom code. Slap some breakpoints in there and kiss the LogHelper goodbye ;-)
public class Global : UmbracoApplication
{
public override void Init()
{
var application = this as HttpApplication;
//Hook up your event handlers as required.
application.PreRequestHandlerExecute += PreRequestHandlerExecute;
ContentService.Published += ContentService_Published;
base.Init();
}
//Custom logic which you can step-through in VS.
private void ContentService_Published(IPublishingStrategy sender,
PublishEventArgs<Umbraco.Core.Models.IContent> e)
{
LogHelper.Info(this.GetType(), "Caught Publish event " + e.PublishedEntities.FirstOrDefault().Name); //very droll.
}
...
}
Phew! Hope this helps someone.

Related

Blazor components - namespace move

I am experiencing problem in VS 2022 in Blazor complex project. After moving Blazor components to different namespace/folder the components are not functional any more. My component does not 'recognize' other Blazor components anymore. For example custom component MudTable from MudBlazor namespace:
I would like to be able move Blazor components easily as simple classes.
Sometimes it helps to specify the same namespace in razor file to match namespace in partial code behind class, but sometimes I end up creating component (razor and cs files) with different name (copying the same code). This is very time consuming. I am not able to reproduce the described bug in Simple Projects. I have tried to reproduce the issue but it works fine (with minor VS bug) as described below. I will keep trying to reproduce the issue on Sample project.
Please find below scenarios working fine:
Simple scenario working fine
Steps to reproduce the issue:
Create Blazor Server App
Create folders
Components
Components\Sub1
Components\Sub2
Add new Razor component e.g. MyComponent1.razor to folder Components\Sub1
Verify that <MyComponent1\> works on some page. Eg. add lines to Index.razor:
#using BlazorApp1.Components.Sub1
<MyComponent1 />
Move MyComponent1.razor to folder Components\Sub2
Update Index.razor:
#using BlazorApp1.Components.Sub2
<MyComponent1 />
It works fine
More complex scenario with minor bug
Create Blazor Server App
Create folders
Components
Components\Sub1
Components\Sub2
Add new Razor component e.g. MyComponent1.razor to folder Components\Sub1
Add behind the class to 'MyComponent1.razor.cs' to folder Components\Sub1:
using Microsoft.AspNetCore.Components;
namespace BlazorApp1.Components.Sub1
{
public partial class MyComponent1:ComponentBase
{
}
}
Verify that <MyComponent1\> works on some page. Eg. add lines to Index.razor:
#using BlazorApp1.Components.Sub1
<MyComponent1 />
Rename folder Components\Sub1 to Components\Sub1Renamed.
Visual studio does not display any error and project can be compiled/started. Code behind is even displayed 'linked' to razor file:
This seems as a VS bug to me.
When project is started Component1 is invisible on Index page, because component 'BlazorApp1.Components.Sub1.Component1has no visual code defined, because it has only non-visual definition 'behind the code'. The file 'Component1.razor' with visual is not used at all because it is in unused namespaceBlazorApp1.Components.Sub1Renamed`
Update Index.razor:
#using BlazorApp1.Components.Sub1Renamed
<MyComponent1 />
When project is started, Component1 is visible on Index page (but I assume that no code behind is processed)
Do you have any hint, how to make it work for blazor components ?

How to change default view creation logic in visual studio?

I've looked here and here to try to answer this question as well as a billion Google searches, but have yet to come up with a solution.
My directory structure is a multi-tenant MVC site with an overridden view engine that uses Areas to serve content for different hosts that share a lot of common logic served globally from the base controllers and views folders. We have taken this a step further and broken out different global site sections into a new folder called SiteSections. Inside of this folder we have more Areas.
The issue I am having, is whenever I try to use the visual studio context menu from inside a controller that is inside the SiteSections folder, it always adds it to the global Views folder.
What I am assuming is happening is that since these are Areas held within a different directory, Visual Studio is searching the Areas folder for an Area with the name of which I am working in. Since this is kept in a different directory, it is just defaulting to the global one. I've looked into all the different T4 templates and do not see anything specifying the directory where the view will be created.
I have just one question, that I'm hoping is possible.
How can I override Visual Studio to look in a second directory for the Area in question?
Thanks in advance!
I experienced something like that, not with Areas, but with Folders.
Have you tried to add custom view engine on ViewEngines?
The steps that i followed:
1 - I put this line at Global.asax.cs on method Application_Start:
ViewsEngines.Engines.Add(new MyCustomViewEngine());
2 - I created the file named as MyCustomViewEngine inheriting from RazorViewEngine, for example:
public class MyCustomViewEngine : RazorViewEngine
{
public MyCustomViewEngine()
{
base.ViewLocationFormats = MyViewLocationFormats;
}
private static string[] MyViewLocationFormats = new[]
{
"~/Views/Folder/{0}.cshtml",
"~/Views/Folder_1/Folder_2/{0}.cshtml"
}
}
I think that you can use in this way: "~/Areas/Views/Folder/{0}.cshtml",
Hope this helps!

Visual Studio web deploy - simple way to include build info?

I have an ASP.NET site that I am deploying to Azure Websites. I have a production and staging environment there and it is easy to get lost which is which. During the web deploy ("Publish") from Visual Studio, is there some simple way to deploy some kind of build info that I could display either via the site itself or through the Azure Portal?
You can add a version number to your assembly by adding this line to AssemblyInfo.cs file
[assembly: AssemblyVersion("1.0.*")]
more details here and here
you can then expose it in your site however you want, maybe a hidden tag, some sort of debug info page, or write it to a file on Application_Start that you can look at later
for example in global.asax you can have
protected void Application_Start()
{
using (var writer = new StreamWriter("version.txt")
{
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
writer.WriteLine(version);
}
}
Generating a text file with current date as a post-build step was the easiest solution I found.

Global.asax.cs is not visible at the server

Running under Server08 | IIS7. I have a website project and am in the habit of hand editing the Global.asax.cs at the deployed site many times in the past. Recently I've found that only the Global.asax is present and has only:
<%# Application Codebehind="Global.asax.cs" Inherits="myDomain.MvcApplication" Language="C#" %>
There are simply no Global.asax.cs files present/visible for any of my active, functioning websites anymore.
I've checked here Global.asax can't find code-behind class and here where is the codes in Global.asax in mvc3? without getting anywhere. The second link contains the comment: 'That's because it's a compiled web application. You'll have to view it in Visual Studio as a project.'
But since my project has always been a website - and I've not converted it (intentionally) I'm puzzled by the changed behavior.
But when i step into VS12 and look at the context menu for the solution's WebProject it presents 'Convert to Web Application'. The sites' folder structures do not contain App_Data or App_Start. To my mind, these 2 fact establish at VS is treating the project as a website, why then, is the Global.cs compiled down to the /bin?
I'll close by repeating - I've hand edited these things in the past - I'm not positive but it's probably accurate to say that this is the first time I've tried to do so after installing VS12. And, coming to think on it harder - it's only recently that I've implemented the 'One Click Publishing' service so that could be coming into play.
verify? I'd like I need to edit a simple update without full re-deployment.
thx
I think the 'One Click Publishing' is most likely the culprit here. I created a sample Web Site project to experiment, and when I use the publish feature in Visual Studio, the files generated are indeed missing the Global.asax.cs file. Instead, a bin folder is created with a compilation called "App_global.asax.dll" even though it is a Web Site and not a Web Application. I am guessing this might be similar to what is happening for you as well.
If not, I found a link which might be useful to you in order to once again be able to edit a class on the server. Particularly, have a look at the second answer (the one not accepted as the best answer) to recreate a class file for the global code: Where is the Global.asax.cs file?
I tried this solution, and verified that the class file is editable on the server, and that it is dynamically compiled at run time (the modifications I made to the file worked immediately).
Hope this helps!
After editing the global.asax you need to rebuild and upload the DLL to the bin folder. Otherwise, your changes will not take effect.
You might be able to upload an web version of the global.asax that includes the code in that one file, which obviously does not require a .cs code behind. I used to do it with aspx files in an application but I have never tried it in the GLobal.asax file.

Is it possible to reference Silverlight 3 class library by .Net 3.5-based project in VS.net 2008?

First, I know Silverlight project can't reference to non-Silverlight based project like Windows class library or Asp.net MVC project. But I need to create my projects which can support both Silverlight-based project & Asp.net MVC project.
So, I created Silverlight-based project for my sharing source code. It works fine on VS.net 2008 & .Net 3.5 SP1. However, I found some error when I try to use some method of Silverlight-based project from .Net-based project like the following code.
Silverlight-based Method
public static void InitializeInstance(object obj)
{
// Initialize Field Value
foreach (FieldInfo fi in obj.GetType().GetFields())
{
foreach (Attribute attr in fi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
fi.SetValue(obj, dv.Value);
}
}
}
// Initialize Property Value
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
foreach (Attribute attr in pi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
if (pi.CanWrite)
{
pi.SetValue(obj, dv.Value, null);
}
}
}
}
}
.Net-based Method
private void Form1_Load(object sender, EventArgs e)
{
InitializeInstance(this);
}
Error Detail
System.IO.FileNotFoundException:
Could not load file or assembly
'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' or
one of its dependencies. The system
cannot find the file specified. File
name: 'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' at
InitializeInstance(Object obj)
Finally, I try to solve this problem by copying system.dll of Silverlight to output directory and reference it. It still shows same error. So, I think this error may be limitation of both .Net & Silverlight platform. Do you have any idea for avoid this issue?
PS. I know I can use this technique for a few sharing code. But it’s impossible to do this for my projects. Because it’s very complicate & very complex more than directly create Silverlight-based or .Net-based class library.
Thanks,
The trouble here is that those types share an assembly with a different strong name: System.Windows in Silverlight, PresentationFramework or PresentationCore on the desktop CLR.
So at runtime, the intended type cannot be loaded, and there are no type forwarders for the Silverlight-to-desktop types.
My recommended solution
Consider using file links, instead of actually trying to reference the same built binary.
This way, you can have a source structure for your project that may look like this:
MyApp\
Silverlight\
Page.xaml
Page.xaml.cs
(link) ..\AspMvc\MySharedDataFile.cs
AspMvc\
MySharedDataFile.cs
MyApp.cs
This way, the source will be re-compiled with both projects. We use this on the Silverlight Toolkit to build many controls, including the charting and data visualization controls, for both WPF and Silverlight. This is by rebuilding for each platform, instead of referencing the binaries from both.
To insert a link in Visual Studio, just right-click on your project or one of its folder, Add Existing Item, then find it in the explorer open file dialog. however, instead of just clicking the button, click on the little down arrow drop-down on the Add file button, and select the "Add as link" option.
Then, it simply builds that file from another location, but it is not a copy, so you can maintain it in one place and use in both.
A crazy solution
You can use .NET reflection from your desktop app that is of a much higher trust to actually create a new app domain, hook up to the assembly resolution event, and see what that does. You may be able to instead return the type from the desktop CLR, or just no-op these warnings.
No clue if it works.

Resources