Roslyn MsBuildWorkspace compilation emit does not contain my changes - c#

I'm trying to do minor changes to documents with roslyn and then compile the project to a new dll. But when I compile it to a new dll all my changes are gone. Am I missing something here?
var workspace = MSBuildWorkspace.Create();
var project = workspace.OpenProjectAsync(#"path\to\.csproj").Result;
var documents = project.DocumentIds;
foreach (var documentId in documents)
{
var document = project.GetDocument(documentId);
var root = document.GetSyntaxRootAsync().Result
var rewrite = new MyRewrite();
root = rewrite.Visit(root);
var newDocument = document.WithSyntaxRoot(root);
var compilation = newDocument.Project.GetCompilationAsync().Result;
// When I look at the sementatic model here it contains my changes.
var sementaticModel =
compilation.GetSemanticModel(newDocument.GetSyntaxTreeAsync().Result);
// But when I inspect this dll with dotPeek it's still the old code without changes.
compilation.Emit("new/dll/path");
}

Somehow it worked by changing:
var newDocument = document.WithSyntaxRoot(root);
to
var newDocument = document.WithText(root.GetText());

Related

Getting files/folders from sharepoint via the api in c#

I'm trying to link a c# application to a sharepoint directory, so I can create folders, download and upload files. However I am strugling with connecting to the correct folder.
I can retrieve the content from allitems.aspx, but I am not sure how to actually get the content from folder.
I have tried using the ClientContext - something like this:
ClientContext cxt = new ClientContext("https://xx.sharepoint.com/sites/");
cxt.Credentials = GetCredentials();
List list = cxt.Web.Lists.GetByTitle("Kontrakter");
var test = list.Views;
var test1 = cxt.Web.Lists;
cxt.Load(test1);
cxt.Load(list);
cxt.Load(test);
var a = 4;
var fullUri = new Uri("https://xx.sharepoint.com/sites/yy/Kontrakter/AllItems.aspx");
//var folder = cxt.Web.GetFolderByServerRelativeUrl(fullUri.AbsolutePath);
using (var rootCtx = new ClientContext(fullUri.GetLeftPart(UriPartial.Authority)))
{
rootCtx.Credentials = GetCredentials();
Uri webUri = Web.WebUrlFromPageUrlDirect(rootCtx, fullUri);
using (var ctx1 = new ClientContext(webUri))
{
ctx1.Credentials = GetCredentials();
var list1 = ctx1.Web.GetList(fullUri.AbsolutePath);
ctx1.Load(list1.RootFolder.Files);
ctx1.ExecuteQuery();
Console.WriteLine(list.RootFolder.Files.Count);
}
}
or via normal api calls like this:
https://xx.sharepoint.com/_api/Web/GetFolderByServerRelativeUrl('Kontrakter/Forms')/Files
The only way I can find some data is if I look into 'Shared documents/Forms'
I'm having problems understanding the directory structure and how I can actually find the content of files/folders.
Thanks in advance :)
Turned out I was missing a /sites in one of my uris.

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.

Get the current iteration path from TFS

