четверг, 13 декабря 2007 г.

Дизайн - это вам не…

Сегодня второй день в должности дизайнера.  Собственно, что я выяснил:

"Дизайн - это вам не программинг, тут головой думать надо!"

Пока все.

понедельник, 10 декабря 2007 г.

Миграция из C# в Java: Часть 3

Оригинал здесь.


До 2002 года я открыто отвергал большинство IDE. Они казались мне слишком настойчивыми. Они постоянно пытались контролировать мою систему сборки или формат кода. Если я хочу раскрыть какие-то вещи, мне нужно что-то взамен. Долгое время обмен не казался мне честным. THINK C на Macintosh был единственной IDE, которая действительно мне нравилась.

Visual Studio .NET 2002 стала первой IDE для Windows, расположившей меня в себе. Я до сих пор использую vi или emacs почти каждый день, но допускаю, что стал больше использовать Visual Studio.

В прошлом году я перешел на Visual Studio 2005 и полюбил ее. Это настолько великолепный продукт, что меня беспокоило, каков же будет следующий релиз. И вот вышла Visual Studio 2008, вероятно, вскоре я попробую ее. Но Visual Studio 2005 похожа на "Если оно работает, так и нечего беспокоиться". Последнее, что мне нужно, это чтобы она стала в чем-то хуже, потому как я совершенно не представляю, как она могла бы быть лучше.

Я догадываюсь, когда что-то уже появляется в IDE, просто у меня не очень хорошее воображение. :-)

Я начал использовать Eclipse несколько недель назад и теперь понимаю, где Visual Studio находит пространство для улучшений. Я думаю, Eclipse удивительна, и я лишь прошелся по поверхности.

Во всяком случае, вот пара избранных мной на этот раз фич Eclipse:

Постоянная сборка.

Когда я установил Eclipse, первое, что я сделал - поискал пункт меню для начала сборки приложения. Когда я не нашел ни одного, я предположил, что должно быть, система меню здесь в полном беспорядке и контринтуитивна. Как они допустили, что такую часто используемую команду так сложно найти?

1730_image001_2

Конечно, теперь я понимаю, что Eclipse не имеет такой команды, потому как сборка выполняется автоматически и постоянно. В действительности, такая возможность меня зацепила. Однажды это привело в замешательство, однако в остальном оно Просто Работает. Я делаю изменения в коде, сохраняю файл и панель Problems автоматически показывает существующие предупреждения и ошибки. Приятно.

В действительности, этим утром я потревожил Visual Studio 2005 чтобы исправить баг. После внесения изменений, я сохранил файл и уставился вниз экрана в ожидании запуска компиляции. Лишь несколько секунд спустя я догадался нажать Ctrl-Shift-B.

Быстрая правка.

Мне действительно по душе функция Quick Fix в Eclipse. В основном, всякий раз, получая ошибку компиляции или предупреждение, я ставлю текстовый курсор на "проблемное" место и жму Ctrl-1. Это вызывает инструмент Quick Fix, который отображает окно со списком путей решения данной проблемы. Например, если я пытаюсь вызвать несуществующий метод, мне предлагается создать его.

1730_image002_2

Студия предлагает сходные возможности (например, Generate Method Stub), но Quick Fix кажется более оригинальным. Мне почти всегда предлагается несколько вариантов разрешения ситуации, включая предпросмотр результата быстрой правки.

Это особенно удобно, если имеешь дело с исключениями. Я большей частью испытываю неприязнь к тому, что Java заставляет объявлять, какие исключения могут быть брошены из метода. Ctrl-1 дает мне также возможность с легкостью добавить объявления типов исключений или блоки try/catch.

В завершение.

Ясно, что я новичок в Eclipse, так что мои представления о ее возможностях могут быть неверными. И как я и сказал, я лишь начинаю. Во время написания данного поста я обнаружил "Generate Constructor Using Fields...". Жаль, что я нашел это немного раньше. :-)

В любом случае, вы можете свободно меня корректировать, дополнять или обвинять в невежестве или называть 7 причин, по которым я должен использовать IntelliJ.

Но главным образом я лишь говорю, что опыт с Eclipse дал мне намного большие перспективы по части IDE в общем. Сравнивая Visual Studio и Eclipse, я не могу назвать, который из них однозначно лучше. По большей части я впечатлен, что эти два соперника могут научиться так многому друг у друга.

