4

I am building a scaffolding tool for visual studio, and need to present a list of classes that derive from a certain abstract class, but only classes in the active project. I have it working, but it takes visual studio a little while to run the code.

ICodeTypeService codeTypeService = (ICodeTypeService)Context
                    .ServiceProvider.GetService(typeof(ICodeTypeService));

var types = codeTypeService.GetAllCodeTypes(Context.ActiveProject);

foreach (var type in types)
{
    type.
    if (type.Kind == vsCMElement.vsCMElementClass)
    {
        foreach (var d in type.Bases)
        {
            var dClass = d as CodeClass;
            var name = type.Name;
            if (dClass.Name == "MyAbstractClass")
            {
                if (type.Namespace.Name.Contains(Context.ActiveProject.Name))
                {
                    yield return type.Name;
                }
            }

        }
    }
}

So I'm having to do a check of the namespace when it finds a matching class to make sure it is in my project. This feels like I am doing a hell of a lot of unnecessary work. This is the example they give with the scaffold template visual studio project:

ICodeTypeService codeTypeService = (ICodeTypeService)Context
                    .ServiceProvider.GetService(typeof(ICodeTypeService));

return codeTypeService
    .GetAllCodeTypes(Context.ActiveProject)
    .Where(codeType => codeType.IsValidWebProjectEntityType())
    .Select(codeType => new ModelType(codeType));

is the codetypeservice the right way to do this?

EDIT Basically when it searches for classes it doesn't just do it in the current active project, but also searches all the referenced projects. This is why I have to check the namespace so I only get the results from the active project. I reckon this is why it is slowed right down because the referenced projects are quite large so it is doing a hell of a lot of completely unnecessary work...

Hazza
  • 6,441
  • 3
  • 24
  • 37
  • can you define 'a little while' also have you considered Roslyn for this? seems like if speed is what you are looking for, it would be the way to go. – Maslow Oct 01 '14 at 13:19
  • Takes about 10-15 seconds on my machine and Visual Studio goes into "not responding" mode during it. So not terrible but...yeah. And yes, Roslyn does look like the way forward but that is for VS2014, right? – Hazza Oct 01 '14 at 13:27
  • I realise I haven't really explained the issue very well...I will add an edit to the post – Hazza Oct 01 '14 at 13:31
  • you can use `Roslyn` to analyze and write c# already. plus parts of `Roslyn` are already in VS2013 – Maslow Oct 01 '14 at 13:41
  • I see, I didn't realise that. I will have to look into it. Thank you, you have been most helpful. One last thing, do you have any good links to resources on Roslyn? The best I've found seem to be the Roslyn codeplex, has some examples on there which should get me going. Cheers again – Hazza Oct 01 '14 at 15:09
  • Here's the roslyn sample I wrote http://code.msdn.microsoft.com/Implementing-a-Code-Action-28579fb1 – Maslow Oct 01 '14 at 17:13

1 Answers1

5

I noticed someone upvoted this question, reminding me to post how I got it to work a little faster. Roslyn, as pointed out by Maslow, is cool but Roslyn was not available when I was building this extension, except in preview, so basically it was just pointless.

My extension is on GitHub, here is a relevant code snippet: https://github.com/Hazzamanic/orchardizer/blob/master/Orchardization/UI/CustomViewModel.cs

But in case GitHub should die/someone pays me a million dollars for my code, here is the relevant code...

/// <summary>
/// Recursively gets all the ProjectItem objects in a list of projectitems from a Project
/// </summary>
/// <param name="projectItems">The project items.</param>
/// <returns></returns>
public IEnumerable<ProjectItem> GetProjectItems(EnvDTE.ProjectItems projectItems)
{
    foreach (EnvDTE.ProjectItem item in projectItems)
    {
        yield return item;

        if (item.SubProject != null)
        {
            foreach (EnvDTE.ProjectItem childItem in GetProjectItems(item.SubProject.ProjectItems))
                yield return childItem;
        }
        else
        {
            foreach (EnvDTE.ProjectItem childItem in GetProjectItems(item.ProjectItems))
                yield return childItem;
        }
    }

}

Once you have all the project items you can filter by them. This seems to work fine in quite large projects, though I don't really have any massively huge projects to be fair. In my case, I wanted to find all classes with a base class DatMigrationImpl. So I had this kind of code:

var allClasses = GetProjectItems(Context.ActiveProject.ProjectItems).Where(v => v.Name.Contains(".cs"));
// check for .cs extension on each

foreach (var c in allClasses)
{
    var eles = c.FileCodeModel;
    if (eles == null)
        continue;
    foreach (var ele in eles.CodeElements)
    {
        if (ele is EnvDTE.CodeNamespace)
        {
            var ns = ele as EnvDTE.CodeNamespace;
            // run through classes
            foreach (var property in ns.Members)
            {
                var member = property as CodeType;
                if (member == null)
                    continue;

                foreach (var d in member.Bases)
                {
                    var dClass = d as CodeClass;
                    if (dClass == null)
                        continue;

                    var name = member.Name;
                    if (dClass.Name == "DataMigrationImpl")
                    {
                        yield return new Migration(member);
                    }
                }
            }
        }
    }
}
Hazza
  • 6,441
  • 3
  • 24
  • 37