I'm trying to get the current iteration path for the teams TFS project. The way I'm trying to do this is by using the blog from http://blog.johnsworkshop.net/tfs11-api-reading-the-team-configuration-iterations-and-areas/ . I start by getting the team configurations from the following code:
TfsTeamProjectCollection tpc = TFSConncetion(#"http://tfs/url");
var configSvc = tpc.GetService<TeamSettingsConfigurationService>();
var configs = configSvc.GetTeamConfigurationsForUser(projectUri);
The problem with this is that my configs is always null, even though I'm a member of the team. I'm positive my projects URI is correct as well. After this I would get the team settings and use that to display the current iteration path.
TeamSettings ts = config.TeamSettings;
Console.WriteLine(ts.CurrentIterationPath);
Even if this didn't work I can still query the iteration dates from the team setting to get the one iteration that has a start date before today and finish date after today. The main problem is that I can't get my TeamSettingsConfigurationService to return anything but null when I try to get the team configurations with my projects URI.
There must be something wrong with your server connection or the project uri you're passing as the other code looks okay.
Maybe try something like this:
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://server:8080/tfs/collection"),
new System.Net.NetworkCredential(tfsUserName, tfsPassword));
tpc.EnsureAuthenticated();
Connect to Team Foundation Server from a Console Application
There is a good sample here which you can download (WPF client) and it will allow you to select a server connection, Team Project and Team:
TFS API Part 46 (VS11) – Team Settings
You can step through it and check the values you're passing into your code.
The sample gets the team configuration information is the same way you have in your code.
TeamSettingsConfigurationService teamConfig = tfs.GetService<TeamSettingsConfigurationService>();
var configs = teamConfig.GetTeamConfigurationsForUser(new[] { projectInfo.Uri });
Once you have the collection of TeamConfiguration items then you need TeamSettings.CurrentIterationPath
I actually got the answer myself without using TeamSettingsConfigurationService at all. Here's how I did it:
private static XmlNode currentIterationNode;
TfsTeamProjectCollection tpc = TFSConncetion(#"http://tfs/url");
ICommonStructureService4 css = tpc.GetService<ICommonStructureService4>();;
WorkItemStore workItemStore = new WorkItemStore(tpc);
foreach (Project teamProject in workItemStore.Projects)
{
if (teamProject.Name.Equals("TeamProjectNameGoesHere"))
{
NodeInfo[] structures = css.ListStructures(teamProject.Uri.ToString());
NodeInfo iterations = structures.FirstOrDefault(n => n.StructureType.Equals("ProjectLifecycle"));
if (iterations != null)
{
XmlElement iterationsTree = css.GetNodesXml(new[] { iterations.Uri }, true);
XmlNodeList nodeList = iterationsTree.ChildNodes;
currentIterationNode = FindCurrentIteration(nodeList);
String currentIterationPath = currentIterationNode.Attributes["Path"].Value;
}
}
}
Where currentIterationPath is the current iteration path from TFS. The key to doing this was to get the NodeInfo[] array of structures and the NodeInfo iterations from these two lines of code I got from chamindacNavantis https://social.msdn.microsoft.com/Forums/vstudio/en-US/4b785ae7-66c0-47ee-a6d2-c0ad8a3bd420/tfs-get-iteration-dates-metadata?forum=tfsgeneral:
NodeInfo[] structures = css.ListStructures(teamProject.Uri.ToString());
NodeInfo iterations = structures.FirstOrDefault(n => n.StructureType.Equals("ProjectLifecycle"));
After that I created an xml with nodes of every iteration inside the team project. These nodes also have the start date and end dates of each iteration. So I checked each node for a start date before DateTime.Now and finish date after DateTime.Now, which is all FindCurrentIteration(nodeList) does.
And that will give you the current iteration node.
The simplest way I've found to do it was by using ICommonStructureService4 and TeamSettingsConfigurationService methods:
static TfsTeamProjectCollection _tfs = TfsTeamProjectCollectionFactory
.GetTeamProjectCollection("<tfsUri>")
(...)
static string GetCurrentIterationPath()
{
var css = _tfs.GetService<ICommonStructureService4>();
var teamProjectName = "<teamProjectName>";
var project = css.GetProjectFromName(teamProjectName);
var teamName = "<teamName>";
var teamSettingsStore = _tfs.GetService<TeamSettingsConfigurationService>();
var settings = teamSettingsStore
.GetTeamConfigurationsForUser(new[] { project.Uri })
.Where(c => c.TeamName == teamName)
.FirstOrDefault();
if (settings == null)
{
var currentUser = System.Threading.Thread.CurrentPrincipal.Identity.Name;
throw new InvalidOperationException(
$"User '{currentUser}' doesn't have access to '{teamName}' team project.");
}
return settings.TeamSettings.CurrentIterationPath;
}

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()

How do I get a previous version of a file with libgit2sharp

I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is, at the very least restored to previous condition.
My initial approach was to try to stash, checkout path on the file I want, save that to a string variable, then stash pop. Is there a way to stash pop? I can't find it easily. Here's the code I have so far:
using (var repo = new Repository(DirectoryPath, null))
{
var currentCommit = repo.Head.Tip.Sha;
var commit = repo.Commits.Where(c => c.Sha == commitHash).FirstOrDefault();
if (commit == null)
return null;
var sn = "Stash Name";
var now = new DateTimeOffset(DateTime.Now);
var diffCount = repo.Diff.Compare().Count();
if(diffCount > 0)
repo.Stashes.Add(new Signature(sn, "x#y.com", now), options: StashModifiers.Default);
repo.CheckoutPaths(commit.Sha, new List<string>{ path }, CheckoutModifiers.None, null, null);
var fileText = File.ReadAllText(path);
repo.CheckoutPaths(currentCommit, new List<string>{path}, CheckoutModifiers.None, null, null);
if(diffCount > 0)
; // stash Pop?
}
If there's an easier approach than using Stash, that would work great also.
Is there a way to stash pop? I can't find it easily
Unfortunately, Stash pop requires merging which isn't available yet in libgit2.
I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is
You may achieve such result by opening two instances of the same repository, each of them pointing to different working directories. The Repository constructor accepts a RepositoryOptions parameter which should allow you to do just that.
The following piece of code demonstrates this feature. This creates an additional instance (otherRepo) that you can use to retrieve a different version of the file currently checked out in your main working directory.
string repoPath = "path/to/your/repo";
// Create a temp folder for a second working directory
string tempWorkDir = Path.Combine(Path.GetTempPath(), "tmp_wd");
Directory.CreateDirectory(newWorkdir);
// Also create a new index to not alter the main repository
string tempIndex = Path.Combine(Path.GetTempPath(), "tmp_idx");
var opts = new RepositoryOptions
{
WorkingDirectoryPath = tempWorkDir,
IndexPath = tempIndex
};
using (var mainRepo = new Repository(repoPath))
using (var otherRepo = new Repository(mainRepo.Info.Path, opts))
{
string path = "file.txt";
// Do your stuff with mainrepo
mainRepo.CheckoutPaths("HEAD", new[] { path });
var currentVersion = File.ReadAllText(Path.Combine(mainRepo.Info.WorkingDirectory, path));
// Use otherRepo to temporarily checkout previous versions of files
// Thank to the passed in RepositoryOptions, this checkout will not
// alter the workdir nor the index of the main repository.
otherRepo.CheckoutPaths("HEAD~2", new [] { path });
var olderVersion = File.ReadAllText(Path.Combine(otherRepo.Info.WorkingDirectory, path));
}
You can get a better grasp of this RepositoryOptions type by taking a look at the tests in RepositoryOptionFixture that exercise it.

Categories