воскресенье, 2 декабря 2007 г.

Миграция из C# в Java: Часть 2

Продолжим тему миграции. Перевод первой части пресдтавлен здесь, а ниже представлен перевод части 2. А в это время у Эрика уже имеется часть 4.


Как я уже упоминал в части 1, прошло уже прочти десять лет с тех пор, как я последний раз писал на Java. Очевидно, что этого достаточно, чтобы подавить некоторые наиболее неприятные воспоминания. Например, я совсем забыл, как работает следующий код:


String a = new String("foo");
String b = new String("foo");
if (a == b)
{       // этого никогда не случится
}



В C# используя оператор == вы получаете ожидаемое. В Java == значит лишь, что две строки представляют собой один обхъект, а не равенство самих строк.



Причина: Перегрузка Операторов



Более широко, возарвщение к Java заставляет меня понять, насколько я разбираюсь в перегрузке операторов в C#.



Я не говорю, что считаю перегрузку операторов действительно полезной для моих собственных классов. Главным образом, я считаю это лишь способом позволить программистам создавать плохой код. Я допущу использование перегрузки операторов в малых долях в Sawdust для упрощения манипуляций с 3D точками и векторами, но я мог-бы обойтись и без них.



Те мне менее, мне действительно нравится использование перегрузки операторов в классах .NET Framework:




  • Использование == для сравнения строк очень интуитивно.


  • Естественно выглядит использование < и > для сравнение объектов DateTime.


  • Доступ к элементам Dictionary по индексу с помощью [] кажется верным.



Ничего из этого не доступно в Java, и я теряю их всех.



Особенно это касается строк.



Однако это еще не так огорчает.



Ты Доолжен Подшутить Надо Мной.



Что действительно шокирует, так это то, что постратил неделю или около того на написание неверного кода и не получил никаких предупреждений от среды разработки. Я не понимал, что == не может быть использован для сравнения строк, пока не обратил внимание на некорректные результаты, выдаваемые приложением, и не начал раскапывать причины. Думаю, какое-нибудь предупреждение на этот случай было бы к месту.



И откровенно я был сильно удивился, когда не получил ни одного. Я считаю верно - такое ощущение, что Eclipse делает удивительную работу по части отслеживания ошибок и предупореждений.



1729_image001_2




  • Когда я получаю ошибку или предупреждение, Eclipse указывает мне на них несколькими способами совсем не прерывая мою работу. Вкладка Problems дает мне полный список. В редакторе я получаю желтые или красные пометки. На левой границе отображаются иконки для каждой строки с ошибкой. В Explorer файлы с ошибками также помечаются иконками.


  • Когда я набираю название нового класса, Eclipse проверяет имя на каждом новом символе и предупреждает, если оно совпадает с именем, используемым где-лиюбо еще.


  • Вчера после полудня я открыл окно моего офиса. Eclipse заметил это, воспользовался моим IP-адресом для определения моего местоположения, проверил погоду для Champaign, обнаружил в прогнозе дождь и предупредил меня, что следует не забыть закрыть окно перед уходом домой. :-)



Серьезно, Eclipse самая болтливая среда разработки что я использовал, и нет, я не жалуюсь. Я нахожу Eclipse чрезвычайно приятным. Он придупреждает иеня о многом, но эти предупреждения непосредственны, уместны и полезны.



И поэтому я немогу поверить, что никакая часть среды Eclipse не решилась предупредить меня о сравнении строк. Мои ожидания были очень высоки. Eclipse предупреждает меня так часто, что я даже заметил, что пытаюсь сидеть в правильной позе, лишь бы не обратить на это его внимание. Но по некоторым причинам, Eclipse молча принял использование == для сравнения строк, даже если такое использование в большинстве случаев некорректно.



В Итоге.



Думаю, я только здесь закончил свои жалобы.



Я сожадею, что этот пост в целом позитивен к C# и негативен к Java. Чтобы удержать баланс, в части 3 я остановлюсь на некоторых областях, где Eclipse выглядит лучше, чем Visual Studio. Way better.  :-)

четверг, 29 ноября 2007 г.

