1

Good day please am using t4 template to autogenerate viewmodel for all my models. I Started by creating a BaseViewModel from which all viewModels inherits, using roslyn to analyse and all classes that inherit from BaseViewModel and then reads all properties of the model which I'll then put into the respective viewmodels. But inherited properties of the Model was never present.

Here is what I've tried

public bool GetAllProperties(ClassDeclarationSyntax classSyntax)
{
    if(classSyntax == null)
    {
        return false;
    }

    foreach(PropertyDeclarationSyntax prp in classSyntax.Members.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration))
    {
        if(!declaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().Any(p => p.Identifier == prp.Identifier))
        {
            sb.AppendLine("          public " + prp.Type + " " + prp.Identifier);
            sb.AppendLine("          {");
            sb.AppendLine("               get => Model." + prp.Identifier + ";");
            sb.AppendLine("               set");
            sb.AppendLine("               {");
            //sb.AppendLine("                    Model." + prp.Identifier + " = value;");
            sb.AppendLine("                    SetProperty(ref Model." + prp.Identifier + ", value, () => this." + prp.Identifier + ");");
            //sb.AppendLine("                    RaisePropertyChanged(() => " + prp.Identifier + ");");
            sb.AppendLine("               }");
            sb.AppendLine("          }\n");
        }
    }

    return true;
}

I even try to get all base classes of the Model recursively but was unable to load properties or convert the BaseTypeSyntax by the ClassDeclarationSyntax.BaseList.Types to a ClassDeclarationSyntax.

public bool GetAllProperties(ClassDeclarationSyntax classSyntax)
{
    if(classSyntax == null)
    {
        return false;
    }

    if(classSyntax.BaseList.Types != null && classSyntax.BaseList.Types.Any())
    {
        foreach(var bt in classSyntax.BaseList.Types)
        {
            GetAllProperties((BaseTypeDeclarationSyntax)bt);
        }
    }

    foreach(PropertyDeclarationSyntax prp in classSyntax.Members.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration))
    {
        if(!declaration.DescendantNodes().OfType<PropertyDeclarationSyntax>().Any(p => p.Identifier == prp.Identifier))
        {
            sb.AppendLine("          public " + prp.Type + " " + prp.Identifier);
            sb.AppendLine("          {");
            sb.AppendLine("               get => Model." + prp.Identifier + ";");
            sb.AppendLine("               set");
            sb.AppendLine("               {");
            //sb.AppendLine("                    Model." + prp.Identifier + " = value;");
            sb.AppendLine("                    SetProperty(ref Model." + prp.Identifier + ", value, () => this." + prp.Identifier + ");");
            //sb.AppendLine("                    RaisePropertyChanged(() => " + prp.Identifier + ");");
            sb.AppendLine("               }");
            sb.AppendLine("          }\n");
        }
    }

    return true;
}

Please help me, am new to both t4 template and roslyn, am just 7 days into them. Thanks in advance.

Edit

After some hours of googling I was able to come up with something like this

IEnumerable<PropertyDeclarationSyntax> GetAllProperties(ClassDeclarationSyntax classSyntax)
{
   var result = new List<PropertyDeclarationSyntax>();

    if(classSyntax == null)
    {
        return result;
    }

    if(classSyntax.BaseList.Types != null && classSyntax.BaseList.Types.Any())
    {
        foreach(var bt in classSyntax.BaseList.Types)
        {
            result.AddRange(GetAllProperties2(bt));
        }
    }

    foreach(PropertyDeclarationSyntax prp in classSyntax.Members.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration))
    {
        result.Add(prp);
    }

    return result;
}

public IEnumerable<PropertyDeclarationSyntax> GetAllProperties2(BaseTypeSyntax typeSyntax)
{

    var result = new List<PropertyDeclarationSyntax>();

    if(typeSyntax == null)
    {
        return result;
    }

    if(GetBaseList(typeSyntax).Types != null)
    {
        foreach(var bt in GetBaseList(typeSyntax).Types)
        {
            result.AddRange(GetAllProperties2(bt));
        }
    }

    foreach(PropertyDeclarationSyntax prp in GetMemberList(typeSyntax).Where(m => m.Kind() == SymbolKind.Property))
    {
        result.Add(prp);
    }

    return result;
}

public BaseListSyntax GetBaseList(BaseTypeSyntax type)
{
    return SyntaxFactory.BaseList(SyntaxFactory.SingletonSeparatedList<BaseTypeSyntax>(SyntaxFactory.SimpleBaseType(type.Type)));
}

