Monthly Archives: March 2020

An ADR Visual Studio Tool – Part 7 – Adding a Context Menu Item

In my previous post I continued with my little series on writing an extension for Visual Studio by completing the functionality to view existing ADRs within the solution. You can see the first post here

In this post, we’ll cover the process of adding a command to the solution and project context menu. Having completed the screen that will show existing ADRs, we now want to allow the user to right-click on a project or solution and select to add a new ADR:

The first step is to add the command; fortunately, this is a pre-made template, so just select to add a new item (ironically), and pick Command:

This will create you a menu item that will appear under the Tools menu by default, and will display a message box when selected:

There are two things that we need to change about this command: the text (we don’t want it to read “Invoke AddAdrCommand”), and the location (we want it to be available from the right-click context menu of a project). Both of those things are changed in the file AdrPackage.vsct (if your project is called Aardvark, this will be named AardvarkPackage.vsct).

If you have a look in that file, you’ll see something called MyMenuGroup; which is referenced in three places. The first defines what it is:

This is where you can change the command text (as I have above).

The second, where it is:

This initially looks like this:

<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS" />

Which adds the menu to the top level Tools menu; change it to:

<Parent guid="guidSHLMainMenu" id="IDM_VS_CSCD_PROJECT_ADD" />

As I have.

There are other options here.

The third is the ID Symbol:

If you decide to change the name of MyMenuGroup, you will need to do so in all three places above.

Getting the Context of the Current Project

Now that we’ve moved the menu to the context menu, we’ll need to find which project we’re in by accessing the DTE Service – this is retrieved by calling the command:

            var dte = await package.GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2;

You can then find out what’s selected by accessing the selected hierarchy:

            UIHierarchy uih = (UIHierarchy)dte.Windows.Item(
                EnvDTE.Constants.vsWindowKindSolutionExplorer).Object;
            Array selectedItems = (Array)uih.SelectedItems;

Here’s the full code of the Execute method, to display the selected project:

        private async void Execute(object sender, EventArgs e)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);

            var dte = await package.GetServiceAsync(typeof(DTE)).ConfigureAwait(false) as DTE2;

            UIHierarchy uih = (UIHierarchy)dte.Windows.Item(
                EnvDTE.Constants.vsWindowKindSolutionExplorer).Object;
            Array selectedItems = (Array)uih.SelectedItems;
            foreach (UIHierarchyItem selectedItem in selectedItems)
            {
                // Show a message box to prove we were here
                VsShellUtilities.ShowMessageBox(
                    this.package,
                    selectedItem.Name,
                    "Selected Project",
                    OLEMSGICON.OLEMSGICON_INFO,
                    OLEMSGBUTTON.OLEMSGBUTTON_OK,
                    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
            }
        }

We’re not far off now – there’s a few little issues, but the main thing that’s left is that we’re not actually adding anything – just displaying a message. The next step is to get it to actually add a file, but we’ll come to that in the next post.

The code for this project can be found here.

References

https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/guids-and-ids-of-visual-studio-toolbars?view=vs-2017

https://docs.microsoft.com/en-us/visualstudio/extensibility/internals/guids-and-ids-of-visual-studio-menus?view=vs-2017

https://stackoverflow.com/questions/51967027/vsix-project-context-menu

https://michaelscodingspot.com/visual-studio-2017-extension-development-tutorial-part-3-add-context-menu-get-selected-code/

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/be61c3bb-aac2-48ea-88ad-883a38b526e2/how-to-add-a-command-button-to-the-project-add-submenu-in-a-vsct-file?forum=vsx

https://stackoverflow.com/questions/52489541/problem-with-dte2-for-running-commands-in-visual-studio

https://docs.microsoft.com/en-us/visualstudio/extensibility/walkthrough-accessing-the-dte-object-from-an-editor-extension?view=vs-2019

https://www.mztools.com/articles/2014/MZ2014009.aspx

Unit Testing EF Core – How to Invoke the Contents of OnModelCreating