Миграция из C# в Java: Часть 1

Я и сам хотел написать несколько постов на тему перехода из .NET на Java, но, не дочитав до конца спецификацию Java, заявлять о чем-то не решался.
Вообще, эта тема мало рассмотрена. Об обратном переходе говорится везде, и книги тоже выходят. А здесь такие вот пробелы.

По теме нашел лишь следующее:

А тут вот Эрик Синк представил серию постов (пока 3 штуки) на эту тему. Ниже представлен перевод части первой.


Пару недель назад я начал еше один проект. Мои регулярные читатели, знают, что в основном я использую C#. Тем не менее, для этого проекта я решил воспользоваться Java и Eclipse.

Я не писал на Java уже несколько лет. Версия JDK в то время была 1.1.7. Средой разработки была Symantec Café, Supercede и Emacs. Понятие рефакторинг значило не более чем тема диссертации Билла Оупдайка (файл PostScript). Eclipse появилась несколькими годами позже.

Я думаю, настало время освежить свои взгляды.

Фигурные Скобки.

Моя младшая дочь спит. Время от времени она встает посреди ночи и начинает ходить во сне. Я найду ее внизу в семейной комнате бормочащей о животных в зоопарке. Я возвращаю ее назад в кровать. А с утра она уже ничего не помнит. Почему она делает это? Я не знаю, но предполагаю, что у любого будут проблемы со сном после двухчасового просмотра The Suite Life of Zach and Cody.

Или, возможно, это наследственное. Видимо, на прошлой неделе я встал посреди ночи, поехал к O'Hare, полетел в Сан-Хосе, взял такси в штаб-квартиру Sun, наплевал на их газон, поехал домой и вернулся в кровать.Я не помню ничего из этого.

Что я помню, так это то, на что я обратил внимания в Java - что фигурные скобки располагаются на предыдущей строке, и мое умственное здоровье стало стремительно ухудшаться.

Мне заметно полегчало, когда я открыл диалог настроек Eclipse и сделал следующее:  1728_image001_2

Ах да, и это тоже:

1728_image002_2

Эти изменения настроек возымели для меня огромное значение. По-видимому, естественный стиль в мире Java - располагать фигурные скобки на одной строке с предшествующей ей конструкцией. Но в любом случае я не смог бы к этому привыкнуть. я не говорю, что мой стиль объективно лучше. Я лишь говорю, что не могу справиться.

К счастью, Eclipse обладает невероятными функциами кастомизации. После вышеизложенных изменений я лишь нажал "Ctrl-Shift-F" и весь мой код стал намного привлекательней для меня.

Я надеался, что это работало и за пределами IDE. Я приобрел Head First Java и Java in a Nutshell. Обе книги были великолепны, но я продолжал обнаруживать свою левую руку жмущей "Ctrl-Shift-F" в надежде отворматировать книжную страницу.

Этим утром по пути на работу я заметил чью-то попытку свернуть с неверной полосы. Моя левая рука жала "Ctrl-Shift-F".

Раскладки Клавиш.

Замечательная кастомизируемость Eclipse пришла на выручку снова для раскладок клавиш. Разница между Visual Studio и Eclipse значительна.

Например, три наиболее часто используемые мной команды отладчика: Debug, Step Over, и Step Into.

  • Для Visual Studio клавиши по умолчанию: F5, F10 and F11. 
  • Для Eclipse это: F11, F6, and F5.

Правда в том, что я ищу раскладку Visual Studio и могу набрать указанное выше. Сознательно я не знаю, что они значат. Они лишь запрятаны где-то в моей бессознательной памяти. Это автоматизм. И когда я впервые запустил отладку в Eclipse, кое-что работало совсем не так хорошо.

В действительности, комбинации клавиш настолько различны по сравнению с Visual Studio, что я заподозрил их умышленное происхождение из недоброжелательных причин. Тем не менее, пока я не нашел сочетание клавиш, означающих "Go to Declaration" в одной среде и "Reformat my Hard Disk" в другой, я собирался отложить все подозрения и принять свою излишнюю параноидальность.

До сих пор я делаю знать, что значит "Ctrl-Shift-F" в Visual Studio?

В Итоге.

