So, I am writing a code generator tool and I want to get the default namespace from an existing csproj that the user will have to specify. Essentially I want to be able to load the csproj from a path and get some configuration from it.
I also want to be able to get all existing projects from a solution, from which I would use the solution file from a path.
I've looked into code analyzers and believe that's the way to go, but I haven't found a single example of what I want to achieve so far.
I do not wish to give support to older format csproj, just the Microsoft.NET.Sdk format that came with VS2017.
So the solution as stated in the comments was to use an MSBuildWorkspace.
My code looks something like this:
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis;
public class ProjectLoader : IProjectLoader
{
public string GetVSProjectDefaultNamespace(string projectPath)
{
var workspace = MSBuildWorkspace.Create();
Project project;
try
{
project = workspace.OpenProjectAsync(projectPath).Result;
}
catch(Exception e)
{
throw new Exception("The requested project failed to load. Make sure the path to the project file is correct.", e);
}
var defaultNamespace = project.DefaultNamespace ?? project.Name;
return defaultNamespace;
}
}
It's important to install the following nuget packages:
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.Workspaces.MSBuild
For the previous snippet to work.
Hope this helps anybody else!
Related
I would like to create my custom dotnet tool and in its implementation, I need to get the list of packages referenced by the project (and the packages they depend on).
From the command line, I can run something like this to get that list:
dotnet list package --include-transitive
I was trying to find how this is implemented in the dotnet sdk repo but the repo is so massive it is very difficult to find anything.
Does anyone have an idea where this is implemented or, even better, can you provide a C# code example on how to get this list in code.
I followed the suggestion from #alexandru-clonțea and tried out the code on github.
In my opinion it actually answers the question correctly.
It involves running dotnet restore on the project and generate a dependency graph file and then reading the file using the NuGet.ProjectModel library.
The core part of the code that reads the dependencies is this:
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NuGet.ProjectModel;
namespace YourNamespace
{
/// <remarks>
/// Credit for the stuff happening in here goes to the https://github.com/jaredcnance/dotnet-status project
/// </remarks>
public class DependencyGraphService
{
public DependencyGraphSpec GenerateDependencyGraph(string projectPath)
{
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
var arguments = new[] {"msbuild", $"\"{projectPath}\"", "/t:GenerateRestoreGraphFile", $"/p:RestoreGraphOutputPath={tempFile}"};
try
{
var runStatus = DotNetRunner.Run(Path.GetDirectoryName(projectPath), arguments);
if (!runStatus.IsSuccess)
throw new Exception($"Unable to process the the project `{projectPath}. Are you sure this is a valid .NET Core or .NET Standard project type?" +
$"\r\n\r\nHere is the full error message returned from the Microsoft Build Engine:\r\n\r\n" + runStatus.Output);
return new DependencyGraphSpec(JsonConvert.DeserializeObject<JObject>(File.ReadAllText(tempFile)));
}
finally
{
if(File.Exists(tempFile))
File.Delete(tempFile);
}
}
}
}
I can't use "Zipfile" class in the name space "System.IO.Compression" my code is :
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath, CompressionLevel.Fastest,true);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
the error is :
The name 'zipfile' does not exist in the current context
How I can solve it ?
You need an extra reference for this; the most convenient way to do this is via the NuGet package System.IO.Compression.ZipFile
<!-- Version here correct at time of writing, but please check for latest -->
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
If you are working on .NET Framework without NuGet, you need to add a dll reference to the assembly, "System.IO.Compression.FileSystem.dll" - and ensure you are using at least .NET 4.5 (since it doesn't exist in earlier frameworks).
For info, you can find the assembly and .NET version(s) from MSDN
For those who are green programmers in .NET, to add the DLL reference as MarcGravell noted, you follow these steps:
To add a reference in Visual C#
In Solution Explorer, right-click the project node and click Add Reference.
In the Add Reference dialog box, select the tab indicating the type of component you want to reference.
Select the components you want to reference, and then click OK.
From the MSDN Article, How to: Add or Remove References By Using the Add Reference Dialog Box.
you can use an external package if you cant upgrade to 4.5. One such is Ionic.Zip.dll from DotNetZipLib.
using Ionic.Zip;
you can download it here, its free. http://dotnetzip.codeplex.com/
Just go to References and add "System.IO.Compression.FileSystem".
In solution explorer, right-click References, then click to expand assemblies, find System.IO.Compression.FileSystem and make sure it's checked. Then you can use it in your class - using System.IO.Compression;
Add Reference Assembly Screenshot
A solution that helped me:
Go to Tools > NuGet Package Manager > Manage NuGet Packaged for Solution... > Browse >
Search for System.IO.Compression.ZipFile and install it
System.IO.Compression is now available as a nuget package maintained by Microsoft.
To use ZipFile you need to download System.IO.Compression.ZipFile nuget package.
I know this is an old thread, but I just cannot steer away from posting some useful info on this. I see the Zip question come up a lot and this answers nearlly most of the common questions.
To get around framework issues of using 4.5+... Their is a ZipStorer class created by jaime-olivares: https://github.com/jaime-olivares/zipstorer, he also has added an example of how to use this class as well and has also added an example of how to search for a specific filename as well.
And for reference on how to use this and iterate through for a certain file extension as example you could do this:
#region
/// <summary>
/// Custom Method - Check if 'string' has '.png' or '.PNG' extension.
/// </summary>
static bool HasPNGExtension(string filename)
{
return Path.GetExtension(filename).Equals(".png", StringComparison.InvariantCultureIgnoreCase)
|| Path.GetExtension(filename).Equals(".PNG", StringComparison.InvariantCultureIgnoreCase);
}
#endregion
private void button1_Click(object sender, EventArgs e)
{
//NOTE: I recommend you add path checking first here, added the below as example ONLY.
string ZIPfileLocationHere = #"C:\Users\Name\Desktop\test.zip";
string EXTRACTIONLocationHere = #"C:\Users\Name\Desktop";
//Opens existing zip file.
ZipStorer zip = ZipStorer.Open(ZIPfileLocationHere, FileAccess.Read);
//Read all directory contents.
List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();
foreach (ZipStorer.ZipFileEntry entry in dir)
{
try
{
//If the files in the zip are "*.png or *.PNG" extract them.
string path = Path.Combine(EXTRACTIONLocationHere, (entry.FilenameInZip));
if (HasPNGExtension(path))
{
//Extract the file.
zip.ExtractFile(entry, path);
}
}
catch (InvalidDataException)
{
MessageBox.Show("Error: The ZIP file is invalid or corrupted");
continue;
}
catch
{
MessageBox.Show("Error: An unknown error ocurred while processing the ZIP file.");
continue;
}
}
zip.Close();
}
Add System.IO.Compression.ZipFile as nuget reference it is working
The issue here is that you just Added the reference to System.IO.Compression it is missing the reference to System.IO.Compression.Filesystem.dll
And you need to do it on .net 4.5 or later (because it doesn't exist on older versions).
I just posted a script on TechNet Maybe somebody would find it useful it requires .net 4.5 or 4.7
https://gallery.technet.microsoft.com/scriptcenter/Create-a-Zip-file-from-a-b23a7530
What's the best way to read (ideally via C#) the packages listed in packages.config files?
Within our source code repository I have a lot of solutions and projects and equally a lot of packages.config files. I'm trying to build a consolidated list of packages (and versions) in use across my source code repository.
I can see there is a NuGet.Core package available - how could I use this to achieve my goal?
Thanks
If you do not want to read the XML directly you can install the NuGet.Core NuGet package and then use the PackageReference class.
Here is some example code that uses this class to print out the package id and its version.
string fileName = #"c:\full\path\to\packages.config";
var file = new PackageReferenceFile(fileName);
foreach (PackageReference packageReference in file.GetPackageReferences())
{
Console.WriteLine("Id={0}, Version={1}", packageReference.Id, packageReference.Version);
}
You will need to find the packages.config files yourself which you can probably do with a directory search, something like:
foreach (string fileName in Directory.EnumerateFiles("d:\root\path", "packages.config", SearchOption.AllDirectories))
{
// Read the packages.config file...
}
An alternative and more up to date way of doing this is to install the NuGet.Packaging NuGet package and use code similar to:
var document = XDocument.Load (fileName);
var reader = new PackagesConfigReader (document);
foreach (PackageReference package in reader.GetPackages ())
{
Console.WriteLine (package.PackageIdentity);
}
As suggested you will need to install NuGet.Core, your solution may have several projects in it, so it's good to know how to specify the project name when installing. Let's say your Solution is MySolution and you have two projects Project01 & Project02 and you only want to install in Project02.
Install-Package NuGet.Core -ProjectName Project02
Next you will need to add a using statement in the whatever.cs page you are going to do your work to target the package and let's say you just want to get the version number so that you can print it out somewhere on your website. That is actually what I wanted to do.
using NuGet;
next I wanted to get at a specific package and read it's version number so that when we release my software I have a visual identifier at a certain place on my website that I can go to and see the version that is in production.
here is the code I wrote to populate a webforms label on my page.
protected void Page_Load(Object sender, EventArgs e)
{
var pkgRefpath = Server.MapPath("~/packages.config");
PackageReferenceFile nugetPkgConfig = new PackageReferenceFile(pkgRefpath);
IEnumerable<PackageReference> allPackages = nugetPkgConfig.GetPackageReferences();
var newtonsoftPkg = (
from pkg in allPackages
where pkg.Id == "Newtonsoft.Json"
select pkg
).FirstOrDefault();
if (newtonsoftPkg== null) return;
var newtonsoftPkg_Version = newtonsoftPkg.Version;
ltrNewtonsoftVer.Text = newtonsoftPkg_Version.ToString();
}
This is a slightly different answer to the question, but this shows the solution that I ended up with for my needs after finding this Question/Answer and modifying what I learned to suit my own needs. I hope it can help someone else out.
I'm trying to allow a user to enter data into a textbox that will be added to the web.config file. I've added the relevent lines to the web.config file but when I make this class all goes wrong.
I keep getting the are you missing a using directive or assembly refenrence error whenever I try to run my app. I have looked at the other times this question has been asked and can't seem to figure out where I'm going wrong. The thing is that I am extremely new to Visual Studio and am just left blank at what could be the answer.
Below here is the class file that's generating the error. I hope I've included everything you need to assist me. Thank you.
using System.Collections.Generic;
using System.Linq;
using System.Configuration;
namespace WebConfigDemo
{
public class CompanyConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
public CompanyConfigCollection Companies
{
get
{
return (CompanyConfigCollection)this[""];
}
set
{
this[""] = value;
}
}
}
public class CompanyConfigElement : ConfigurationElement
{
[ConfigurationProperty("id", IsKey = true, IsRequired = true)]
public int Id
{
get
{
return (int)this["id"];
}
set
{
this["id"] = value;
}
}
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get
{
return this["name"].ToString();
}
set
{
this["name"] = value;
}
}
} '
public class CompanyConfigCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new CompanyConfigElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CompanyConfigElement)element).Id;
}
}
public class CompaniesConfig
{
private static readonly Dictionary<int, CompanyConfigElement>
Elements;
static CompaniesConfig()
{
Elements = new Dictionary<int, CompanyConfigElement>();
var section = (CompanyConfigSection)ConfigurationManager.GetSection ("companies");
foreach (CompanyConfigElement system in section.Companies)
Elements.Add(system.Id, system);
}
public static CompanyConfigElement GetCompany(int companyId)
{
return Elements[companyId];
}
public static List<CompanyConfigElement> Companies
{
get
{
return Elements.Values.ToList();
}
}
}
} '
Any help is appreciated
You probably don't have the System.Configuration dll added to the project references. It is not there by default, and you have to add it manually.
Right-click on the References and search for System.Configuration in the .net assemblies.
Check to see if it is in your references...
Right-click and select Add Reference...
Find System.Configuration in the list of .Net Assemblies, select it, and click Ok...
The assembly should now appear in your references...
.Net framework of the referencing dll should be same as the .Net framework version of the Project in which dll is referred
If you've tried the above solutions and haven't found the answer, make sure that the .NET versions of all projects are the same.
I ran into this problem when importing a .NET version 4.6.1 into a .NET version 4.6.2 project. Without any warnings from Visual Basic!
More Info: The type or namespace name could not be found
Your using statements appear to be correct.
Are you, perhaps, missing the assembly reference to System.configuration.dll?
Right click the "References" folder in your project and click on "Add Reference..."
This problem would be caused by your application missing a reference to an external dll that you are trying to use code from. Usually Visual Studio should give you an idea about which objects that it doesn't know what to do with so that should be a step in the right direction.
You need to look in the solution explorer and right click on project references and then go to add -> and look up the one you need. It's most likely the System.Configuration assembly as most people have pointed out here while should be under the Framework option in the references window. That should resolve your issue.
I have observed a quote ' in your 1st line and also at the end of your last line.
'using System.Collections.Generic;
Is this present in your original code or some formatting mistake?
I had the same problem earlier today. I could not figure out why the class file I was trying to reference was not being seen by the compiler. I had recently changed the namespace of the class file in question to a different but already existing namespace. (I also had using references to the class's new and previous namespaces where I was trying to instantiate it)
Where the compiler was telling me I was missing a reference when trying to instantiate the class, I right clicked and hit "generate class stub". Once Visual Studio generated a class stub for me, I coped and pasted the code from the old class file into this stub, saved the stub and when I tried to compile again it worked! No issues.
Might be a solution specific to my build, but its worth a try.
In some cases, when necessary using has been obviously added and studio can't see this namespace, studio restart can save the day.
I was getting warnings about different versions in .NET framework; I ignored them.
The project compiles fine making the change in the solution's properties.
I'm using Visual Studio Code and could not use instructions from above so I found another way to fix the problem with referencing to namespace from another file.
All what need to be done is to add include to your .csproj file e.g:
<ItemGroup>
<Compile Include="filename.cs" />
</ItemGroup>
Then you can use namespaces from filename.cs
The following technique worked for me:
1) Right click on the project Solution -> Click on Clean solution
2) Right click on the project Solution -> Click on Rebuild solution
I'm trying to update the label/publisher field using Taglib-sharp, but I can't see it anywhere in its Object Hierarchy using Object Browser.
I've searched through google and the documentation and it looks like it's a field that's not catered for.
Before I look for alternatives (can any one suggest any?) that can edit those field, I thought I'd have one last crack and ask within the StackOverflow community who is familiar with TagLib-sharp that had a more informed opinion?
Thanks in Advance,
Francis
Update : I've investigated other libraries such as mpg123 & UltraID3Lib but they seem to have the same limitations.
Well, Daniel Fuchs answer didn't work for me. But, it was a beginning.
The step by step to add a field in the TagLib-sharp code is:
Download Source
Open the File TagLib/Tag.cs and insert the following code (I inserted it below PerformersSort, line 250):
public virtual string Publisher
{
get { return ""; }
set { }
}
Open the File TagLib/Id3v2/Tag.cs and insert the following code (I inserted it below PerformersSort, line 1292):
public override string Publisher
{
get { return GetTextAsString(FrameType.TPUB); }
set { SetTextFrame(FrameType.TPUB, value); }
}
Open the File TagLib/Id3v2/FrameTypes.cs and insert the following code (I inserted it below TPOS, line 71):
public static readonly ReadOnlyByteVector TPUB = "TPUB";
Now comes the "Aha" thing. Open the File TagLib/CombinedTag.cs and insert the following code (I inserted it below PerformersSort, line 318):
public override string Publisher
{
get
{
foreach (Tag tag in tags)
{
if (tag == null)
continue;
string value = tag.Publisher;
if (value != null)
return value;
}
return null;
}
set
{
foreach (Tag tag in tags)
if (tag != null)
tag.Publisher = value;
}
}
Finally, compile the code.
IMPORTANT: I had problems compiling the code, as well. You must download the SharpZipLib dll (.NET 2.0) and include this dll in the taglib project. Also, I needed to install NUnit, which I made with Nuget. At last, I commented the GDK lib and all its errors inside the test code, since in production it won't be used.
Well TagLib# is not able to to read the publisher tag. Even the newest version (2.1.0.0) as of now won't be able to do that. As an alternative you can add this functionality yourself using the source code of TagLib#, which is freely available.
To do so, open the file TagLib/Id3v2/FrameTypes.cs and add the following line somewhere:
public static readonly ReadOnlyByteVector TPUB = "TPUB"; // Publisher field
And in the file TagLib/Id3v2/Tag.cs:
public string Publisher {
get {return GetTextAsString (FrameType.TPUB);}
set {SetTextFrame (FrameType.TPUB, value);}
}
You can then access the Publisher field using something like this
TagLib.File tf = TagLib.File.Create(...); // open file
tf.Tag.Publisher = "Label XY"; // write new Publisher
tf.Save(); // save tags
Please note, that this is an ugly hack but will work for MP3 files.
I'm not used to TagLib#, but I'm using TagLib in a Qt project, where I retrieve this information inspecting TagLib::File::properties.
Take a look at the documentation, it is just a string map with every property and values.
Hope TagLib# has this method.
Update 2019-12-30:
It looks like the main taglib project has included the publisher field, so you should just use the latest version instead. I've updated to the latest TagLib from my fork and can attest that it works as expected.
Tip : If you want to change the framework version that TagLib compiles to (at time of writing it defaults to 462 and .NET STD 2.0), you need to change the Directory.Build.Props file located in the Solutions folder.
<Project>
<PropertyGroup>
<ReleaseVersion>2.2.0.0-beta</ReleaseVersion>
<RepositoryUrl>https://github.com/mono/taglib-sharp</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<TaglibSharpTargetFramework>net472;netstandard2.0</TaglibSharpTargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
I've pasted my version above which shows that I've changed it to compile to .NET 4.7.2 instead.