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
Add Intent Attributes
Mark code sections with [IntentManaged(Mode)] attributes to control weaving behavior
Generate Code
Templates generate code with Intent management attributes
Weaving Process
During Software Factory execution, the weaver merges existing and generated code
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
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