NRefactory: How do I access unresolved Named Argum

2019-07-15 06:58发布

问题:

I apologize in advance for the long description of a simple question but I want to make sure people properly understand what I'm trying to do.

Background

I'm writing a tool that can read in a file generated by SqlMetal and create a class that contains methods for simple Inserting, Updating, Deleting and Selecting, which can then be exposed to a web service. The main advantage here is that if a table changes, I simply have to re-run the tool and the database-related code is automatically updated and everywhere that uses it will generate compile errors, making it easy to track down where manual changes need to be made. For example, if I have a Customer table that has the following fields:

  • CustomerId (PK, Identity)
  • FirstName
  • LastName

I want to be able to generate Insert and Delete methods as follows:

// 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)
{
...
}

I am using SqlMetal to generate a Customer class. Now what I want to do is read that .cs file into my new tool in order to create another class with the above methods. This new class can then be exposed to the web service to grant access to this functionality without having to expose the underlying database. I am using NRefactory to read in the SqlMetal-generated file and so far, it's going well but I've run into a snag trying to read the property attributes on my Customer class.

SqlMetal generates its classes using a ColumnAttribute to identify each property that is derived from a database column. The ColumnAttribute will have a number of arguments to describe the database column's properties. In the above example, it would generate something like this:

...
[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
{
...
}
...

Problem

As you can see, SqlMetal gives me the attributes I need in order to identify which columns are database-generated and which ones are part of the primary key. So when I read this file into NRefactory and resolve the type, I would expect to be able to get at all of this information. However, I'm finding that while I can get to the ColumnAttribute, all of the arguments on it are unresolved and therefore aren't accessible via the NamedArguments or PositionalArguments properties.

Here's my code:

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.
        }
    }
}

This all works until I try to loop through the IAttribute.NamedArguments because the collection contains no elements. However, when I go through the debugger and examine the value of 'attr', I can see that there is a private variable called 'unresolved', which contains a list of all the arguments I want but I can find no way to access this through code.

How do I get at the contents of this 'unresolved' variable? Do I need to do something more with the Resolver? This is my first time using NRefactory so I'm not overly familiar with all the nuances yet. I've been having a tough time finding an example that goes into this level of depth on Google and the documentation I've seen for NRefactory doesn't seem to cover it. Any help would be appreciated.

回答1:

I figured it out. I needed to load the assembly for System.Data.Linq into the IProjectContent before resolving the 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);
...


标签: c# nrefactory