In this post, I wrote about how you can test an EF Database, using an InMemory database.

I’m guessing a few people reading this will be thinking this is stating the bloody obvious, but it certainly wasn’t to me. Imagine you have a DBContext with some seed data; for example:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {

            modelBuilder.Entity<EntityType1>().HasData(
                new EntityType1
                {
                    Id = "1",
                    Name = "Test1",
                    Description = "Testing",
                    IsTest = true,
                    SomeNumber = 1
                },

In the above linked article, this seed data will never fire; however, simply calling:

context.Database.EnsureCreated();

Once the context is created, will force the migrations to be run inside the in-memory instance, and you should end up with a system mirroring what you would see, should you run this migration against a physical database.

Caveat Emptor

When using this against EF Core 2.2 I found, what appeared to be, this issue. The error being, while I was trying to insert a record, an error returned saying that an item with the same key exists on the table. However, no such item should exist. The linked article seems to imply that this relates to a bug with the insert for the in-memory database, and that it is resolved for EF Core 3.0. I haven’t validated this, so it may, or may not work to upgrade to EF Core 3.0. Please add a comment if you can validate or negate this.

References

https://github.com/dotnet/efcore/issues/11666

https://github.com/dotnet/efcore/issues/6872

Add HttpClientFactory to an Azure Function

Since I first wrote about dependency injection in Azure Functions things have moved on a bit. These days, the Azure Functions natively* support DI. In this post, I’ll cover, probably the most common, DI scenario: adding HttpClientFactory to your project.

I’ll assume that you have an Azure function, and that it looks something like this:

    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }
    }

I’ll assume that, because that’s the default HTTP Trigger function you get when you select to create a new function.

NuGet Packages

You’ll need a few NuGet packages; first, you’ll need:

Install-Package Microsoft.Extensions.Http

Which will allow you to use the HttpClientFactory. You’ll also need some packages for the DI:

Install-Package Microsoft.Azure.Functions.Extensions
Install-Package Microsoft.NET.Sdk.Functions

Startup

If you create a new MVC project, you get a Startup class, which manages all your DI, etc. So we’re going to create one here. Create a Startup.cs class in the function app:

    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddHttpClient();
        }
    }

The Configure method is a member of FunctionsStartup (ctrl-. to add the override). You’ll also need to add the following line outside of the namespace:

[assembly: FunctionsStartup(typeof(FullNamespace.Startup))]

Essentially, FullNamepsace here refers to the fully qualified Startup class in your project. Without this line, nothing will be added to the IoC container.

The AddHttpClient call inside Configure adds HttpClientFactory to your IoC container.

Azure Function

If you have a look at the code above, you’ll notice the class is static. We can’t have constructor injection into a static class (because we can’t have a constructor); let’s change that to an instance class:

        public Function1(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

You’ll also need to change the function itself from static to instance:

        public async Task<IActionResult> Run(

That’s it – you can now reference HttpClientFactory from inside your function.

Notes

* Maybe not exactly ‘natively’

References

https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection

React Tips: 5 – Raising Events

Raising an Event is an interesting concept is React: essentially, you pass a function into a component; and then, when the component handles an event, it simply calls the function handler. This is referred to as “Raising” the event, because despite the event happening in the component, it is dealt with by a parent of that component. In C#, you would refer to this as a delegate, and in C, this would be passing a pointer to a function.

Here’s a component:

function MyComponent(props) {
    
    return (
        <div>
            <label>Change the text below</label>
            <input type='text' onChange={props.onChange} />
        </div>
    );
}
export default MyComponent;

If you look at that code, you’ll see that the onChange event is handled by a function passed in, called onChange. Here’s the calling component:

export class Home extends Component {

  render () {
    return (
      <div>
        <MyComponent onChange={this.onChange} />
      </div>
    }

The onChange function in Home will be called whenever the MyComponent textbox is changed.

Important Note

Since this is React, one of the things that you may do in the onChange function is to update the state. I have previously written on how to deal with this, or indeed any method which uses the this property.