Whit
Whit
Explore posts from servers
MCMetalama Community
Created by Whit on 10/6/2023 in #technical-questions
How to inject factory-based dependencies from method aspect into type?
Let's use an example - in Microsoft.Extensions.Logging, I might register an ILoggerFactory so I can inject it into a type's constructor and then build a field from that. For example, typically this might look like this usually:
public class MyType
{
private readonly ILogger<MyType> _logger;

public MyType(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<MyType>();
}
}
public class MyType
{
private readonly ILogger<MyType> _logger;

public MyType(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<MyType>();
}
}
So I'd like to do something similar here. It's not quite what the dependency injection framework does because I'm injecting one thing into the constructor, but then making something else from it. And ideally, it's something that I could do from a MethodAspect because I want to be able to apply the attribute to various methods. Is this possible? As always, thank you!
3 replies
MCMetalama Community
Created by Whit on 9/3/2023 in #technical-questions
Unable to get aspect testing to work
No description
41 replies
MCMetalama Community
Created by Whit on 8/29/2023 in #technical-questions
How to debug a "Cannot preview the transformed code: unknown error"?
I've got an aspect that's building without error and a target project that's applying the aspect that also builds without error. Despite that, if I attempt to view the Metalama preview of the applied aspect, I get an error "Cannot preview the transformed code: unknown error" in the gold bar in VS. No crash report is generated for this error. Any ideas of how to diagnose what's happening here?
7 replies
MCMetalama Community
Created by Whit on 8/26/2023 in #technical-questions
Warn on ineligibility instead of throwing
I've got two conditions in my BuildEligibility for a method override aspect: If the target type is a record the method must be explcitly declared on the type, and the type's name must end with "Store". Unfortunately, when looking at the build logs, this means I've got several dozen errors as it's throwing an exception for any conditions that fails eligibility. I'd rather it not throw an error, but rather simply notate that it's skipped the application with a warning. Is this possible? Can I still get the benefits of build eligibility without it breaking the build due to all the errors thrown? Thanks!
4 replies
CC#
Created by Whit on 8/6/2023 in #help
How to perform LINQ Average against collection of TValue constrained as INumber<TValue>
I would like to perform an average against a collection of TValue wherein I know TValue is a number because I've constrained it to INumber<TValue>. However, when trying to use it in the LINQ expression, it doesn't appear to know how to handle the arbitrary number type:
var avg = group.Average(v => v.Value);
var avg = group.Average(v => v.Value);
CS0029: Cannot implicitly convert type 'TValue' to 'long?'
Any guesses as to how I can handle this generically without writing a separate overload for every numeric type? Thank you!
22 replies
MCMetalama Community
Created by Whit on 8/1/2023 in #technical-questions
Referencing value assigned to variable in statement builder within another statement
Given a statement that does something like:
var expr = new ExpressionBuilder();
expr.AppendVerbatim("var propertyValue = (");
expr.AppendType(field.Type);
expr.AppendVerbatim(")valueType?.GetValue(");
//And so on
var expr = new ExpressionBuilder();
expr.AppendVerbatim("var propertyValue = (");
expr.AppendType(field.Type);
expr.AppendVerbatim(")valueType?.GetValue(");
//And so on
I would like to use the value assigned to propertyValue elsewhere, but of course, I cannot use AppendExpression with it since the context isn't aware it's been assigned. How might I go about referencing this variable value as an expression? Thanks!
20 replies
MCMetalama Community
Created by Whit on 7/31/2023 in #technical-questions
Specifying generic constraints of compile-time introduced type
I have a TypeAspect that has three attribute arguments when applied to a type. One of these is a Type and is sprinkled liberally through my resulting compiled target. Since generic typed aspects aren't yet supported (https://github.com/postsharp/Metalama/issues/199) and one cannot limit eligibility based on instance members (such as this Type - https://github.com/postsharp/Metalama/issues/198), I'm at a loss as to how I can specify the generic constraints of this type (must implement IEquatable<>) at compile-time otherwise. Any ideas? Thanks!
9 replies
MCMetalama Community
Created by Whit on 7/28/2023 in #technical-questions
Step-through aspect-debugging
I'm sure I've asked this before, but I cannot find the thread in which it was answered (if I actually did).
Given an aspect, is it possible to step through my code (e.g. see the while Metalama is building the compile-time code? I'm trying to determine if a given type implements an IEnumerable, and if so, what the generic argument is for this IEnumerable, but I can't figure out how to peek inside it while debugging (Debugger.Break never hits) to see what precisely is happening.
if (typeof(IEnumerable).IsAssignableFrom(prop.Type.ToType()))
{
var genericTypes = prop.Type.ToType().GetInterfaces().FirstOrDefault(t =>
t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (genericTypes == null)
continue;

var innerGenericType = genericTypes.GetGenericArguments()[0];
if (typeof(IEnumerable).IsAssignableFrom(prop.Type.ToType()))
{
var genericTypes = prop.Type.ToType().GetInterfaces().FirstOrDefault(t =>
t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (genericTypes == null)
continue;

var innerGenericType = genericTypes.GetGenericArguments()[0];
I tried following the instructions at https://doc.metalama.net/conceptual/aspects/testing/debugging-aspects#debugging-compile-time-logic I inserted Debugger.Break() before my If statement, popped open a console window pointing at the project directory and ran dotnet build -p:MetalamaDebugCompiler=True. It asked me to pick a debugger, so I selected VS 2022 and it opened, loaded some symbols and then indicated that the task had been cancelled and that was it. What am I missing about that process? Thanks!
7 replies
MCMetalama Community
Created by Whit on 7/26/2023 in #technical-questions
How to apply an attribute to a method introduced by another aspect?
I have an aspect that has a dependency on another aspect. However, I'd like for downstream projects to be able to elect to apply either aspect without actually specifying both on the page. When applying the changes via a TypeAspect, how might I have it decorate the type with the attribute for another aspect? Thank you!
6 replies
MCMetalama Community
Created by Whit on 7/25/2023 in #technical-questions
Set eligibility to not run against (built-in) methods of record classes or structs
I've figured out how to set up an If statement for the record types, but I cannot figure out how to end it - how can I ensure my OverrideMethodAspect doesn't actually apply to any methods that are attached to a record? Even better though - is there any way to have it not apply to any methods that aren't automatically a part of a record (e.g. don't apply to ToString or Equals, but do apply to MyCustomMethod without specifically calling such custom methods out)? Thanks!
2 replies
MCMetalama Community
Created by Whit on 7/21/2023 in #technical-questions
How to specify an IMethod as the callback for an event?
I'd like to specify an introduced method on a TypeAspect as the callback for an event handler. 1) When I attempt to just pass it in via a template, I get squigglies: dictionary.OnDictionaryChanged += myDictionaryChangedMethod; //Doesn't like this since IMethod isn't a System.EventHandler 2) How can I similarly remove the event in a subsequently introduced Dispose() method? Thank you!
6 replies
MCMetalama Community
Created by Whit on 7/3/2023 in #technical-questions
Eligibility rule based on target method not returning void
Typically eligibility rules I've put together are based on a specifically typed return or paramters. I can't figured out how to do this so I thought I'd ask here - how can one add an eligibility rule that excludes methods that return a void? Thanks!
3 replies
MCMetalama Community
Created by Whit on 6/18/2023 in #technical-questions
Please cache licensing key rather than check every build
Every time I attempt to build an aspect today, I'm getting an error that I've got an invalid license key and that I should get one from the Metalama website, which appears to be offline. I'd like to request that the local licensing scheme be updated to support say, a three-day cache or so. In the future, if the licensing server goes offline, at least it'll work over the weekend until your team is able to resolve the issue. Thanks!
10 replies
MCMetalama Community
Created by Whit on 6/16/2023 in #technical-questions
Documentation: Bad link
At https://doc.metalama.net/conceptual/using/fabrics there's a link at the bottom titled "Configuring aspects with fabrics" pointing to https://doc.metalama.net/conceptual/using/fabrics/configuring but clicking this results in a popup indicating it 404s (visiting it manually does the same and redirects to the search page).
3 replies
MCMetalama Community
Created by Whit on 6/15/2023 in #technical-questions
Can one pass state between aspects?
One can trivially pass arbitrary data between the aspect class and a template via the 'args' parameter. As Metalama cannot yet create class out of thin air, I need to modify the override method of another class to do different things based on the data captured (and fields created) on another class (where it's later going to reference this second class and do things with it). Question: Can I pass any data from the "parent" aspect into this "child" aspect at compile-time?
9 replies
MCMetalama Community
Created by Whit on 5/30/2023 in #technical-questions
Why is it indicating I have compile-time code despite not?
No description
7 replies
MCMetalama Community
Created by Whit on 5/28/2023 in #technical-questions
How do I invoke (and cast) an introduced generically typed field?
Bear with me - there's a lot going on here. Also Discord is funky about styling my code, so I'm handwriting it (pardon any typos). At a high level, let me explain where I'm trying to go and then I'll get into the issue. I want to attach an attribute to a target type that defines a type (TEntity) and another type (TEntityId) so I can use these to introduce some advice later on. I can retrieve those no problem - each is saved as an INamedType on the BuildAspect method. I then want to iterate through most of the properties of the TEntity type itself and introduce at least one field for several of them, but the type of the field depends on the type of the property itself. Again, this is set up and works fine. I save all the references to the introductions in a dictionary keyed by a reproducible mechanism to link the properties with the introduced types. Again, this works fine. My target type implements a base type which has a bunch of event callbacks in place. What I want to do is replace the last methods in those event callback chains with a method that can do operations against some or all of these introduced fields and this is where I'm having some difficulty. I can pass along the produced IIntroductionAdviceResult<IField> via tags, but my problem is that I can't figure out how to assign the appropriate type to it. Now, typically I'd do something like this:
[Template]
private void DoSomething<[CompileTime]TMyType>(IMethod myMethod)
{
var thisIsMyMethod = (TMyType) myMethod.Invoke()
//Use it as though it's cast as a TMyType now
//...
}
[Template]
private void DoSomething<[CompileTime]TMyType>(IMethod myMethod)
{
var thisIsMyMethod = (TMyType) myMethod.Invoke()
//Use it as though it's cast as a TMyType now
//...
}
But the issue here is that the type is only known by the field itself and only as part of this compilation pipeline. As a result, I cannot do the following (or can I?):
[Template]
internal void RebuildNotificationAsyncCallback(
IIntroductionAdviceResult<IFIeld> adviceField
)
{
//I can get the type of the field off the type declaration itself...
var fieldType = adviceField.Declaration.Type; //...but then I cannot then use this with an 'as' or 'is' nor can I cast with a (fieldType)whatever.Value;
//So when I then get my value for it, it's stuck cast as a `dynamic?` and it's unclear how to proceed
var myField = adviceField.Declaration.Value; //Type = dynamic?
}
[Template]
internal void RebuildNotificationAsyncCallback(
IIntroductionAdviceResult<IFIeld> adviceField
)
{
//I can get the type of the field off the type declaration itself...
var fieldType = adviceField.Declaration.Type; //...but then I cannot then use this with an 'as' or 'is' nor can I cast with a (fieldType)whatever.Value;
//So when I then get my value for it, it's stuck cast as a `dynamic?` and it's unclear how to proceed
var myField = adviceField.Declaration.Value; //Type = dynamic?
}
I could try using meta since this is a template, but it's a similar problem - the only thing that knows what type it is is the method advice itself and I can't cast with that. Is there a helper method I'm missing somewhere to allow invocation of arbitrary typed introduced fields? Or how else might I go about using the adviceField in this template? Thank you!
90 replies
MCMetalama Community
Created by Whit on 5/27/2023 in #technical-questions
Integration with VS to display introduced elements in partial classes
No description
9 replies
MCMetalama Community
Created by Whit on 5/25/2023 in #technical-questions
How might I list the properties of a generic type within an aspect?
No description
2 replies
MCMetalama Community
Created by Whit on 5/25/2023 in #technical-questions
Live re-application and diff view updates of aspects on save
It's awfully convenient to enable Continuous Testing in Visual Studio. I turn it on and every time I save a file, Visual Studio kicks in to re-run any unit tests that apply to that particular bit of code and if I've got the test tooling panel open, I can see in near real-time whether I've broken anything. Today, when I start making changes to as aspect, that little golden bar shows up at the top of Visual Studio indicating that I'm making changes to the aspect and when I'm finished, I can click the "I'm done" button. The aspect will be re-applied everywhere applicable and after a brief pause (indicated by the processing of background tasks icon in the bottom left of Visual Studio) I can right click any of the targets it's applied to, select the "Show Metalama Diff" option from the context menu and voila, there's the target with the applied aspect. If I already have a diff open, I have to close it and re-open it from a target. All well and good for now, but I'd like to propose that this workflow be updated to reflect something more akin to the Continuous Testing functionality. I'd love to instead shift the workflow to the following: - Make changes to an aspect. Visual Studio detects that I've made changes and marks the tab with the asterisk showing unsaved changes. No golden warnings show up. - When I save the aspect, it automatically applies it behind the scenes to all applicable targets - If I have any Metalama Diff windows open, they maybe show a little icon in a corner indicating they're getting updated, but then automatically refresh to show the updates as applied (and any such open tabs are prioritized as the first targets of any aspect updates with other updates happening in the background, assuming they run in some sort of consecutive order). Ideally then, I'd be able to then start up VS, open up the diff of one of the targets, open the aspect and start typing as I would during run-time code development. When I'm done, I hit Ctrl + S and after a brief delay, my diff window updates to reflect the changes. Especially by having multiple diffs open that each have a different aspect order applied and being able to see them all refresh in turn with updates automatically, this would save me an awful lot of 1) time to view the changes, memory from open target tabs (so I might re-open the diffs today between changes) and 3) generally make the whole workflow feel a lot smoother and more alike any other background processing task (again, like Continuous Testing). Thank you for the consideration!
10 replies