Skip to content

Variables ​

The Variable class in JasperFx.CodeGeneration.Model represents a named, typed value flowing through generated code. Variables are the primary mechanism frames use to declare their inputs and outputs, enabling the code generation engine to resolve dependencies and order frames automatically.

Creating Variables ​

cs
public static void CreateVariables()
{
    // Create a variable with an auto-generated name based on the type
    var widget = new Variable(typeof(Widget), "widget");

    // The Usage property is the C# identifier used in generated code
    Console.WriteLine(widget.Usage); // "widget"

    // DefaultArgName generates a camelCase name from the type
    var defaultName = Variable.DefaultArgName(typeof(Widget));
    Console.WriteLine(defaultName); // "widget"

    // Variable tied to a creating Frame
    var frame = new MethodCall(typeof(WidgetFactory), nameof(WidgetFactory.Build));
    var returnVar = frame.ReturnVariable;
    Console.WriteLine(returnVar!.Creator == frame); // true
}

snippet source | anchor

Key Properties ​

PropertyTypeDescription
VariableTypeTypeThe .NET type of the variable.
UsagestringThe C# identifier used in generated source code.
CreatorFrame?The frame that creates this variable, if any.

Default Naming ​

Variable.DefaultArgName(Type) produces a conventional camelCase name from a type:

cs
public static void DefaultArgNameExamples()
{
    // Simple types use lowercase type name
    Console.WriteLine(Variable.DefaultArgName(typeof(Widget))); // "widget"

    // Arrays get "Array" suffix
    Console.WriteLine(Variable.DefaultArgName(typeof(int[]))); // "intArray"

    // Generic types include inner type
    Console.WriteLine(Variable.DefaultArgName(typeof(List<string>))); // "listOfString"

    // Reserved C# keywords get an @ prefix
    Console.WriteLine(Variable.DefaultArgName(typeof(Event))); // "@event"
}

snippet source | anchor

The naming rules are:

  • Simple types use the lowercase type name (e.g., Widget becomes widget).
  • Array types append Array (e.g., int[] becomes intArray).
  • Generic types include the inner type (e.g., List<string> becomes listOfString).
  • If the resulting name is a C# reserved keyword, it is prefixed with @ (e.g., Event becomes @event).

Variables and Frames ​

Every Frame maintains two lists:

  • creates -- variables this frame produces. Downstream frames that need this type will depend on this frame.
  • uses -- variables this frame consumes. The engine will find or create the producing frame automatically.

When a frame calls Create<T>() or Create(Type), the returned Variable has its Creator set to that frame and is added to the frame's creates list.

Variable Resolution ​

During code arrangement, the engine resolves variables in this order:

  1. Locally created variables -- a variable created by a frame earlier in the chain.
  2. Method arguments -- parameters declared on the GeneratedMethod.
  3. Injected fields -- fields declared on the GeneratedType (constructor-injected dependencies).
  4. Variable sources -- custom IVariableSource implementations registered on the GenerationRules.

If a variable cannot be resolved through any of these sources, the code generation will throw an exception describing the missing dependency.

A variable source is a factory, not a lookup

Step 4 builds what the earlier steps could not find. That makes IMethodVariables.TryFindVariable(type, VariableSource.All) -- and VariableSource.NotServices -- a poor way to ask "does this method already have one of these?", because the answer is manufactured on the spot, and taking a dependency on it pulls the newly built frame into the method.

Use VariableSource.Existing for that question instead. It answers only from steps 1-3 and returns null rather than consulting any IVariableSource:

csharp
var session = chain.TryFindVariable(typeof(IDocumentSession), VariableSource.Existing);
if (session != null)
{
    // ...the method really does have one, so it is safe to use
}

InjectedField ​

InjectedField is a specialized Variable that represents a constructor parameter and corresponding private field on the generated type:

csharp
var field = new InjectedField(typeof(ILogger));
type.AllInjectedFields.Add(field);

This produces a constructor parameter and a private readonly field assignment in the generated class. Any frame that needs an ILogger variable will automatically resolve to this injected field.

Released under the MIT License.