Rename classes using Visual Studio Project Template - c#

I have created a Visual Studio template using this link: http://msdn.microsoft.com/en-us/library/ms185301.aspx.
I am able to create a dialog where the user enters a custom message and it gets displayed:
namespace TemplateProject
{
class WriteMessage
{
static void Main(string[] args)
{
Console.WriteLine("$custommessage$");
}
}
}
What I want to do it allow the user to rename the class names so I want to do something like:
But you see I'm getting errors of "Unexpected character $"
How can I do this?
EDIT
I see from this link: http://msdn.microsoft.com/en-us/library/eehb4faa(v=vs.110).aspx that
To enable parameter substitution in templates:
In the .vstemplate file of the template, locate the ProjectItem element that corresponds to the item for which you want to enable parameter replacement.
Set the ReplaceParameters attribute of the ProjectItem element to true.
BUT above I have not yet generated the template yet as I am still defining the classes. I understnad that the above step needs to be done in order to get the parameter substitution enabled for a File-->New Project scenario.

It looks like you have your template file as a cs file, which is causing Visual Studio to attempt to build it directly.
From what I can tell you should create a functioning Project, export it, and then modify the resulting template to add any replacements you need.

Related

How can I use a file-scoped namespace declaration in a class template?

C# 10 introduced file-scoped namespaces, which I would like to use in Visual Studio's class templates. I've updated the 'Class' template file to the following:
namespace $rootnamespace$;
class $safeitemrootname$
{
//I put this comment here to make sure it's using the right file
}
But when I create a new empty class I get this autogenerated code:
namespace ProjectName
{
internal class Class1
{
//I put this comment here to make sure it's using the right file
}
}
What do I need to do to make the auto-generated code for an empty class look like this?
namespace ProjectName;
internal class Class1
{
}
For reference, I am using Visual Studio 2022 Professional and my project is using C#10 with .NET 6.
The location of the class template file that I am modifying is: C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\ItemTemplates\CSharp\Code\1033\Class\Class.cs
You have to set up your project's editorconfig to prefer File-scoped namespaces.
Right click your project. Select "Add" → "New Item"
Select "editorConfig File (.NET)"
Double click the new editorconfig file. In the "Code Style" tab set "Namespace declarations" to "File scoped"
The code template will now work as expected.
Check this thread: https://stackoverflow.com/a/69889803
They use a .editorconfig file where you can specify the namespace declaration style. When creating a new file in VS 2022 it will use that new style

How to output a file from a roslyn code analyzer?

