Open sandboxFocusImprove this doc

LAMA0656: The Initialize method isn't called in an expression tree

Severity Warning
Reported by Metalama.Framework
Message The 'Initialize' method of '<Type>' is not called after this object creation expression, because the expression is in a lambda expression converted to an expression tree, and an expression tree cannot contain this call.

Cause

When an aspect adds an initializer of kind AfterObjectInitializer, the target type implements IInitializable. Metalama rewrites every object creation expression of this type, such as new Document { Title = title }, so that it calls the Initialize method after the object initializer.

An expression tree can't contain this call. When the object creation expression is in a lambda expression converted to an expression tree (Expression<TDelegate>), Metalama keeps the expression unchanged and reports LAMA0656 on it. The created object isn't initialized: the code that the aspect adds to Initialize doesn't run for this object. This situation is common with IQueryable<T> and with query providers such as Entity Framework, whose methods receive their lambda expressions as expression trees. It also applies to the lambda expressions that the compiler generates for a query expression, such as from title in titles select new Document { Title = title }. In the message, <Type> is the type of the created object.

Lambda expressions converted to a delegate aren't affected. Metalama rewrites the object creation expressions that they contain.

In the following example, the [LogInitialized] aspect adds an AfterObjectInitializer initializer to Document. The GetDocuments method creates Document objects in a lambda expression that Queryable.Select receives as an expression tree.

1using System;
2using Metalama.Framework.Advising;
3using Metalama.Framework.Aspects;
4using Metalama.Framework.Code;
5
6namespace Doc.LAMA0656.Error;
7
8public class LogInitializedAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        // Call the template after the object initializer of each new instance.
13        builder.AddInitializer(
14            nameof(this.OnInitialized),
15            InitializerKind.AfterObjectInitializer );
16    }
17
18    [Template]
19    private void OnInitialized()
20    {
21        Console.WriteLine( $"{meta.Target.Type.Name} initialized." );
22    }
23}
24
Source Code
1using System.Linq;
2

3namespace Doc.LAMA0656.Error;

4
5[LogInitialized]
6public class Document
7{
8    public string? Title { get; set; }
9}
10




11public class DocumentQueries

12{
13    public IQueryable<Document> GetDocuments( IQueryable<string> titles )
14    {
15        // The lambda expression is converted to an expression tree, so Initialize is not called.
        Warning LAMA0656: The 'Initialize' method of 'Document' is not called after this object creation expression, because the expression is in a lambda expression converted to an expression tree, and an expression tree cannot contain this call.

16        return titles.Select( title => new Document { Title = title } );
17    }
18}
19
Transformed Code
1using System;
2using System.Linq;
3using Metalama.Framework.RunTime.Initialization;
4
5namespace Doc.LAMA0656.Error;
6
7[LogInitialized]
8public class Document : IInitializable
9{
10    public string? Title { get; set; }
11
12    public virtual void Initialize(InitializationContext context = default)
13    {
14        Console.WriteLine("Document initialized.");
15    }
16}
17
18public class DocumentQueries
19{
20    public IQueryable<Document> GetDocuments(IQueryable<string> titles)
21    {
22        // The lambda expression is converted to an expression tree, so Initialize is not called.
23        return titles.Select(title => new Document { Title = title });
24    }
25}
26

Resolution

Create the object in a method, and call this method from the lambda expression. The expression tree then contains a method call, which is allowed, and Metalama rewrites the object creation expression in the method so that it calls Initialize.

The query provider must be able to evaluate the call to your method. Check that your provider supports a call to a method of your code in this position of the query.

In the following example, the lambda expression calls the CreateDocument method, which creates the Document object.

1using System;
2using Metalama.Framework.Advising;
3using Metalama.Framework.Aspects;
4using Metalama.Framework.Code;
5
6namespace Doc.LAMA0656.Fixed;
7
8public class LogInitializedAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        // Call the template after the object initializer of each new instance.
13        builder.AddInitializer(
14            nameof(this.OnInitialized),
15            InitializerKind.AfterObjectInitializer );
16    }
17
18    [Template]
19    private void OnInitialized()
20    {
21        Console.WriteLine( $"{meta.Target.Type.Name} initialized." );
22    }
23}
24
Source Code
1using System.Linq;
2

3namespace Doc.LAMA0656.Fixed;

4
5[LogInitialized]
6public class Document
7{
8    public string? Title { get; set; }
9}
10





11public class DocumentQueries
12{
13    public IQueryable<Document> GetDocuments( IQueryable<string> titles )
14    {
15        // The expression tree calls a method, and this method creates the object.
16        return titles.Select( title => CreateDocument( title ) );
17    }
18
19    private static Document CreateDocument( string title ) => new Document { Title = title };
20}
21
Transformed Code
1using System;
2using System.Linq;
3using Metalama.Framework.RunTime.Initialization;
4
5namespace Doc.LAMA0656.Fixed;
6
7[LogInitialized]
8public class Document : IInitializable
9{
10    public string? Title { get; set; }
11
12    public virtual void Initialize(InitializationContext context = default)
13    {
14        Console.WriteLine("Document initialized.");
15    }
16}
17
18public class DocumentQueries
19{
20    public IQueryable<Document> GetDocuments(IQueryable<string> titles)
21    {
22        // The expression tree calls a method, and this method creates the object.
23        return titles.Select(title => CreateDocument(title));
24    }
25
26    private static Document CreateDocument(string title)
27    {
28        return new Document { Title = title }.WithInitialize();
29    }
30}
31

See also