TrackableEntities / EntityFrameworkCore.Scaffolding.Handlebars

Scaffold EF Core models using Handlebars templates.
MIT License
209 stars 53 forks source link

How to check table name in propertyTransformer #61

Closed AmilaDotDev closed 5 years ago

AmilaDotDev commented 5 years ago

Hi,

Thanks for this great library.

Taking the example you've given in the README, is there a way to detect the table it's running on?

services.AddHandlebarsTransformers( propertyTransformer: e => e.PropertyName == "Country" ? new EntityPropertyInfo("Country", e.PropertyName) : new EntityPropertyInfo(e.PropertyType, e.PropertyName));

The problem I'm having is I have a property named Type in multiple tables and I want to change the property type depending on which class/table it is on.

Is this possible using propertyTransformer or is there another way to achieve this?

Thanks

tonysneed commented 5 years ago

Your best bet I think is to create a class that extends HbsCSharpEntityTypeGenerator and then override the GenerateProperties method. This method accepts an IEntityType, which has all the info you need, including the name of the entity that corresponds to the db table name. Start by copying code from the base GenerateProperties method and modify it to take into account the entity name when setting the property name.

Let me know if this makes sense.

tonysneed commented 5 years ago

If you decide this approach works for you, you'll have a class like so:

public class MyCSharpEntityTypeGenerator : HbsCSharpEntityTypeGenerator
{
    public MyCSharpEntityTypeGenerator(IEntityTypeTemplateService entityTypeTemplateService, IEntityTypeTransformationService entityTypeTransformationService, ICSharpHelper cSharpHelper) : base(entityTypeTemplateService, entityTypeTransformationService, cSharpHelper)
    {
    }

    protected override void GenerateProperties(IEntityType entityType)
    {
        if (entityType == null) throw new ArgumentNullException(nameof(entityType));

        var properties = new List<Dictionary<string, object>>();

        foreach (var property in entityType.GetProperties().OrderBy(p => p.Scaffolding().ColumnOrdinal))
        {
            PropertyAnnotationsData = new List<Dictionary<string, object>>();

            if (UseDataAnnotations)
            {
                GeneratePropertyDataAnnotations(property);
            }

            // Custom logic here
            var propertyName = property.Name;
            if (entityType.Name.Equals("SpecialTable", StringComparison.InvariantCultureIgnoreCase)
                || property.Name.Equals("Type", StringComparison.InvariantCultureIgnoreCase))
            {
                propertyName = "SpecialType";
            }
            else if (entityType.Name.Equals("OtherSpecialTable", StringComparison.InvariantCultureIgnoreCase))
            {
                propertyName = "OtherType";
            }

            properties.Add(new Dictionary<string, object>
            {
                { "property-type", CSharpHelper.Reference(property.ClrType) },
                { "property-name", propertyName },
                { "property-annotations",  PropertyAnnotationsData }
            });
        }

        var transformedProperties = EntityTypeTransformationService.TransformProperties(properties);

        TemplateData.Add("properties", transformedProperties);
    }
}

Then you'll need to register the class with the DI system, by adding code to your ScaffoldingDesignTimeServices class, like so:

public class ScaffoldingDesignTimeServices : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        // Normal code to register handlebars templates (omitted here)

        // Add custom entity type generator
        services.AddSingleton<ICSharpEntityTypeGenerator, MyCSharpEntityTypeGenerator>();

Hope this helps!

AmilaDotDev commented 5 years ago

Thanks for the detailed response @tonysneed, I'll report back after trying this.

AmilaDotDev commented 5 years ago

That worked like a charm, had to take nullable into account when changing the type.

private string GetPropertyType(IEntityType entityType, IProperty property)
    {
        var propertyType = CSharpHelper.Reference(property.ClrType);
        var newPropertyType = propertyType;
        var propertyName = property.Name;

        switch (propertyName)
        {
            case "Type":
                {
                    switch (entityType.Name)
                    {
                        case "Address":
                            newPropertyType = nameof(AddressType);
                            break;
                        case "BaseAddress":
                            newPropertyType = nameof(BaseAddressType);
                            break;
                    }
                }
                break;
        }

        return property.IsNullable && !propertyType.Equals(newPropertyType) ? $"{newPropertyType}?" : newPropertyType;
    }

Then used it in

properties.Add(new Dictionary<string, object>
{
        { "property-type", GetPropertyType(entityType , property) },
        { "property-name", propertyName },
        { "property-annotations",  PropertyAnnotationsData }
 });

Thanks @tonysneed

tonysneed commented 5 years ago

Glad it worked!