public PropertyDeclarationSyntax GetMemberList(BaseTypeSyntax type)
{
    return SyntaxFactory.PropertyDeclaration(type.Type, SyntaxFactory.Token(type.Kind()));
}

But the method GetMemberList only return a single PropertyDeclarationSyntax from a TypeSyntax whereas I want a List of all the PropertyDeclarationSyntax of a TypeSyntax

Gizmo Code
  • 21
  • 5
  • 2
    Could you please explain what are you exactly trying to do/get? If you want to get all properties of a Class you could use [Reflection](https://stackoverflow.com/a/737156/3563910) , but... are you willing to check if something would compile using Roslyn? – Gonzo345 Sep 16 '19 at 07:41
  • The `ClassDeclarationSyntax` represents the declaration of a class. I think what you're looking for is the semantic model that you can get from the compilation: [Compilation.GetSemanticModel(SyntaxTree, Boolean) Method](https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.compilation.getsemanticmodel) – Paulo Morgado Sep 16 '19 at 07:51
  • @Gonzo345 I am trying to autogenerate ViewModel given the Model. The Model will be gotten from the Generic BaseViewModel. So am doing this using T4 template with Roslyn API. – Gizmo Code Sep 16 '19 at 09:38
  • @Gonzo345 Is it possible to use Reflection with Roslyn ClassDeclarationSyntax. Because with Roslyn I was able to read all ".cs" files and have access to all Classes. Can I use reflection on ClassDeclarationSyntax? – Gizmo Code Sep 16 '19 at 09:40
  • @PauloMorgado I don't think so. All what am doing or trying to do is I want to TModel from all classes that inherit from BaseViewModel and then get all public properties (including inherited properties that TModel could have inherited from any base type) of TModel. I will use all this properties to generate a ViewModel with NotifyPropertyChanged event and its handler. Am doing this in a T4 template file and I've been able to get all the classes and the Models, – Gizmo Code Sep 16 '19 at 09:49
  • @PauloMorgado I've been able to autogenerate ViewModels from all the classes but I found out the ClassDeclarationSyntax.Members.Where(m => m.Kind() == SyntaxKind.PropertyDeclaration) only returns the properties that are only present in the current class and not including the inherited type. – Gizmo Code Sep 16 '19 at 09:49
  • Yes, @GizmoCode, because you're looking at the syntactic model when what you need is the semantic model. – Paulo Morgado Sep 16 '19 at 10:31
  • @PauloMorgado Ok good. Can you please tell me more or how can I use the SemanticModel? I only have the ClassDeclarationSyntax node. Thanks – Gizmo Code Sep 16 '19 at 11:06
  • Please is there a way I cast or convert from BaseTypeSyntax to ClassDeclarationSyntax? – Gizmo Code Sep 16 '19 at 12:26
  • Where are you getting that `ClassDeclarationSyntax` from? From a `Compilation`? – Paulo Morgado Sep 16 '19 at 13:24
  • @PauloMorgado I used the CSharpSyntaxTree.ParseText() to load all .cs file in the Solution directory. `var solutionPath = Host.ResolveAssemblyReference("$(SolutionDir)"); var files = Directory.GetFiles(solutionPath,"*.cs",SearchOption.AllDirectories); IEnumerable classDeclarationSyntaxes = files.Select(x => CSharpSyntaxTree.ParseText(File.ReadAllText(x), path:x)).Cast().SelectMany(c => c.GetRoot().DescendantNodes().OfType());` _thanks, I really appreciate your follow up_ – Gizmo Code Sep 18 '19 at 09:03
  • Here's an example on how to get a compilation: http://www.tugberkugurlu.com/archive/compiling-c-sharp-code-into-memory-and-executing-it-with-roslyn – Paulo Morgado Sep 18 '19 at 11:06
  • @PauloMorgado. A big thanks. am so grateful for the follow up and the great help. thanks – Gizmo Code Sep 19 '19 at 07:01

1 Answers1

2

Step 1: convert ClassDeclarationSyntax to TypeSymbol, so you work with sematic model, not syntactic.

For that you need a "Compilation" object. If you're in a source generator, you can get it from the context for example. For other situations - look it up.

var typeNodeSymbol = context.Compilation
    .GetSemanticModel(classDecl.SyntaxTree)
    .GetDeclaredSymbol(classDecl);

Step 2: Get all properties

var result = typeSymbol
    .GetMembers()
    .Where(s => s.Kind == SymbolKind.Property)
    .ToList();

Step 3: Get the base type and repeat the process

var baseType = typeSymbol.BaseType;
if (baseType != null)
    //repeat step 2

Step 4: come up with a recursive method (I'll leave that to you)

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149