{S0}简介
我看,代码生成是有时尚... ...但是,大多数开发人员忽略的东西。这就是所谓的MSBuild。什么的MSBuild与代码生成?事实上,你们中的大多数已经知道,当你添加x:Name属性XAML对象,你可以从代码隐藏访问它,它像魔术。显然,也没有神奇。如果你能做到这一点自己很容易吗?我很高兴知道你会想象,possibility.nbsp;
我的目标是向您展示如何用代码生成很酷的MSBuild是。为此,我将创建一个强类型的appSettings。
:
{S2}有没有神奇
我建议booknbsp; {A},如果你是在MSBuild感兴趣。这是一个在一个地方的技巧,窍门和引用的凉爽编译。这给了我很多的ideas.nbsp;
X:当您添加一个XAML元素的名称,您可以访问此Objet公司在后面的代码。而事实上,构建过程在您的项目(/ OBJ)中间目录生成一个*. g.cs,这些文件的动态链接您的intellisense。
事实上,你可以做的,自己在三线与MSBuild。只需创建一个。CS(MyMagicalClass.cs)文件,并把它在您的项目相同目录。然后,添加在您的。csproj文件结束(结束,而且是很重要的)。不包括的项目中的代码文件。我只希望你把它复制到项目文件的同一目录。<target name="BeforeBuild">
<ItemGroup>
<Compile Include="MyMagicalClass.cs" />
</ItemGroup>
</target>
现在,编译第一次,你就会有这个文件的IntelliSense,即使它不是在您的项目在解决方案资源管理器显示。
很快,我将解释如何这是可能的。当你打"; Buildquot在Visual Studio,Visual Studio会调用名为quot目标; Buildquot;您的项目文件。但构建目标有一定的构建目标执行之前,将执行的依赖。让我们来看看什么的依赖在C:\ WINDOWS \ Microsoft.NET \框架\ V3.5 \ Microsoft.Common.targets。{C}
它似乎BeforeBuild的基础上。<Target Name="BeforeBuild"/>
,这个目标是空的。
在现实中,当你添加一个目标BeforeBuild呼吁在项目结束时,你告诉MSBuild来覆盖任何以前定义的目标称为BeforeBuild(你甚至可以覆盖一个quot; Buildquot;是什么)。
我们在csproj之前写的代码,只是告诉MSBuild来quot;添加的compilationquot文件;每当你。CS项目文件中添加一个新的编译项目包括在您的。csproj,这就是为什么他们在Solution Explorer中所示。
然而,编译项目添加通过调用目标的动态解决方案资源管理器中不显示... ...然而,他们被编译和解释的IntelliSense。
所以现在,我将显示你一件很酷的事情:在app.config文件取决于编译生成一个类,在编译和集成类。让我们去...现在你的想法,让我们尝试创建的AppSettings类生成... ...
我们的目标是可以参考,一个强类型的方式,在App.config文件中的appSettings项目。
我不会进入我的课的实施讨厌的细节,有趣的一点仍然与MSBuild集成。在这段代码中的有趣的事情是WriteClass方法巫传递给构造配置文件的CodeDOM生成代码。public enum Language
{
CSharp,
VB
}
public class Generator
{
readonly string _ConfigFile;
public Generator(string configFile)
{
if(configFile == null)
throw new ArgumentNullException("configFile");
if(!File.Exists(configFile))
throw new FileNotFoundException(configFile, configFile);
_ConfigFile = configFile;
NameSpace = "";
ClassName = "Settings";
Language = Language.CSharp;
}
public String NameSpace
{
get;
set;
}
public String ClassName
{
get;
set;
}
public Language Language
{
get;
set;
}
CodeDomProvider CreateProvider()
{
switch(Language)
{
case Language.CSharp:
return new Microsoft.CSharp.CSharpCodeProvider();
case Language.VB:
return new Microsoft.VisualBasic.VBCodeProvider();
default:
throw new NotSupportedException();
}
}
public void WriteClass(TextWriter writer)
{
var config =
ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap()
{
ExeConfigFilename = _ConfigFile
}, ConfigurationUserLevel.None);
CodeNamespace ns = new CodeNamespace(NameSpace);
CodeTypeDeclaration type = new CodeTypeDeclaration(ClassName);
ns.Types.Add(type);
foreach(KeyValueConfigurationElement setting in config.AppSettings.Settings)
{
CodeMemberProperty property = new CodeMemberProperty();
property.HasSet = false;
property.HasGet = true;
property.Attributes = MemberAttributes.Static | MemberAttributes.Public;
property.Type = new CodeTypeReference(typeof(String));
property.Name = setting.Key;
var getSettingCall =
new CodeIndexerExpression(
new CodePropertyReferenceExpression(
new CodeTypeReferenceExpression(typeof(ConfigurationManager)),
"AppSettings"),
new CodePrimitiveExpression(setting.Key));
property.GetStatements.Add(new CodeMethodReturnStatement(getSettingCall));
type.Members.Add(property);
}
var provider = CreateProvider();
provider.GenerateCodeFromNamespace(ns, writer, new CodeGeneratorOptions());
}
}
例如,此配置文件:<configuration>
<appSettings>
<add key="val1" value="value"/>
<add key="val2" value="value2"/>
<add key="val3" value="value2"/>
<add key="hello" value="value2"/>
<add key="hello3" value="value2"/>
<add key="hello6" value="value2"/>
</appSettings>
</configuration>
会生成这个类:
... ...集成在构建过程中中public class Settings {
public static string val1 {
get {
return System.Configuration.ConfigurationManager.AppSettings["val1"];
}
}
public static string val2 {
get {
return System.Configuration.ConfigurationManager.AppSettings["val2"];
}
}
public static string val3 {
get {
return System.Configuration.ConfigurationManager.AppSettings["val3"];
}
}
public static string hello {
get {
return System.Configuration.ConfigurationManager.AppSettings["hello"];
}
}
public static string hello3 {
get {
return System.Configuration.ConfigurationManager.AppSettings["hello3"];
}
}
public static string hello6 {
get {
return System.Configuration.ConfigurationManager.AppSettings["hello6"];
}
}
}
首先,我只需要创建一个自定义MSBuild中任务和实施Execute方法。每个记录的错误会出现在Visual Studio错误窗口。 (建立在MSBuild任务,你需要参考Microsoft.Build .*集会。)
正如你可以看到... ...这是一个在我的发电机的包装:public class ClassGeneratorTask : Task
{
public ClassGeneratorTask()
{
Language = "CS";
}
[Required]
public ITaskItem FilePath
{
get;
set;
}
public String Language
{
get;
set;
}
[Required]
public ITaskItem ConfigFile
{
get;
set;
}
Language? GetLanguage()
{
if("CS".Equals(Language, StringComparison.InvariantCultureIgnoreCase))
return SettingsClassGenerator.Language.CSharp;
if("VB".Equals(Language, StringComparison.InvariantCultureIgnoreCase))
return SettingsClassGenerator.Language.VB;
return null;
}
public override bool Execute()
{
var language = GetLanguage();
if(language == null)
{
Error(Language + " is not a valid language, specify CS or VB");
return false;
}
try
{
string text = File.ReadAllText(ConfigFile.ItemSpec);
}
catch(IOException ex)
{
Error("Error when trying to open config file : " + " " +
ConfigFile.ItemSpec + " " + ex.Message);
return false;
}
try
{
using(var generatedWriter =
new StreamWriter(File.Open(FilePath.ItemSpec, FileMode.Create)))
{
Generator gen = new Generator(ConfigFile.ItemSpec);
gen.Language = language.Value;
gen.WriteClass(generatedWriter);
generatedWriter.Flush();
}
}
catch(IOException ex)
{
Error("Error when accessing to " + FilePath.ItemSpec +
" " + ex.Message);
return false;
}
return true;
}
public void Error(string message)
{
Log.LogError(message);
}
}
configfile中,文件路径,和语言时,我们通过调用任务的属性。 filepath是生成的文件的路径。你可以看到,configfile中和FILEPATH ITaskItem,和它的工作,如果它只是一个字符串。然而,ITaskItem更多的信息,如在MSBuild元数据。在现实中,我并不需要它,我只可以使用字符串代替,但我觉得这是一个很好的做法,使用ITaskItem。毕竟,它的接近我的类域(这是MSBuild的)。
现在,我们想要一个简单的方法在适当的时候执行这项任务的app.config文件。对于这一点,我只需要创建一个目标文件只是一个典型的MSBuild项目文件后,我们将进口csproj文件。
注意:SettingsGeneratorTask.dll是包含前面所示的MSBuild任务的大会。大会必须在相同的目录。目标文件。<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<UsingTask AssemblyFile="SettingsGeneratorTask.dll" TaskName="ClassGeneratorTask"/>
<ItemGroup>
<SettingsGenerated Include="$(IntermediateOutputPath)AppSettings.g.cs">
</SettingsGenerated>
</ItemGroup>
<Target Name="GenerateSetting"
Inputs="@(AppConfigWithTargetPath)"
Outputs="@(SettingsGenerated)">
<ClassGeneratorTask Language="$(Language)" ConfigFile="@(AppConfigWithTargetPath)"
FilePath="@(SettingsGenerated)"></ClassGeneratorTask>
<ItemGroup>
<Reference Include="System.Configuration"></Reference>
<Reference Include="System"></Reference>
<Compile Include="@(SettingsGenerated)"></Compile>
<FileWrites Include="@(SettingsGenerated)"/>
</ItemGroup>
</Target>
<PropertyGroup>
<ResolveReferencesDependsOn>
GenerateSetting;
$(ResolveReferencesDependsOn)
</ResolveReferencesDependsOn>
</PropertyGroup>
</Project>
正如你可以在这里看到:
<PropertyGroup>
<ResolveReferencesDependsOn>
GenerateSetting;
$(ResolveReferencesDependsOn)
</ResolveReferencesDependsOn>
</PropertyGroup>
我作为一个依赖ResolveReferences插入我的自定义任务。因为在调查Microsoft.Common.targets后,我已经看到了这一步,是唯一一个,我做到了这一点:在编译之前。之后,我在我的任务来定位app.config文件的项目AppConfigWithTargetPath决议。
现在这里:<ItemGroup>
<Reference Include="System.Configuration"></Reference>
<Reference Include="System"></Reference>
<Compile Include="@(SettingsGenerated)"></Compile>
<FileWrites Include="@(SettingsGenerated)"/>
</ItemGroup>
你可以看到,我的任务,我想补充系统和System.Configuration参考动态,因为生成的代码依赖于它。我不想打扰用户,如果他忘记这些引用添加到项目。
所以,我想补充的两个引用,生成的类编译项目和FileWrites项目。 FileWrites项目是用来到MSBuild的说,应删除该文件,当你清理项目。
另一个技巧:<Target Name="GenerateSetting" Inputs="@(AppConfigWithTargetPath)"
Outputs="@(SettingsGenerated)">
这意味着目标取决于了AppConfig文件,并创建@(SettingsGenerated)项目。事实上,在执行任务时,MSBuild会比较@(SettingsGenerated)和配置文件的时间戳。如果@(SettingsGenerated)修改后的配置文件,配置文件并没有改变,可以跳过自上次任务的平均,这样,在编译期间没有无用的开销。
那么,什么是左?只要包括在您csproj的目标文件后,Microsoft.Common.targets进口。<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\SettingsGeneratorTask\bin\Debug\SettingsClassGenerator.targets" />
创建与您最喜爱的设置你的app.config,并编译...你会得到你的配置文件的appSettings的IntelliSense!结论
这个项目是向您展示如何MSBuild和代码生成功能强大的... ...正如你可以看到,我所做的是非常简单的代码,但非常有用的。现在,你知道这一点,你会想象呢?