Skip to main content
Code weaving (also known as “Software Factory weaving”) allows Intent Architect to merge generated code with user-written code in the same file. This enables a “managed” workflow where developers can add custom logic without their changes being overwritten.

Overview

Intent Architect uses Roslyn Weaver for C# code files. The weaver:
  • Reads existing files with user code
  • Compares with newly generated code
  • Intelligently merges the two based on attributes
  • Preserves user code in designated sections

How It Works

1

Add Intent Attributes

Mark code sections with [IntentManaged(Mode)] attributes to control weaving behavior
2

Generate Code

Templates generate code with Intent management attributes
3

Weaving Process

During Software Factory execution, the weaver merges existing and generated code
4

User Code Preserved

Code marked with Mode.Ignore or in unmanaged sections is preserved

Intent Management Modes

The Mode enum controls how Intent manages different code sections:

Mode.Fully

[IntentManaged(Mode.Fully)]
public class MyClass
{
    // Intent completely manages this class
    // User changes will be overwritten on next generation
}

Mode.Fully

Intent fully manages this code. Any user changes are overwritten on next generation.Use for: Auto-generated DTOs, interfaces, boilerplate code

Mode.Merge

[IntentManaged(Mode.Merge)]
public class MyService
{
    // Intent manages the signature but preserves user code
    [IntentManaged(Mode.Ignore)]
    public void CustomMethod()
    {
        // User can add custom methods
        // This method will be preserved
    }

    [IntentManaged(Mode.Merge, Body = Mode.Ignore)]
    public void ProcessData(string input)
    {
        // Intent manages signature, user writes body
        // Custom implementation preserved
    }
}

Mode.Merge

Intent manages the structure/signature. Users can add new members that are preserved.Use for: Service implementations, repositories, business logic classes

Mode.Ignore

[IntentManaged(Mode.Ignore)]
public class CustomLogic
{
    // Intent never touches this class
    // Completely user-managed
}

Mode.Ignore

Intent completely ignores this code section. Fully user-managed.Use for: Custom business logic, user-written code, manual implementations

Granular Control

You can control different aspects of a code element separately:

Signature, Body, and Attributes

[IntentManaged(Mode.Merge, Signature = Mode.Fully, Body = Mode.Ignore)]
public async Task<Result> ProcessAsync(string input)
{
    // Intent manages the method signature (name, parameters, return type)
    // User writes the implementation
    var result = await _service.Process(input);
    return result;
}
Signature
Mode
default:"Mode from base attribute"
Controls method/property signature (name, parameters, return type, access modifiers)
Body
Mode
default:"Mode from base attribute"
Controls method implementation code
Attributes
Mode
default:"Mode from base attribute"
Controls attributes applied to the member
Comments
Mode
default:"Mode from base attribute"
Controls XML documentation comments

Common Patterns

// Intent manages signature, user writes implementation
[IntentManaged(Mode.Fully, Body = Mode.Ignore)]
public void Initialize()
{
    // Custom initialization logic
}

// Intent manages everything except attributes
[IntentManaged(Mode.Fully, Attributes = Mode.Ignore)]
[CustomAttribute] // User can add custom attributes
public class MyEntity { }

// Intent manages structure, user adds documentation
[IntentManaged(Mode.Merge, Comments = Mode.Ignore)]
/// <summary>
/// Custom documentation added by user
/// </summary>
public class MyClass { }

File-Level Configuration

Set default modes for entire files using assembly attributes:
using Intent.RoslynWeaver.Attributes;

// Set default for entire file
[assembly: DefaultIntentManaged(Mode.Fully)]

// Set defaults with granular control
[assembly: DefaultIntentManaged(Mode.Merge,
    Signature = Mode.Fully,
    Body = Mode.Ignore)]

namespace MyNamespace
{
    // Classes inherit the default mode
    public class MyClass
    {
        // Can still override on individual members
        [IntentManaged(Mode.Ignore)]
        public void CustomMethod() { }
    }
}

RoslynWeaverConfiguration API

Configure weaving behavior programmatically from templates:
RoslynWeaverConfiguration Usage
public class MyTemplate : CSharpTemplateBase<ClassModel>, ICSharpFileBuilderTemplate
{
    public MyTemplate(IOutputTarget outputTarget, ClassModel model)
        : base("Template.Id", outputTarget, model)
    {
        CSharpFile = new CSharpFile(this.GetNamespace(), this.GetFolderPath());

        // Configure Roslyn Weaver settings
        var weaverConfig = new RoslynWeaverConfiguration(CSharpFile.GetConfig());
        
        weaverConfig
            .WithDefaultMode(Mode.Merge)
            .WithDefaultBodyMode(Mode.Ignore)
            .WithDefaultSignatureMode(Mode.Fully);
    }

    public CSharpFile CSharpFile { get; }
}

Configuration Methods

WithDefaultMode

WithDefaultMode(Mode mode)
Set the default management mode for the file

WithDefaultBodyMode

WithDefaultBodyMode(Mode mode)
Set default mode for method/property bodies

WithDefaultSignatureMode

WithDefaultSignatureMode(Mode mode)
Set default mode for signatures

WithDefaultAttributesMode

