Add references to a project template programmatically - c#

I'm doing a project Template. there is some custom parameters ( The services and attributes that he will be using during the implementation of the project ). Each service needs a specific reference. So depending on the custom parameters, I prepare a list that contains the paths of the needed assemblies. How can I add them to the project ?! I tried the following code but no result.`
var workspace = MSBuildWorkspace.Create();
var solution = workspace.OpenSolutionAsync(#"path").Result;
var projects = solution.Projects;
foreach (EnvDTE.Project proj in solution.Projects)
{
if (proj.Name == projectName)
{
VSLangProj.VSProject vsproj = (VSLangProj.VSProject)proj.Object;
foreach (string dll in Wizard.View.View.refs)
{
vsproj.References.Add(dll);
}
}
}`

Related

how to read the app.config file of another project

Suppose I have a project A and project B. I want to access config file of project A in project B then how I can achieve this?
Note: I'm trying to access custom section of the config file as below
var connectionManagerDataSection = ConfigurationManager.GetSection(ConnectionManagerDataSection.SectionName) as ConnectionManagerDataSection;
List<AddEndpoint> AddEndpoint = new List<AddEndpoint>();
if (connectionManagerDataSection != null)
{
foreach (MyConfigInstanceElement endpointElement in connectionManagerDataSection.ConnectionManagerEndpoints)
{
var endpoint = new AddEndpoint() { Name = endpointElement.Name, Code = endpointElement.Code };
AddEndpoint.Add(endpoint);
}
}

Visual Studio extension: Change the hint path of an assembly reference

I'm writing a Visual Studio extension and I would like to change the hint path of an assembly reference of a C#-project without to trigger the "File Modification Detected"-dialog.
<Reference Include="SomeAssembly">
<HintPath>C:\ChangeMe\SomeAssembly.dll</HintPath>
</Reference>
But in the VSLangProj110.Reference5-interface I can't find any property that I can use. (Accessed through VSLangProj140.VSProject3.References)
Microsoft.Build.BuildEngine.Project is outdated. Here is an updated working solution.
foreach (var dteProject in dte.Solution.Projects.OfType<Project>())
{
// You can edit the project through an object of Microsoft.Build.Evaluation.Project
var buildProject = ProjectCollection.GlobalProjectCollection.GetLoadedProjects(dteProject.FullName).First();
foreach (var item in buildProject.Items.Where(obj => obj.ItemType == "Reference"))
{
var newPath = SomeMethod(item.GetMetadata("HintPath"));
item.SetMetadataValue("HintPath", newPath);
}
// But you have to save through an object of EnvDTE.Project
dteProject.Save();
}
I create a demo and reproduce your issue on my side. I think it is a by design issue, if you modify the project outside the environment, it will popup the "File Modification Detected" dialog, we need to change it by manually.
you could post a feedback on the following link: https://connect.microsoft.com/VisualStudio/Feedback 
Update:
DTE2 dte = (DTE2)this.ServiceProvider.GetService(typeof(DTE));
EnvDTE.Project currentProject = dte.Solution.Projects.Item(1);
// Create a new Project object.
Microsoft.Build.BuildEngine.Project project = new Microsoft.Build.BuildEngine.Project();
project.Load(currentProject.FullName);
foreach (BuildItemGroup ig in project.ItemGroups)
{
//var items = ig.ToArray();
foreach (BuildItem item in ig.ToArray())
{
if (item.Include == "ClassLibrary1")
{
item.Include = "Utils";
item.SetMetadata("HintPath", #"C:\relativePath\Utils.dll");
}
}
}
project.Save(currentProject.FullName);

Get dependencies between classes in Roslyn

I am successfully getting dependencies between projects with Roslyn, and now I would like to get dependencies between classes, similar to the Code Map feature in Visual Studio Enterprise.
Here is my code, the "?????" part is where I imagine I could get something. I am very new to the Roslyn API, though, and I don't know how to proceed from there on.
Solution solution = MSBuildWorkspace.Create()
.OpenSolutionAsync(Path.Combine(repoRootFolder, "MySolution.sln"))
.Result;
ProjectDependencyGraph projdeps = solution.GetProjectDependencyGraph();
Digraph graph = new Digraph();
foreach (ProjectId projectId in projdeps.GetTopologicallySortedProjects())
{
string projName = solution.GetProject(projectId).Name;
var projDeps = projdeps.GetProjectsThatThisProjectDirectlyDependsOn(projectId);
foreach (ProjectId depId in projDeps)
{
Project dep = solution.GetProject(depId);
Compilation compilation = dep.GetCompilationAsync().Result;
foreach (var syntree in compilation.SyntaxTrees)
{
foreach (var classNode in syntree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>())
{
var classesThisClassNodeReferences = ?????????
}
}
string depName = dep.Name;
graph.Dependencies.Add(new Dependency
{
Source = projName,
Target = depName
});
}
}
I'm not sure about your requirements, but you can probably go for checking all descendant SyntaxNodes of the class and get the corresponding symbol, and it's type, and then collect these types. Something like the following:
var semantic = compilation.GetSemanticModel(syntree);
var typesForCurrentClass = classNode.DescendantNodes().Select(n =>
semantic.GetTypeInfo(n).Type);
Note that there can be multiple typesForCurrentClass for a given class symbol because of partial classes.

Use Roslyn MSBuildWorkspace Project AddAnalyzerReference doesn't load analyzers

I'm working on a code report project.
Currently, I'm able to compile the solution projects, get the diagnostics related to the compilation, etc..
The problem appears when I try to load my custom IDiagnosticAnalyzers, I've tried to use the AnalyzerFileReference and the AnalyzerImageReference without any result, Always I access the projects.Analizers are empty.
var inmutableArray = (new List<IDiagnosticAnalyzer>
{
new VariableEndedWithIdNamedCorrectlyDiagnosticAnalyzer()
}).ToImmutableArray();
var analyzerImageReference = new AnalyzerImageReference(inmutableArray);
foreach (Project project in solution.Projects)
{
project.AddAnalyzerReference(analyzerImageReference );
//No analizers loaded....
}
UPDATE (thanks for the feedback [Josh Varty])
I've tried this two ways:
var newProjects = new List<Project>();
foreach (Project project in solution.Projects)
{
var newSolutionn= solution.AddAnalyzerReference(project.Id, analyzerImageReference);
newProjects.Add(newSolutionn.Projects.FirstOrDefault(p=> p.Id == project.Id));
}
foreach (Project project in solution.Projects)
{
var newProject = project.AddAnalyzerReference( analyzerImageReference);
}
In both cases have the analyzers loaded but when I get the compilation and I get the diagnostics, I don't get the output related to this analyzers (I think they are not being called at the get compilation function).
var compilation = newProject.GetCompilationAsync().Result;
var diagnostics = compilation.GetDiagnostics();
Any suggestions?
As I commented, most Roslyn objects are immutable. This means methods like AddAnalyzerReference() don't mutate the project, but instead return a new one.
I don't have an analyzer to test this, but I believe you can use the following. Note that I'm using Solution.AddAnalyzerReference() instead of the one you were using.
var inmutableArray =(new List<IDiagnosticAnalyzer>
{
new VariableEndedWithIdNamedCorrectlyDiagnosticAnalyzer()
}).ToImmutableArray();
var analyzerImageReference = new AnalyzerImageReference(inmutableArray);
Solution newSolution = solution;
//We iterate over the original solution
foreach (Project project in solution.Projects)
{
//But we save our work in the newSolution
newSolution = newSolution.AddAnalyzerReference(project.Id, analyzerImageReference);
}
//Now newSolution should contain all your changes.
//Maybe you want to save this reference?
solution = newSolution;
I've found the way to do it:
public static Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync(this Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
options = options ?? new AnalyzerOptions(ImmutableArray<AdditionalStream>.Empty, ImmutableDictionary<string, string>.Empty);
Compilation newCompilation = null;
var analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, options, out newCompilation, cancellationToken);
newCompilation.GetDiagnostics(cancellationToken);
return analyzerDriver.GetDiagnosticsAsync();
}
I've published a version of the open source project that I've been working using Roslyn, you can see the code and other thing related to analyzers and codefix.
https://bitbucket.org/jrierapeiro/codeanalyzer
I had similar question which i answered over here.
You have to use compilation.WithAnalyzer(analyzer) and then getDiagnostics()

RazorGenerator PrecompiledMvcEngine cannot locate Partial View or Editor Templates across assemblies

I have a large MVC 4 site that is broken into one project per MVC Area. We use RazorGenerator to pre-compile all of our views into the project assembly for deployment and use the PrecompiledMvcEngine for our ViewEngine.
I have just created a new Area that I would like to share the Shared views from another assembly, but I get an InvalidOperationException when trying to locate a Partial View and none of the DisplayTemplates or EditorTemplates appear to be found either.
I believe it is similar to the issue described in this question.
My code in the RazorGenerator App_Start is like this:
var assemblies = new List<Tuple<string, Assembly>>()
{
Tuple.Create("Areas/Area1", typeof(ABC.Project1.AreaRegistration).Assembly),
Tuple.Create("Areas/Area2", typeof(ABC.Project2.AreaRegistration).Assembly),
};
// Get rid of the default view engine
ViewEngines.Engines.Clear();
foreach ( var assembly in assemblies )
{
var engine = new PrecompiledMvcEngine(assembly.Item2, assembly.Item1) {
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
};
// Allow sharing of Area1 Shares views with Area2
if (assembly.Item1 == "Areas/Area2")
{
var sharedPaths = new[] { "~/Areas/Area1/Views/Shared/{0}.cshtml" };
engine.ViewLocationFormats = engine.ViewLocationFormats.Concat(sharedPaths).ToArray();
engine.MasterLocationFormats = engine.MasterLocationFormats.Concat(sharedPaths).ToArray();
engine.PartialViewLocationFormats = engine.PartialViewLocationFormats.Concat(sharedPaths).ToArray();
}
ViewEngines.Engines.Insert(0, engine);
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}
When I encounter a partial reference in a view within Area2 like #Html.Partial("Partials/Footer"), I get the exception. It appears that Razor is looking for the correct path
System.InvalidOperationException: The partial view 'Partials/Footer' was not found or no view engine supports the searched locations. The following locations were searched:
~/Areas/Area2/Views/Home/Partials/Footer.cshtml
~/Areas/Area2/Views/Shared/Partials/Footer.cshtml
~/Views/Home/Partials/Footer.cshtml
~/Views/Shared/Partials/Footer.cshtml
~/Areas/Area1/Views/Shared/Partials/Footer.cshtml
at System.Web.Mvc.HtmlHelper.FindPartialView(ViewContext viewContext, String partialViewName, ViewEngineCollection viewEngineCollection)
Looking at the source code of the PrecompiledMvcEngine, it appears that it only looks for views within the assembly.
I initially thought that the view system would look across all registered ViewEngines when trying to resolve a path, but that seems to be an incorrect assumption (and I can see why one would not do that).
Is there a way to share the views across multiple assemblies?
Update
I've worked around the issue by creating a custom version of the PrecompiledMvcEngine that takes a list of assemblies in its constructor. The core change is this:
_mappings = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
foreach (var kvp in assemblies)
{
var baseVirtualPath = NormalizeBaseVirtualPath(kvp.Key);
var assembly = kvp.Value;
var mapping = from type in assembly.GetTypes()
where typeof(WebPageRenderingBase).IsAssignableFrom(type)
let pageVirtualPath = type.GetCustomAttributes(inherit: false).OfType<PageVirtualPathAttribute>().FirstOrDefault()
where pageVirtualPath != null
select new KeyValuePair<string, Type>(CombineVirtualPaths(baseVirtualPath, pageVirtualPath.VirtualPath), type);
foreach (var map in mapping)
{
_mappings.Add(map);
}
}
Any better alternative or pitfalls with this approach?

Categories