I'm using the roslyn API to write a DiagnosticAnalyzer and CodeFix.
After I have collected all strings and string-interpolations, I want to write all of them to a file but I am not sure how to do this the best way.
Of course I can always simply do a File.WriteAllText(...) but I'd like to expose more control to the user.
I'm also not sure about how to best trigger the generation of this file, so my questions are:
I do not want to hard-code the filename, what would be the best way to expose this setting to the user of the code-analyzer? A config file? If so, how would I access that? ie: How do I know the directory?
If one string is missing from the file, I'd like to to suggest a code fix like "Project contains changed or new strings, regenerate string file". Is this the best way to do this? Or is it possible to add a button or something to visual studio?
I'm calling the devenv.com executable from the commandline to trigger builds, is there a way to force my code-fix to run either while building, or before/after? Or would I have to "manually" load the solution with roslyn and execute my codefix?
I've just completed a project on this. There are a few things that you will need to do / know.
You will probably need to switch you're portable class library to a class library. otherwise you will have trouble calling the File.WriteAllText()
You can see how to Convert a portable class library to a regular here
This will potentially not appropriately work for when trying to apply all changes to document/project/solution. When Calling from a document/project/solution, the changes are precalcuated and applied in a preview window. If you cancel, an undo action is triggered to undo all changes, if you write to a file during this time, and do not register an undo action you will not undo the changes to the file.
I've opened a bug with roslyn but you can handle instances by override the preview you can see how to do so here
And one more final thing you may need to know is how to access the Solution from the analyzer which, Currently there is a hack I've written to do so here
As Tamas said you can use additional files you can see how to do so here
You can use additional files, but I know on the version I'm using resource files, are not marked as additional files by default they are embeddedResources.
So, for my users to not have to manually mark the resource as additonalFiles I wrote a function to get out the Designer.cs files associated with resource files from the csproj file using xDoc you can use it as an example if you choose to parse the csproj file:
protected List<string> GetEmbeddedResourceResxDocumentPaths(Project project)
{
XDocument xmldoc = XDocument.Load(project.FilePath);
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";
var resxFiles = new List<string>();
foreach (var resource in xmldoc.Descendants(msbuild + "EmbeddedResource"))
{
string includePath = resource.Attribute("Include").Value;
var includeExtension = Path.GetExtension(includePath);
if (0 == string.Compare(includeExtension, RESX_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
{
var outputTag = resource.Elements(msbuild + LAST_GENERATED_TAG).FirstOrDefault();
if (null != outputTag)
{
resxFiles.Add(outputTag.Value);
}
}
}
return resxFiles;
}
For config files you can use the AdditionalFiles msbuild property, which is passed to the analyzers through the context. See here.

Get assembly name at compile time and use it in code [duplicate]

Is there a way to find out the assembly name at design-time (i.e. not using reflection or runtime APIs such as System.Reflection.Assembly.GetEntryAssembly) from within Visual Studio?
The scenario requires a tool to get the assembly name that a Visual Studio project will eventually compile into.
This is like parsing the AssemblyName property of the .csproj - I am wondering if there are any APIs that can give this information reliably.
Please do not respond back with runtime APIs that use reflection - there is no assembly file present at the time I need the assembly name - just the metadata of the assembly in the csproj file.
if you are calling the tool via a post/pre-build event, this data is very easy to access.
Just go to the "project properties->Build Events" tab, then select either "edit pre-build" or "edit post-build", depending on when you want the tool to run. This should bring up an edit window with the ever helpful "Macros >>" button. Press this and you will be given a heap of macros to use and should be pretty much everything you need.
The "API" you could use is LINQ to XML after all the .csproj file is just xml. (and you can get the location of the .csproj file if you need from the solution file which for some reason is not XML but can be easily parsed)
You can use "TargetName" available in Macros for Post-build events. It will give you the assembly name for your project.
After a quick run through MSDN I found this article which might be a good start for some further research:
Accessing Project Type Specific Project, Project Item, and Configuration Properties
I think you will need to write some regular expression that will give you the value of "AssemblyTitle" attribute in AssemblyInfo.cs file.
Something like this:
public class Assembly
{
public static string GetTitle (string fileFullName) {
var contents = File.ReadAllText (fileFullName); //may raise exception if file doesn't exist
//regex string is: AssemblyTitle\x20*\(\x20*"(?<Title>.*)"\x20*\)
//loading from settings because it is annoying to type it in editor
var reg = new Regex (Settings.Default.Expression);
var match = reg.Match (contents);
var titleGroup = match.Groups["Title"];
return (match.Success && titleGroup.Success) ? titleGroup.Value : String.Empty;
}
}

Data Driven MSTest: DataRow is always null

I am having a problem using Visual Studio data driven testing. I have tried to deconstruct this to the simplest example.
I am using Visual Studio 2012. I create a new unit test project.
I am referencing system data.
My code looks like this:
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[DeploymentItem(#"OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
[TestMethod]
public void TestMethod1()
{
try
{
Debug.WriteLine(TestContext.DataRow["ID"]);
}
catch (Exception ex)
{
Assert.Fail();
}
}
public TestContext TestContext { get; set; }
}
}
I have a very small csv file that I have set the Build Options to to 'Content' and 'Copy Always'. I have added a .testsettings file to the solution, and set enable deployment, and added the csv file.
I have tried this with and without |DataDirectory|, and with/without a full path specified (the same path that I get with Environment.CurrentDirectory). I've tried variations of "../" and "../../" just in case. Right now the csv is at the project root level, same as the .cs test code file.
I have tried variations with xml as well as csv.
TestContext is not null, but DataRow always is.
I have not gotten this to work despite a lot of fiddling with it. I'm not sure what I'm doing wrong.
Does mstest create a log anywhere that would tell me if it is failing to find the csv file, or what specific error might be causing DataRow to fail to populate?
I have tried the following csv files:
ID
1
2
3
4
and
ID, Whatever
1,0
2,1
3,2
4,3
So far, no dice.
I am using ReSharper, could it be interfering in some way?
Updated
I have it mostly working now! I am able to use XML, but when I use CSV my column, which is named ID comes back as ID
Not sure why. I've checked the actual file of course, and no weird characters are present.
For anyone having a similar problem, I turned off Just My Code and enabled Net Framework source stepping, etc. so that I could get more detailed debug information. This allowed me to determine that ReSharper was causing me problems. I disabled resharper and modified my attributes like this:
[DeploymentItem("UnitTestProject1\\OrderService.csv")]
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\bin\\Debug\\OrderService.csv", "OrderService#csv", DataAccessMethod.Sequential)]
And it worked (except as noted). I am still suspicious of the "bin\debug" in my path, but I'm just happy my DataRow is no longer null.
Thanks!
Any ideas?
I was struggling with a similar problem today when trying to make data-driven tests work with CSV input file. The name of the first column had some garbage at the beggining of it, i.e. ID instead of just ID.
It turned out it was an encoding issue. The CSV file was saved in UTF-8 which adds a byte order mark at the beginning, obviously confusing the parser. Once I saved the file in ANSI encoding, it worked as expected.
I know it's an old question, but this information might help someone else ending up on this page.
Have you tried adding it through the properties window?
Go to Test menu -> Windows -> Test View -> the tests will load up.
Click on the test to alter i.e. TestMethod1 and press F4 (properties).
Look for 'Data Source' and click the ellipses next to it
It will walk you through a wizard that sets up the attributes properly for the TestMethod
You have the deployment part set up properly, which is normally the big stumbling block.
You also don't have to set the build action to Copy Always as the deployment does this for you. This option is used if you include items like .xml files you use for configs, or icons/images as part of your project.
Update 1:
Also try this tutorial on MSDN.
Update 2:
Try this post, involving ProcMon
I see that you said you tried putting the CSV itself into the testsettings file, but have you tried just putting in the directory?
<Deployment>
<DeploymentItem filename="Test\Data\" />
</Deployment>
Then your DataSource line will look something like this:
[DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", "|DataDirectory|\\YOURCSV.csv", "YOURCSV#csv", DataAccessMethod.Sequential)]
If you do it this way, you don't need to specify the DeploymentItem line.
Our folder structure looks like this: Trunk\Test\Test\Data
We include: Test\Data in the deployment
We then access Test\Data via the |DataDirectory|\
All CSVs live within the \Data folder

Adding custom format to a template parameters Visual studio project

I have defined a Visual Studio template called classDB.cs. I would like the default name for the class to appear as [projectname]DB.cs, where [projectname] is the name of the current project (as entered in the Create Project dialog). Is there a way to achieve this? I tried setting the name of the class to $safeprojectname$DB.cs, but that didn't work.
UPDATE
I modified my project template but give's this error when it's generating the project
here's the template class
namespace $safeprojectname$.Models
{
public class $safeprojectname$DB : DbContext
{
}
}
I have been battling with a similar error to this for days, and I finally figured it out. Visual Studio escapes the $ in the .csproj file. So you will have a node that looks like this:
<Compile Include="Models\%24safeprojectname%24DB.cs" />
Open up the .csproj file in a text editor, and change it to:
<Compile Include="Models\$safeprojectname$DB.cs" />
And save the file. Your project will reload, but it won't try to escape the filename again! Export your template, and you should find that the parameter now gets replaced.
Try a template like this:
using System;
//...
namespace $rootnamespace$ {
class $safeitemname$DB {
}
}
Works for me.
Make sure you update the correct template (should be located under C:\Users\[user]\Documents\Visual Studio 2010\Templates\ItemTemplates on Windows 7) and restart Visual Studio.
EDIT
The above code is for an Item Template, but that shouldn't differ from a Project Template. According to MSDN, the $safeitemname$ and $safeprojectname$ parameters behaves the same:
safeitemname
The name provided by the user in the Add New Item dialog box, with all unsafe characters and spaces removed.
safeprojectname
The name provided by the user in the New Project dialog box, with all unsafe characters and spaces removed.

Categories