Обычно я не люблю делать излишнюю кастомизацию своей рабочей среды. В конце концоа, я пользователь Windows. Все мои измененные настройки теряются после переустановки системы. Легче научиться пользоваться вещами в из естественном состоянии.

Но если я собираюсь использовать Visual Studio и Eclipse одновременно, мне определенно нужно, чтобы один из них был немного сходен с другим.

вторник, 16 октября 2007 г.

Практика с XSLT в ASP.NET

Часто возникает необходимость в небольшом сайте, но ASP.NET оказывается слишком тяжеловесен, статичный HTML не устраивает совмещением данных и представления, а PHP или SSI просто брезгуешь.

В случае же использования XSLT кодирование займет ненамного больше времени, чем в случае с HTML. В этом посте я рассмотрю 2 варианта развертывания XSLT-сайта под ASP.NET.

Трансформацию на стороне клиента не рассматриваю, потому как на любителя это. Не в том смысле, что простые пути - не наш выбор, а в том, что выставлять данные напоказ... хмм... нет.

Вариант первый: собственный рендерер страницы

Пример:

/test1/default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="Default.aspx.cs" Inherits="_Default" %>
//
<root title="Page Title">
<data>
<item text="node 1 contents" />
<item text="node 2 contents" />
<item text="node 3 contents" />
</data>
</root> 

/test1/default.aspx.cs:

public partial class _Default : XslTransformPage 
{
public _Default() : base("test1.xsl") { }
}

Как видно, содержимое страницы представляет собой данные в формате XML. И страница эта наследуется от XslTransformPage, реализующего функционал XSL преобразования:

/app_code/xsltransformpage.cs:

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
//
public class XslTransformPage : Page
{
public XslTransformPage() { }
//
public XslTransformPage(string styleSheetFile)
{
this.styleSheetFile = styleSheetFile;
}
//
private string styleSheetFile;
public string StyleSheetFile
{
get { return styleSheetFile; }
set { styleSheetFile = value; }
}
//
protected override void Render(HtmlTextWriter writer)
{
if (Cache[Request.RawUrl] != null)
{
writer.Write(Cache[Request.RawUrl].ToString());
return;
}
//
string xslPath = Server.MapPath("~/App_Code/" + styleSheetFile);
if (!File.Exists(xslPath))
throw new HttpException(404, "Cannot find " + Request.RawUrl);
//
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xslPath);
//
StringBuilder inBuilder = new StringBuilder();
StringBuilder outBuilder = new StringBuilder();
//
using (StringWriter sw = new StringWriter(inBuilder))
using (HtmlTextWriter hw = new HtmlTextWriter(sw))
{
//Получает содержимое страницы, т.е. данные в xml
base.Render(hw);
//
using (StringReader sr = new StringReader(inBuilder.ToString()))
using (XmlReader xr = XmlReader.Create(sr))
//
using (XmlWriter xw = XmlWriter.Create(outBuilder))
transform.Transform(xr, xw);
}
//
Cache.Add(Request.RawUrl, outBuilder.ToString(),
new CacheDependency(new string[] { Request.PhysicalPath, xslPath }),
Cache.NoAbsoluteExpiration,
new TimeSpan(24, 0, 0),
CacheItemPriority.Normal, null);
//
writer.Write(outBuilder.ToString());
}
} 
Кроме преобразования здесь используется также кеширование результата преобразования. 

Вариант второй: HttpHandler на .aspx

Как, наверное, понятно, используется HttpHandler, навешиваемый на расширение .aspx, вот таким образом:

app.config:

<httpHandlers>  <add verb="*" path="*.aspx" type="XslTransformHandler"/>
</httpHandlers>

Теперь сам обработчик:

/app_code/xsltransformhandler.cs:

