NRefactory: Как получить доступ к неразрешенным именованным аргументам в атрибуте свойства?
Я заранее прошу прощения за длинное описание простого вопроса, но хочу убедиться, что люди правильно понимают, что я пытаюсь сделать.
Фон
Я пишу инструмент, который может читать в файле, сгенерированном SqlMetal, и создать класс, содержащий методы для простой вставки, обновления, удаления и выбора, которые затем могут быть открыты для веб-службы. Основным преимуществом здесь является то, что, если таблица изменяется, мне просто нужно повторно запустить инструмент, и код, связанный с базой данных, автоматически обновляется, и везде, где он используется, генерируются ошибки компиляции, что позволяет легко отследить, где нужно вносить изменения вручную. быть сделано. Например, если у меня есть таблица Customer, которая имеет следующие поля:
- CustomerId (PK, Identity)
- Имя
- Фамилия
Я хочу иметь возможность генерировать методы вставки и удаления следующим образом:
// I only want non-database-generated fields to be parameters here.
public static Customer InsertCustomer(String firstName, String lastName)
{
...
}
// I only want the primary key fields to be parameters here.
public static int DeleteCustomer(int customerId)
{
...
}
Я использую SqlMetal для создания класса Customer. Теперь я хочу прочитать этот файл.cs в моем новом инструменте, чтобы создать другой класс с помощью описанных выше методов. Затем этот новый класс может быть предоставлен веб-службе для предоставления доступа к этой функции без необходимости выставления базовой базы данных. Я использую NRefactory для чтения в сгенерированном SqlMetal файле, и пока все идет хорошо, но я столкнулся с трудностью, пытаясь прочитать атрибуты свойства в моем классе Customer.
SqlMetal генерирует свои классы, используя атрибут ColumnAttribute для идентификации каждого свойства, полученного из столбца базы данных. Атрибут ColumnAttribute будет иметь ряд аргументов для описания свойств столбца базы данных. В приведенном выше примере он будет генерировать что-то вроде этого:
...
[Column(Name="customerId", Storage="_CustomerId, DbType="INT NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int CustomerId
{
...
}
[Column(Name="firstName", Storage="_FirstName", DbType="NVarChar(100) NOT NULL", CanBeNull=false)]
public String FirstName
{
...
}
[Column(Name="lastName", Storage="_LastName", DbType="NVarChar(100) NOT NULL", CanBeNull=false)]
public String LastName
{
...
}
...
проблема
Как видите, SqlMetal дает мне необходимые мне атрибуты, чтобы определить, какие столбцы генерируются базой данных, а какие являются частью первичного ключа. Поэтому, когда я прочитал этот файл в NRefactory и определил тип, я ожидал, что смогу получить всю эту информацию. Тем не менее, я обнаружил, что хотя я могу добраться до ColumnAttribute, все его аргументы не разрешены и, следовательно, не доступны через свойства NamedArguments или PositionalArguments.
Вот мой код:
SyntaxTree syntaxTree = ...;
foreach(AstNode tableNode in syntaxTree.Children)
{
ResolveResult result = resolver.Resolve(tableNode);
var properties = result.Type.GetProperties();
foreach (IProperty p in properties)
{
var attributes = p.Attributes;
bool isPrimaryKeyField = false;
bool isDbGenerated = false;
bool isColumn = false;
foreach (IAttribute attr in attributes)
{
if (attr.AttributeType.Name == "Column")
{
isColumn = true;
foreach (var arg in attr.NamedArguments) // NamedArguments contains no items.
{
if (arg.Key.Name == "IsPrimaryKey")
{
isPrimaryKeyField = (bool)arg.Value.ConstantValue == true;
}
if (arg.Key.Name == "IsDbGenerated")
{
isDbGenerated = (bool)arg.Value.ConstantValue == true;
}
}
}
}
if (isColumn)
{
... // Create a parameter as appropriate.
}
}
}
Это все работает до тех пор, пока я не попытаюсь пройти через IAttribute.NamedArguments, потому что коллекция не содержит элементов. Однако, когда я прохожу через отладчик и проверяю значение 'attr', я вижу, что есть закрытая переменная с именем 'unresolved', которая содержит список всех нужных мне аргументов, но я не могу найти способ получить доступ к этому через код.
Как мне получить содержимое этой "неразрешенной" переменной? Нужно ли мне делать что-то еще с Resolver? Я впервые использую NRefactory, так что я пока не слишком знаком со всеми нюансами. Мне было тяжело найти пример, который подходит к этому уровню глубины в Google, и документация, которую я видел для NRefactory, кажется, не покрывает это. Любая помощь будет оценена.
1 ответ
Я понял. Мне нужно было загрузить сборку для System.Data.Linq в IProjectContent перед разрешением SyntaxTree.
...
CecilLoader loader = new CecilLoader();
Assembly[] assembliesToLoad = {
...
typeof(System.Data.Linq.Mapping.ColumnAttribute).Assembly
...};
IUnresolvedAssembly[] projectAssemblies = new IUnresolvedAssembly[assembliesToLoad.Length];
for(int i = 0; i < assembliesToLoad.Length; i++)
{
projectAssemblies[i] = loader.LoadAssemblyFile(assembliesToLoad[i].Location);
}
IProjectContent project = new CSharpProjectContent();
project = project.AddAssemblyReferences(projectAssemblies);
...