Проверка и генерация кода во время сборки на основе файлов кода в разных проектах
Я ищу метод, который позволяет мне проверять код и генератор кода как часть процесса сборки, используя Visual Studio 2010 (не экспресс) и MSBuild.
Фоновая проверка:
Я пишу RESTful веб-сервис с использованием веб-API WCF. Внутри класса сервиса, который представляет веб-сервис, я должен определить конечную точку, объявив дополнительные параметры как простой тест. Когда имя параметра в объявлении конечной точки отличается от параметра метода C#, я получаю ошибку - к сожалению, во время выполнения при доступе к веб-службе, а не во время компиляции. Поэтому я подумал, что было бы неплохо проанализировать класс веб-сервиса как часть шага компиляции для таких недостатков, как возвращение ошибки, когда что-то не так.
Пример:
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public string MyMethod(string param1, string parameter2) {
// Accessing the web service now will result in an error,
// as there's no fitting method-parameter named "param2".
}
Также я хотел бы применить некоторые правила именования, такие как GET-методы должны начинаться со слова "Get". Я считаю, что это поможет службе оставаться более удобной для обслуживания при работе с несколькими коллегами.
Фоновое поколение:
Я буду использовать этот веб-сервис REST в нескольких других проектах, там мне нужно написать клиента для доступа к этому сервису. Но я не хочу писать клиент для каждого из них, всегда корректируя каждый раз, когда сервис меняется. Я бы хотел, чтобы клиенты генерировались автоматически на основе файлов кода веб-службы.
Предыдущий подход:
До сих пор я пытался использовать шаблон T4 с использованием интерфейса DTE, чтобы проанализировать файл кода и проверить его, или сгенерировать клиент. Это отлично работало в Visual Studio при сохранении вручную, но интеграция в процесс сборки оказалась неэффективной, так как хост Visual Studio недоступен при использовании MSBuild.
Любое предложение приветствуется. :)
4 ответа
Вместо использования DTE или каких-либо других средств для анализа кода C# вы можете использовать отражение (с контекстом только для отражения) для проверки сборки после ее компиляции. Использование отражения является более надежным решением и, вероятно, также более быстрым (особенно если вы используете Mono.Cecil для отражения).
Для интеграции с MSBuild я бы порекомендовал написать пользовательское задание MSBuild - оно довольно простое и более надежное / элегантное, чем написание утилиты командной строки, выполняемой MSBuild.
Это может быть длинный выстрел, но все еще квалифицируется как "любое предложение":)
Вы можете скомпилировать код, а затем выполнить команду после сборки, которая будет инструментом, который вам нужно написать, который использует отражение для сравнения проанализированного текста UriTemplate с именами параметров метода, перехвата ошибок и вывода их способом, который MSBuild будет пикап. Посмотрите эту ссылку для получения информации о том, как выводить данные, чтобы MSBuild поместил ошибки в список ошибок Visual Studio. Средство после сборки может затем удалить скомпилированные сборки, если найдены ошибки, таким образом "имитируя" неудачную сборку.
Вот ссылка SO, которая привела меня в блог MSBuild, просто для справки.
НТН
С точки зрения правоприменения, пользовательские правила FxCop, вероятно, очень хорошо подойдут.
Для генерации клиентского кода существует довольно много возможностей. Если вам нравится подход T4, возможно, есть способ заставить его работать с MSBuild (но вам определенно потребуется предоставить немного больше информации о том, что сейчас не работает). В любом случае, если вам нужна альтернатива, инструмент для пост-сборки на основе рефлексии - это еще один способ...
Вот короткая, крайне уродливая программа, которую вы можете запустить над сборкой или группой сборок (просто передавая dll в качестве аргументов) для выполнения проверки WebGet UriTemplate. Если вы ничего не проходите, он запускается сам по себе (и, соответственно, дает сбой, поскольку это его собственный модульный тест).
Программа выведет на вывод название методов, в которых отсутствуют параметры, и имена отсутствующих параметров, и, если они найдены, вернет ненулевой код возврата (стандартный для программы, которая не работает), что делает его подходящим как событие после постройки. Я не несу ответственности, если ваши глаза кровоточат
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;
namespace ConsoleApplication1
{
class Program
{
static int Main(string[] args)
{
var failList = new ConcurrentDictionary<MethodInfo, ISet<String>>();
var assembliesToRunOn = (args.Length == 0 ? new[] {Assembly.GetExecutingAssembly()} : args.Select(Assembly.LoadFrom)).ToList();
assembliesToRunOn.AsParallel().ForAll(
a => Array.ForEach(a.GetTypes(), t => Array.ForEach(t.GetMethods(BindingFlags.Public | BindingFlags.Instance),
mi =>
{
var miParams = mi.GetParameters();
var attribs = mi.GetCustomAttributes(typeof (WebGetAttribute), true);
if (attribs.Length <= 0) return;
var wga = (WebGetAttribute)attribs[0];
wga.UriTemplate
.Split('/')
.ToList()
.ForEach(tp =>
{
if (tp.StartsWith("{") && tp.EndsWith("}"))
{
var tpName = tp.Substring(1, tp.Length - 2);
if (!miParams.Any(pi => pi.Name == tpName))
{
failList.AddOrUpdate(mi, new HashSet<string> {tpName}, (miv, l) =>
{
l.Add(tpName);
return l;
});
}
}
});
})));
if (failList.Count == 0) return 0;
failList.ToList().ForEach(kvp => Console.Out.WriteLine("Method " + kvp.Key + " in type " + kvp.Key.DeclaringType + " is missing the following expected parameters: " + String.Join(", ", kvp.Value.ToArray())));
return failList.Count;
}
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillPass(String param1, String param2) { }
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillFail() { }
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillFail2(String param1) { }
}
}