11

I have simple POCO classes, like this:

public class Book
{
    public string BookId { get; set; }
    public string Name { get; set; }
    //etc...
}

I want to get all those classes and generate another classes, which called viewmodels, for example, the output would be:

public class BookViewModel
{
    [HiddenInput(DisplayValue = false)]
    public string BookId { get; set; }

    [Required]
    public string Name { get; set; }
    //etc...
}

This is the simplest example, in real life I would also want to generate special classes, which implements database operations logic with corresponding entities.

However, how should I do this? I'm using ASP.NET Core right now, but if solution doesn't exist in Net Core, I'll just use it where it exists and copy-paste result.

So, I want generate C# classes from other classes, how should I do this?

Yurii N.
  • 341
  • 3
  • 12

1 Answers1

14

If that's all you have and it isn't practical / feasible for you to have a common single source of truth to generate your different artifacts, you may find the T4 template feature helpful.

For example, let's assume that "Dummy" is the class library project of your model in the same solution, with, say, in Dummy.cs:

namespace Dummy
{
    public class Something
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

    public class SomethingElse
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }
}

Then, in your view model class library, you can use Visual Studio's

"Add > New Item... > Visual C# Items > Text Template"

to add, in a "ViewModel.tt":

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="$(SolutionDir)\Dummy\bin\Debug\Dummy.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="Dummy" #>
<#
  // choose a convenient "anchor type" from your model, e.g., a base class or enum type, etc
  var model = typeof(Dummy.Something).Assembly.GetTypes();
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DummyViewModels
{
<# foreach (var type in model) { #>
    public class <#= type.Name #>ViewModel
    {
<# foreach (var property in type.GetProperties()) { #>
        public <#= property.PropertyType.Name #> <#= property.Name #> { get; set; }
<# } #>
    }
<# } #>
}

which should yield, after "right-click > Run Custom Tool" on the "ViewModel.tt" (in the same project), the following "ViewModel.cs":

namespace DummyViewModels
{
    public class SomethingViewModel
    {
            public Int32 Id { get; set; }
            public String Name { get; set; }
    }

    public class SomethingElseViewModel
    {
            public Int32 Id { get; set; }
            public String Name { get; set; }
    }
}

'Hope this helps.

YSharp
  • 888
  • 6
  • 10
  • 1
    Excellent, that's what I need! I don't really understand what happens when `var model` declared, we just get type info about `Something`? And what do you mean by _common single source of truth to generate your different artifacts_? – Yurii N. Oct 05 '16 at 23:33
  • 1
    This "model = typeof(Dummy.Something).Assembly.GetTypes()" is just the data source, so to speak, for your template code to start walking thru reflection over the types and members of your "Dummy" model assembly. The "assembly" and "import" directives are what makes it work. By "single source of truth" I was alluding to the more robust / complete approach where you have some sort of a single model in your solution, to generate all kinds of artifacts from it (e.g., from UML to HTML + JS + CSS + etc) -- in your case, you'd thus generate both the model and view model layers, not just the latter. – YSharp Oct 05 '16 at 23:44
  • 1
    For more on the underlying concepts / jargon, see for instance what the folks at Tangible have built around the T4 templates: http://t4-editor.tangible-engineering.com/blog/blog-series-model-driven-development-with-t4-templates.html – YSharp Oct 05 '16 at 23:48
  • Now it's all clear. Thanks! Single source of truth seems also is perfect for my purpose, where should I start digging through this? – Yurii N. Oct 06 '16 at 09:09
  • 1
    @Yuriy well, this is a pretty broad topic, and I didn't mean to distract you with it. Such an all encompassing (or mostly) model can come in different forms -- UML, your own Domain Specific Modeling Language, etc. It's one of the purposes of Visual Studio extensibility & the DSL tools (google "VSIX") used by MS & other vendors; for a sample attempt at creating one from scratch to generate C# + SQL + NHibernate mappings + app.config's, etc, I made this proof of concept back in the days; demo'ed here (45 mins long video) : http://www.ysharp.net/experiments/using-Microsoft-DSL-Tools/AppBuilder-SF – YSharp Oct 06 '16 at 16:37
  • Thank you for your answers, I'll try these recommendations! – Yurii N. Oct 06 '16 at 21:57