Armen Tsirunyan
Armen Tsirunyan

Reputation: 132984

Read a file compile-time in C#

I am writing some unit tests in which I need a fake xml file. I can create that file and require it to be deployed with the unit tests, but experience shows that at my office it's a lot of headache. So I decided that the file will be created by UT's.

StreamWriter sw = new StreamWriter(testFileName);
sw.Write(contents);
sw.Close();

Now the problem is the contents string. It is virtually a long xml, something like this:

string contents = 
@"<?xml version="1.0" encoding="windows-1251"?>
  <blah>
  ~100 lines here
  </blah> ";

I don't want this to be in the same file as the rest of the code. I want the string to be generated compile-time from a file.

In C++, I'd do this

string contents = "
#include "test.xml"
   ";

Is it possible somehow in C#?

Upvotes: 6

Views: 4463

Answers (7)

polferov
polferov

Reputation: 51

I am currently writing a Blazor WASM app and I need to load GraphQL queries from files. T4 templates do not feel appropriate to solve this issue, and neither do the other options.

I created a simple NuGet packege that leverages source generation to read files at compile time and return the contents from a method.

Take a look on Github: https://github.com/polferov/Polferov.StringFromFile

Disclaimer: The development stage is "it works for me" and will likely stay that way.

Upvotes: 0

Frank Bryce
Frank Bryce

Reputation: 8446

You can do this with a T4 template. What you need is 3 things

  1. Create your template file, which will generate your .cs code
  2. Include your text file (or *.sql file) in the same project as your template file
  3. Configure your template to re-create your t4 template on every build

Something like this for your template.tt file

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Windows.Forms" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#
   string TextFromFile = File.ReadAllText(Host.ResolvePath("my_file_next_to_my_template.txt"));
#>
// This is the output code from your template
namespace MyNameSpace
{
    class MyGeneratedClass
    {
        static void main (string[] args)
        {
            System.Console.WriteLine("<#= TextFromFile #>");
        }
    }
}

If you don't want to configure your template to be re-created on the build then just opening it and saving it will do the trick.

Upvotes: 1

48klocs
48klocs

Reputation: 6103

You can go the resource file route but, depending on how many resources/how large they are (and XML can be large), you may eventually run into compilation problems (I did).

I've since transitioned from resources to singletons. Reading a text file in with File.ReadAllText is pretty dead simple and it's still IntelliSense-friendly.

Upvotes: 0

Matt Kellogg
Matt Kellogg

Reputation: 1252

If the contents are static, why can't you commit it to source control? Add it to your project and then it will always be exactly where you expect it to be.

Upvotes: 0

Igor Brejc
Igor Brejc

Reputation: 19004

I usually add such test files directly into the unit test C# project and set them as "Content" and "Copy if newer" to be copied to the bin directory during the compilation.

Upvotes: 0

Jeff
Jeff

Reputation: 36563

Use a resources (resx file) and add your xml file. Then you can access using MyNs.MyResources.MyXmlFile.

Upvotes: 1

Daniel A. White
Daniel A. White

Reputation: 190907

Why don't you include it in a resource?

Upvotes: 9

Related Questions