Search code examples
c#visual-studiotemplatest4auto-generate

Editing T4 template does not change generated code on Models


I needed to override or replace the default constructor of my models for a project using database first approach. After some research I found this answer to be the most appropiate to accomplish what I wanted: Override or replace default constructor when using database first approach.

So, not being an expert, I went to my ProjectModel.edmx and inside I found two files with extension .tt, they were called ProjectModel.Context.tt and ProjectModel.tt, so I figured I needed to edit the second one.

Trying to understand how that worked I found a piece of code that seems to generate the constructor of the class:

 1 <#
 2     var complexProperties = typeMapper.GetComplexProperties(complex);
 3     var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex);
 4 
 5     if (propertiesWithDefaultValues.Any() || complexProperties.Any())
 6     {
 7 #>
 8     public <#=code.Escape(complex)#>()
 9     {
10 <#
11         foreach (var edmProperty in propertiesWithDefaultValues)
12         {
13 #>
14         this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
15 <#
16         }
17 
18         foreach (var complexProperty in complexProperties)
19         {
20 #>
21         this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
22 <#
23         }
24 #>
25      Init();
26     }
27 
28     partial void Init();
29 
30 <#
31     }

I added lines 25 and 28, just to make the model be generated with the call to Init() and the method be declared after that.

Now, I went to ProjectModel.edmx which shows the diagram of my database, I right clicked over the diagram and ran Update Model from Database... in the menú. Then I went to the Refresh tab and highlighted Tables and clicked Finish. I was expecting that the new generated files would be similar to this:

namespace Project.Models.DB
{
    using System;
    using System.Collections.Generic;

    public partial class Class1
    {
        public Class1()
        {
            this.Events = new HashSet<Event>();
            Init();
        }

        partial void Init();

        public int id { get; set; }
        public string name { get; set; }
        public string slug { get; set; }
        public bool is_active { get; set; }
        public System.DateTime date_created { get; set; }
        public System.DateTime date_updated { get; set; }

        public virtual ICollection<Event> Events { get; set; }
    }
}

but it didn't work, I am wondering if I need to do something else or if I am editing the correct file. Any guidance would be very appreciated.


Solution

  • Your changes are in the complex type section of the template. Note:

     5     if (propertiesWithDefaultValues.Any() || complexProperties.Any())
     6     {
     7 #>
     8     public <#=code.Escape(complex)#>()
     9     {
    10 <#
    

    Look for the iteration of entities, then make your edits there:

    foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
    {
        fileManager.StartNewFile(entity.Name + ".cs");
        BeginNamespace(code);
    #>
    <#=codeStringGenerator.UsingDirectives(inHeader: false)#> // This may be slightly different based on version of EF, but you get the idea
    <#=codeStringGenerator.EntityClassOpening(entity)#>
    {
    <#
        var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
        var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
        var complexProperties = typeMapper.GetComplexProperties(entity);
    
        if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
        {
    #>
        public <#=code.Escape(entity)#>()
        {
    // ... much later
            foreach (var complexProperty in complexProperties)
            {
    #>
            this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
    <#
            }
    #>
        }
        Init();
    }
    partial void Init();