using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Xml;
using System.Xml.Xsl;
//
public class XslTransformHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string path = context.Request.Path.Substring(context.Request.ApplicationPath.Length);
//
ProcessRequest(context, path);
}
//
public void ProcessRequest(HttpContext context, string path)
{
context.Response.ContentType = "text/html";
//
if (context.Cache[path] == null)
{
string xslPath = String.Empty;
string xmlPath = String.Empty;
//
string xslFile = String.Empty;
string xmlFile = String.Empty;
string xmlDir = String.Empty;
//
string[] parts = path.Contains("/") ?
path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries) :
new string[] { path };
//
// Замечу, что файл стилей будет один для каждой папки, для корневой: 
//   default.xsl, ...
if (parts.Length == 1)
{
xslFile = "default.xsl";
xmlFile = Path.GetFileNameWithoutExtension(parts[0]) + ".xml";
xmlDir = "";
}
//   ... для вложенных - вида папка.папка...папка.xsl
else
{
xslFile = String.Join(".", parts, 0, parts.Length - 1) + ".xsl";
xmlFile = Path.GetFileNameWithoutExtension(parts[parts.Length - 1]) + ".xml";
xmlDir = String.Join("/", parts, 0, parts.Length - 1);
if (xmlDir.Length > 0)
xmlDir += "/";
}
//
xslPath = context.Server.MapPath("~/App_Code/" + xslFile);
xmlPath = context.Server.MapPath("~/App_Data/" + xmlDir + xmlFile);
//
if (!File.Exists(xmlPath) || !File.Exists(xslPath))
throw new HttpException(404, "Cannot find" + context.Request.RawUrl);
//
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(xslPath);
//
StringBuilder sb = new StringBuilder();
using (XmlReader xr = XmlReader.Create(xmlPath))
using (XmlWriter xw = XmlWriter.Create(sb))
transform.Transform(xr, xw);
//
context.Cache.Add(path, sb.ToString(),
new CacheDependency(new string[] { xmlPath, xslPath }),
Cache.NoAbsoluteExpiration,
new TimeSpan(12, 0, 0),
CacheItemPriority.Normal, null);
//
context.Response.Write(sb.ToString());
}
else
context.Response.Write(context.Cache[path].ToString());
}
//
public bool IsReusable
{
get { return false; }
}
} 

Пример:

Для запрашиваемого /test2/default.aspx данные будут распологаться в /App_Data/test2/default.xml, а стили в /App_Code/test2.xsl

Для запрашиваемого /test2/subdir/page.aspx: в /App_Data/test2/subdir/page.xml и /App_Code/test2.subdir.xsl соответственно.

А самого файла .aspx может и не существовать, либо в случае со страницами по умолчанию (например, default.aspx) он может присутствовать и быть пустым, чтобы IIS был в курсе о ее существовании.

Также, как и в случае со статичными данными, xml может быть получен и из БД (SELECT FOR XML) и вообще откуда угодно, и оба описанных метода можно приспособить в том числе и для этого.

Да, немногословно как-то получилось :)

среда, 27 июня 2007 г.

Задание версии и даты сборки при компиляции

Задание версии и даты сборки при компиляции

Недавно около часа гуглил как же запихнуть в AssemblyInfo ревизию SVN. Остановился было на сборнике MSBuild тасков, когда обнаружил замечательную утилиту SubWCRev из состава Tortoise SVN, которая вдобавок умеет вставлять еще и дату.