WithDefaultAttributesMode(Mode mode)
Set default mode for attributes

WithDefaultCommentsMode

WithDefaultCommentsMode(Mode mode)
Set default mode for XML comments

Template Integration

Configure weaving in File Builder templates:
public class ServiceTemplate : CSharpTemplateBase<ServiceModel>, ICSharpFileBuilderTemplate
{
    private readonly CSharpFile _file;

    public ServiceTemplate(IOutputTarget outputTarget, ServiceModel model)
        : base("Service.Template", outputTarget, model)
    {
        _file = new CSharpFile(this.GetNamespace(), this.GetFolderPath())
            .IntentManagedMerge(); // Set file-level mode

        _file.AddClass(model.Name, @class =>
        {
            @class.AddConstructor(ctor =>
            {
                // Constructor fully managed by Intent
                foreach (var dependency in model.Dependencies)
                {
                    ctor.AddParameter(dependency.Name, dependency.Type);
                }
            });

            foreach (var operation in model.Operations)
            {
                @class.AddMethod(
                    GetTypeName(operation.TypeReference),
                    operation.Name,
                    method =>
                    {
                        method
                            .AddMetadata("intent-managed", "Mode.Fully, Body = Mode.Ignore");

                        // Intent manages signature
                        foreach (var param in operation.Parameters)
                        {
                            method.AddParameter(param.Name, GetTypeName(param.TypeReference));
                        }

                        // User writes body
                        method.AddStatement("throw new NotImplementedException();");
                    });
            }
        });
    }

    public CSharpFile CSharpFile => _file;
}

Tag Modes

Control how Intent identifies managed code sections:

TagMode.Explicit

[assembly: DefaultIntentTagMode(TagMode.Explicit)]

public class MyClass
{
    // MUST have IntentManaged attribute to be managed
    [IntentManaged(Mode.Fully)]
    public string ManagedProperty { get; set; }

    // No attribute = Intent ignores this
    public void CustomMethod()
    {
        // User code, never touched by Intent
    }
}

TagMode.Implicit (Default)

[assembly: DefaultIntentTagMode(TagMode.Implicit)]
[assembly: DefaultIntentManaged(Mode.Merge)]

public class MyClass
{
    // Inherits Mode.Merge from assembly attribute
    public string ManagedProperty { get; set; }

    // Must explicitly ignore
    [IntentManaged(Mode.Ignore)]
    public void CustomMethod()
    {
        // User code
    }
}

Common Scenarios

Repository Pattern

[assembly: DefaultIntentManaged(Mode.Merge)]

namespace MyApp.Repositories
{
    public interface IProductRepository
    {
        // Intent manages interface (adds new methods)
        Task<Product> GetByIdAsync(int id);
        Task SaveAsync(Product product);
    }

    public class ProductRepository : IProductRepository
    {
        // Constructor fully managed
        [IntentManaged(Mode.Fully)]
        public ProductRepository(DbContext context)
        {
            _context = context;
        }

        // Signature managed, implementation by user
        [IntentManaged(Mode.Fully, Body = Mode.Ignore)]
        public async Task<Product> GetByIdAsync(int id)
        {
            return await _context.Products
                .Include(p => p.Category)
                .FirstOrDefaultAsync(p => p.Id == id);
        }

        // User adds custom methods
        [IntentManaged(Mode.Ignore)]
        public async Task<List<Product>> GetPopularProductsAsync()
        {
            return await _context.Products
                .Where(p => p.Rating > 4.5)
                .ToListAsync();
        }
    }
}

Service Layer

[assembly: DefaultIntentManaged(Mode.Merge,
    Signature = Mode.Fully,
    Body = Mode.Ignore)]

namespace MyApp.Services
{
    public class OrderService
    {
        // Intent manages signature based on designer
        public async Task<OrderDto> CreateOrderAsync(
            CreateOrderCommand command)
        {
            // User writes business logic
            var order = new Order
            {
                CustomerId = command.CustomerId,
                Items = command.Items
            };

            await _validator.ValidateAsync(order);
            await _repository.SaveAsync(order);

            return MapToDto(order);
        }

        // User adds helper methods
        [IntentManaged(Mode.Ignore)]
        private OrderDto MapToDto(Order order)
        {
            // Custom mapping logic
        }
    }
}

Best Practices

Default to Merge

Use Mode.Merge as default for most application code to balance generation and customization

Fully Manage DTOs

Use Mode.Fully for data transfer objects and simple data structures

Ignore Bodies

Use Body = Mode.Ignore for business logic methods while managing signatures

Document Modes

Add comments explaining why specific modes are used for future maintainers

Troubleshooting

Code Not Being Generated

  • Check the [IntentManaged] attribute mode
  • Verify file-level [assembly: DefaultIntentManaged] setting
  • Ensure template is configured correctly

User Code Being Overwritten

  • Use Mode.Ignore or Body = Mode.Ignore
  • Check if code is inside Intent-managed section
  • Verify TagMode setting

Weaving Errors

  • Ensure code compiles before running Software Factory
  • Check for syntax errors in user code
  • Verify Intent.RoslynWeaver.Attributes package is referenced

See Also

Build docs developers (and LLMs) love