Preserving Attributes in Auto-Generated Classes

Introduction

The attribute-based customization of source objects in Code Effects is highly convenient. However, when your source classes are auto-generated by a framework (such as Entity Framework or LINQ to SQL), special care is needed to ensure that your custom attributes survive subsequent regenerations.

Challenge: Auto-Generated Classes

Consider an auto-generated table class, for example:

namespace Project
{
    public partial class Product
    {
        public int ProductID { get; set; }
        // … other auto-generated members …
    }
}

Because this class is regenerated (for example when the database schema changes), any manual modifications, such as adding custom attributes, can be lost during regeneration. If you want to treat Product as your source object for rules/filters and decorate its members with Code Effects attributes, you would otherwise need to reapply the attributes after each rebuild - which is tedious.

Solution: Use Interface + Partial Class Extension

You can avoid this manual overhead by employing a combination of interfaces and partial class extensions. The pattern is:

  • Declare an interface that defines the members you care about and decorate those members with Code Effects attributes.

      using CodeEffects.Rule.Common.Attributes;
    
      namespace Project
      {
      	public interface IProduct
      	{
      		[Field(AllowCalculations = false, Min = 0)]
      		int ProductID { get; set; }
    
      		[ExcludeFromEvaluation]
      		string ProductNumber { get; set; }
      	}
      }
    
  • Create a second partial class file for Product that implements the IProduct interface. This file lives outside the auto-generated region and is not overwritten. In this part you can decorate the class with SourceAttribute, expose additional members (such as in-rule methods or actions), and apply attributes to them:

      using CodeEffects.Rule.Common.Attributes;
    
      namespace Project
      {
      	[Source(DeclaredMembersOnly = true)]
      	[ExternalAction(typeof(Helper), "SomeAction")]
      	public partial class Product : IProduct
      	{
      		[Field(DisplayName = "Invoice", Max = 10)]
      		public string InvoiceNumber { get; set; }
    
      		[Method(DisplayName = "Now")]
      		[return: Return(DateTimeFormat = "MMMM dd, yyyy")]
      		public DateTime GetNow()
      		{
      			return DateTime.Now;
      		}
      	}
    
      	public class Helper
      	{
      		[Action("Some Action")]
      		public static void SomeAction()
      		{
      			// Action logic here
      		}
      	}
      }
    

    Why this works

    • Since the interface IProduct declares the properties and they carry the custom attributes, those attributes persist even though the underlying class may be regenerated.
    • The partial class lives in a distinct source file and is not overwritten by the generation process.
    • The SourceAttribute(DeclaredMembersOnly = true) instructs the Rule Editor to consider only the members declared on Product (not inherited ones), which supports controlled exposure.
    • New members (such as InvoiceNumber and GetNow()) that are not part of the auto-generated schema are added to the partial class file and will appear in the Rule Editor just like the other fields/methods.

    Important considerations

    • If you later add new database columns to the Product table and you want the Rule Editor to use those columns, you must also update the IProduct interface to declare matching members.
    • Keep your partial extension file in a project folder that is not overwritten by regeneration.
    • Avoid applying custom attributes directly in the auto-generated file because they may be lost during regeneration.

Summary

By using an interface decorated with Code Effects attributes, plus a partial class extension file for your auto-generated class, you ensure that:

  • custom attributes remain intact across regeneration,
  • new members (fields, methods, actions) can be added permanently,
  • the Rule Editor sees all intended members while being resilient to the regeneration process.

p101

p102

l097 --

l102

p101

×