6

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?

  • 2
    if your object is a 1:1 mapping with your viewmodel, why are they different objects? They're tightly coupled anyways... – Telastyn Oct 5 '16 at 15:59
  • They're different, viewmodel contains a lot of special for view attributes, like validation and localization, it could contains some logic, the're different. – Yurii N. Oct 5 '16 at 16:10
  • 2
    And what's the problem with just making the viewmodel be the model? – whatsisname Oct 5 '16 at 16:14
  • 1
    The most straightforward and obvious way is to use simple string concatenation and/or templating. T4 is one such way. – Robert Harvey Oct 5 '16 at 17:10
  • @whatsisname the problem is to do it automatically, without a lot of routine work. – Yurii N. Oct 5 '16 at 21:07
7

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.

  • 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 5 '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 5 '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: t4-editor.tangible-engineering.com/blog/… – YSharp Oct 5 '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 6 '16 at 9: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) : ysharp.net/experiments/using-Microsoft-DSL-Tools/AppBuilder-SF – YSharp Oct 6 '16 at 16:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.