Используется это так:

    Создается шаблонный файл AssemblyInfo.template.cs следующего вида:
    AssemblyInfo.template.cs:


    [assembly: AssemblyDescription("Inductive UI Toolkit Test Application. Built at $WCDATE$.")]
    [assembly: AssemblyVersion("2.2.$WCREV$.*")]




    В файл проекта инклудится проектик, таргет BeforeBuild (описан в Microsoft.Common.targets) в котором будет выполняться понятно из его названия когда:


    MyProject.csproj:









    <Import Project="$(MSBuildProjectDirectory)\..\RevisionInfo.targets" />




    RevisionInfo.targets:









    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
    <SubWCRevDir>C:\Program Files\TortoiseSVN\bin</SubWCRevDir>
    </PropertyGroup>

    <!-- Sets AssemblyVersion value based on Subversion revision -->
    <Target Name="BeforeBuild" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <Exec Command='"$(SubWCRevDir)\SubWCRev" $(MSBuildProjectDirectory)
    $(MSBuildProjectDirectory)\Properties\AssemblyInfo.template.cs
    $(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs' IgnoreExitCode="true"/>
    </Target>
    </Project>




    Студия довольно благосклонно отнеслась к правке файла проекта и теперь при Release-билде проекта будет создаваться новый AssemblyInfo.cs с нужными данными.

среда, 23 мая 2007 г.

Почему тупит дизайнер

В IuiToolkit встала необходимость заиметь такое свойство, которому уже в дизайнере можно было бы присвоить следующие значения:

    Экземпляра любого класса соответствующего типа (при наличии конструктора без параметров, конечно же)
    Возвращаемого значения любого статического метода (также без параметров)
    .. статического свойства
    .. статического поля

+ Создавался еще custom CollectionEditor для хранения задач.

В процессе разработки всего этого пришлось познакомиться со следующими прелестями дизайнера:

Если из UITypeEditor'а возвращается ссылка на тот же объект, он не сериализуется

Это проявилось в дизайнере коллекции, т.к. коллекция, передаваемая в редактор, только меняла свое содержимое, объект оставался тот же самый и сравнив их по ссылкам, дизайнер не сериализовал "не изменившуюся" коллекцию.
Для решения нужно использовать методы ITypeDescriptorContext.OnComponentChanging и ITypeDescriptorContext.OnComponentChanged, следующим образом:


    public class TasksEditor : UITypeEditor
{
private IWindowsFormsEditorService editorService;

public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider, object value)
{
object newValue = value;

if (context != null &&
context.Instance != null &&
provider != null)
{
editorService = (IWindowsFormsEditorService)provider.
GetService(typeof(IWindowsFormsEditorService));

if (editorService != null)
{
TasksEditorDialog d = new TasksEditorDialog();
d.Tasks = (TasksGroupsCollection)value;

context.OnComponentChanging();
if (editorService.ShowDialog(d) == DialogResult.OK)
{
newValue = d.Tasks;
context.OnComponentChanged();
}
}
}

return newValue;
}

//...
}




В статьях MSDN, посвященных UITypeEditor упоминаний этого не нашел и попался. Благо, гугл затем направил на верный путь.




Асинхронные вызовы в UITypeEditor'е


В диалоге редактора асинхронно перебирались Reflection'ом сборки. И выполнялось это, естественно, асинхронно.





Поначало с помощью BackgroundWorker'а, который ввел себя несовсем предсказуемо. В какой-то момент времени по завершении DoWork диалог просто закрывался без каких либо пояснений. К сожалению отладка дизайнера в Express-студии ограничена, а MessageBox'ами выяснилось только то, что при этом не вызывается ни OnRunWorkerCompleted, ни даже FormClosing.





ОК, с BackgroundWorker'ом нам не по пути, реализовал асинхронность вручную с помощью делегатов и BeginInvoke. Результат из метода возвращался с помощью IAsyncResult. Здесь лучше не стало, в тот момент, когда метод завершал работу, студия просто валилась с предложением др.Ватсона отправить отчет.





С чувством, что меня здесь не ждали, реализовал следующее: выбросил IAsyncResult и организовал передачу в асинхронный метод делегата EndProcessingCallback, который вызывался в конце метода. И... заработало!)




Сложности отладки designtime-компонентов


Если единожды воспользоваться в дизайнере таким компонентом и изменить его состояние, то при следующем билде компонента, и открывании дизайнера того компонента, в котором используется наш designtime-компонент, студия может заявить, что не может привести тип X к типу X. Почему?


Как выяснилось, designtime-компонент закешировался в (LocalApplicationData)\Microsoft\(Visual Studio | VCSExpress | VBExpress)\8.0\ProjectAssemblies и будет там оставаться неизменным по перезапуска студии. И никакие Cleanup'ы и Rebuild'ы здесь не помогут.




MSB4018: The "GenerateResource" task failed unexpectedly.


Также, если сильно увлечься designtime'ом можно словить и такую ошибку. Это баг в MSBuild, лечится только переустановкой .NET Framweork'а. Связана эта ошибка, как понятно, с ресурсами, у меня таким образом MSBuild не нашел понимания в находящимися там System.Reflection.Runtime[Constructor|Field|Method|Property]Info, сериализующимися наследниками System.Reflection.[Constructor|Field|Method|Property]Info, запиханными туда дизайнером студии.


И не нужно спешить добавлять в файл проекта ноду GenerateResourceNeverLockTypeAssemblies, как предложено на форуме MSDN — я добавил было — не помогло, переставил фреймворк — та же фигня. И лишь после удаления этого ключа, MSBuild дал добро.