Drizin / CodegenCS

C# Toolkit for Code Generation (T4 alternative!)
MIT License
223 stars 30 forks source link

Unable to make my Template with Custom Model work ... Please Help! #26

Closed vnetonline closed 2 months ago

vnetonline commented 2 months ago

I have made a class following your examples as follows.

using CodegenCS;
using CodegenCS.Models;
using System;
using System.Linq;

namespace Amazing.Module.CodeGen.Templates
{
    public class MyModel : IJsonInputModel
    {
        public string[] Tables { get; set; }
    }

    public class OqtaneModelTemplate : ICodegenStringTemplate<MyModel>
    {
        public FormattableString Render(MyModel schema) => $$"""
        namespace MyNamespace
        {
            {{schema.Tables.Select(t => RenderTable(t))}}
        }
        """;

        FormattableString RenderTable(string table) => $$"""
        /// <summary>
        /// POCO for Users
        /// </summary>
        public class {{table}}
        {

        }
        """;
    }
}
        public async Task<FormattableString> GenerateOqtaneModel()
        {

            MyModel model = new MyModel();
            model.Tables = new string[] { "Patient", "Doctor" };
            var entities = (await _entityRepository.GetEntitiesAsync()).ToList();
            //foreach (var entity in entities)
            //{
            //    model.Tables.Append(entity.Name);
            //}

            OqtaneModelTemplate oqtaneModelTemplate = new OqtaneModelTemplate();
            return oqtaneModelTemplate.Render(model);

        }
  namespace MyNamespace
  {{
      {0}
  }}

Why doesn't this work?

Drizin commented 2 months ago

With that code you're just calling your own Render() which just returns a Formattable string.

All the magic happens inside CodegenTextWriter, so you have to create an instance of that and use it to write whatever you want (FormattableString/Func/Action, etc.) CodegenTextWriter goes recursively through your objects and will "dissect" the FormattableString parts (or many other types).

There are different ways of passing the model - check the Unit Tests that get delegates: https://github.com/CodegenCS/CodegenCS/blob/master/src/Core/CodegenCS.Tests/CoreTests/02-DelegateTests.cs

PS: There is no support yet for async (Tasks) callbacks, the main method where you're calling async tasks can't be evaluated by the writer - it can only understand Actions or Funcs

vnetonline commented 2 months ago

Can you please tell me when I create an instance of ICodegenTextWriter

vnetonline commented 2 months ago

I think I got it my code now looks like this

`` public async Task GenerateOqtaneModel() {

        DatabaseSchema databaseSchema = new DatabaseSchema();
        databaseSchema.Tables = new List<Table>();

        var entities = (await _entityRepository.GetEntitiesAsync()).ToList();
        foreach (var entity in entities)
        {
            Table table = new Table();
            table.TableName = entity.Name;
            databaseSchema.Tables.Add(table);
        }

        ICodegenTextWriter writer = new CodegenTextWriter();
        writer.RemoveWhitespaceFromEmptyLines = false;
        writer.LoadTemplate<OqtaneModelTemplate>().Render(databaseSchema);
        return writer.GetContents();

    }

``

image

Drizin commented 2 months ago

Since you have a single writer: writer.GetContents() returns the string, just save it to file - System.IO.File.WriteAllText(path, contents). If you had a ICodegenContext (holds multiple writers where each one has a filename) then you would use context.SaveToFolder(targetFolder)

vnetonline commented 2 months ago

Is my approach above right ??

Also how can I iterate the files in context and put them in different folders ?

Drizin commented 2 months ago

There are dozens of different ways. Using the LoadTemplate() extension is a nice way because Render will expect the same type that your model requires. If you are invoking other subtemplates from "inside" the interpolated string then you would have to use interpolation (there are also a few different ways of doing that, but in general you just embed the delegate).

But I don't think you have to map from your model to the DatabaseSchema model. DatabaseSchema is there as a generic model to extract and hold the schema of a MSSQL, but if you already have your own model you can probably just use it.

Drizin commented 2 months ago

Also how can I iterate the files in context and put them in different folders ?

https://github.com/CodegenCS/CodegenCS/blob/master/src/Core/CodegenCS.Tests/CoreTests/10-ContextTests.cs#L68-L70

var w1 = _ctx["Subfolder/File1.cs"];
var w2 = _ctx["Subfolder/File2.cs"];
//w1.Write(...)
...
_ctx.SaveToFolder(parentFolder);
vnetonline commented 2 months ago

Thank you for your help ... this toolkit is exactly what I have been looking for brilliant!!!