I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}
Related
I am trying to make a source generator for mapping columns from the google bigquery api client to class properties. I'm having trouble getting custom column names from a ColumnAttribute on the properties. ConstructorArguments is always empty and columnAttribute.AttributeClass in this sample is always an ErrorTypeSymbol. If I try to load that type using compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute") the result is always null.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace BigQueryMapping;
[Generator]
public class BigQueryMapperGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// add marker attribute
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedAttribute.g.cs", SourceText.From(Attribute, Encoding.UTF8)));
// add static interface
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedInterface.g.cs", SourceText.From(Interface, Encoding.UTF8)));
// get classes
IncrementalValuesProvider<ClassDeclarationSyntax> classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax c && c.AttributeLists.Any(),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)
)
.Where(static m => m is not null)!;
IncrementalValueProvider<(Compilation Compilation, ImmutableArray<ClassDeclarationSyntax>Syntaxes)>
compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses,
static (spc, source) => Execute(source.Compilation, source.Syntaxes, spc));
static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
{
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
var fullName = context.SemanticModel.GetTypeInfo(attributeSyntax).Type?.ToDisplayString();
if (fullName == "BigQueryMapping.BigQueryMappedAttribute")
return classDeclarationSyntax;
}
}
return null;
}
static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
try
{
if (classes.IsDefaultOrEmpty)
return;
var distinctClasses = classes.Distinct();
var classesToGenerate = GetTypesToGenerate(compilation, distinctClasses, context.CancellationToken);
foreach (var classToGenerate in classesToGenerate)
{
var result = GeneratePartialClass(classToGenerate);
context.AddSource($"{classToGenerate.RowClass.Name}.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
catch (Exception e)
{
var descriptor = new DiagnosticDescriptor(id: "BQD001",
title: "Error creating bigquery mapper",
messageFormat: "{0} {1}",
category: "BigQueryMapperGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(descriptor, null, e.Message, e.StackTrace));
}
}
}
static IEnumerable<ClassToGenerate> GetTypesToGenerate(Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
CancellationToken ct)
{
var columnAttributeSymbol =
compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute");
foreach (var #class in classes)
{
Debug.WriteLine($"Checking class {#class}");
ct.ThrowIfCancellationRequested();
var semanticModel = compilation.GetSemanticModel(#class.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(#class) is not INamedTypeSymbol classSymbol)
continue;
var info = new ClassToGenerate(classSymbol, new());
foreach (var member in classSymbol.GetMembers())
{
if (member is IPropertySymbol propertySymbol)
{
if (propertySymbol.DeclaredAccessibility == Accessibility.Public)
{
if (propertySymbol.SetMethod is not null)
{
var columnName = propertySymbol.Name;
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
if (columnAttribute is not null)
{
if (!columnAttribute.ConstructorArguments.IsDefaultOrEmpty)
{
var nameArg = columnAttribute.ConstructorArguments.First();
if (nameArg.Value is string name)
{
columnName = name;
}
}
}
info.Properties.Add((columnName, propertySymbol));
}
}
}
}
yield return info;
}
}
static string GeneratePartialClass(ClassToGenerate c)
{
var sb = new StringBuilder();
sb.Append($#"// <auto-generated/>
namespace {c.RowClass.ContainingNamespace.ToDisplayString()}
{{
public partial class {c.RowClass.Name} : BigQueryMapping.IBigQueryGenerated<{c.RowClass.Name}>
{{
public static {c.RowClass.Name} FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row)
{{
return new {c.RowClass.Name}
{{");
foreach (var (columnName, property) in c.Properties)
{
// would like to check if key exists but don't see any sort of ContainsKey implemented on BigQueryRow
var tempName = $"___{property.Name}";
var basePropertyType = property.Type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
if (basePropertyType.EndsWith("?"))
{
basePropertyType = basePropertyType.Substring(default, basePropertyType.Length - 1);
}
sb.Append($#"
{property.Name} = row[""{columnName}""] is {basePropertyType} {tempName} ? {tempName} : default,");
}
sb.Append($#"
}};
}}
}}
}}");
return sb.ToString();
}
private record struct ClassToGenerate(INamedTypeSymbol RowClass,
List<(string ColumnName, IPropertySymbol Property)> Properties);
public const string Attribute = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
[System.AttributeUsage(System.AttributeTargets.Class)]
public class BigQueryMappedAttribute : System.Attribute
{
}
}";
public const string Interface = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
public interface IBigQueryGenerated<TRow> {
static TRow FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row) => throw new System.NotImplementedException();
}
}";
}
I have tried this with both System.ComponentModel.DataAnnotations.Schema.ColumnAttribute and a custom attribute injected via context.RegisterPostInitializationOutput to similar results. I have also tried rewriting this to use ISourceGenerator instead of IIncrementalGenerator and gotten the same behavior. Am wondering what I need to do to get columnAttribute loading correctly.
Thanks for any help in advance
It's hard to psychic debug the code but this does jump out:
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
I'd guess the class here would be the fully qualified name. You already fetched the ColumnAttribute type earlier, so what'd be even better is to compare the AttributeClass to that type rather than doing string checks like this.
As a semi-related comment, if you're looking for types/members that are annotated with a specific attribute, rather than doing it yourself we have SyntaxValueProvider.ForAttributeWithMetadataName which is pretty heavily optimized to reduce the performance impact on your Visual Studio. It requires 17.3 or higher, but as long as you're OK with that it'll generally help performance.
How can I get all classes that implements a specific interface then call a function of that class if a string member of the specific class matches a given one?
Basically what I have is a ICommandHandler interface:
interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
and a class that implements it:
public class StartCommand : ICommandHandler
{
public string Command { get => "start"; }
public async Task ExecuteAsync()
{
// do something
}
}
What I want to do is to get all the classes that implements the ICommandHandler interface, then verify if the class.Command equals a specific string and if it does then call the ExecuteAsync method.
I've tried using this answer here: https://stackoverflow.com/a/45382386/15306888 but the class.Command is always null
Edit: The answer I got bellow does what I wanted to do:
What I was looking for was a way to use ICommandHandler to allow me to easily gather all the classes inheriting from it and call the ExecuteAsync function instead of having to manually add the methods in the part of the code handling TdLib message events.
So now my project directory looks something like this:
TelegramClient.cs - The place where the classes inheriting from the ICommandHandler are loaded, and where the ExecuteAsync method is called if the Command member matches the Command parsed from the message returned by telegram.
Handlers/ - Base directory for my handlers
CommandHandlers/ - Folder with the ICommandHandler interface and the classes inheriting from it
MessageHandlers/ - Folder with another interface IMessageHandler and the classes inheriting from it
Anyway, in the meantime I've found another answer on a stackoverflow question (Had to scroll a few times) that made it way easier and faster to get multiple handlers without having to repeat the same code over and over again. I've ended by combining the answer linked above with this one: https://stackoverflow.com/a/41650057/15306888
So I ended with a simple method:
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
That can be easily used like:
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
}
How the Interface is actually implemented:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TdLib;
namespace YoutubeDl_Bot.Handlers.CommandHandlers
{
public class StartCommand : ICommandHandler
{
public string Command { get => "/start"; }
public async Task ExecuteAsync(TdClient client, TdApi.Message message)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Hi! I'm a bot that downloads and sends video and audio files from youtube links and many other supported services");
stringBuilder.AppendLine(String.Empty);
stringBuilder.AppendLine("**Usage:**");
stringBuilder.AppendLine("• Send or forward a text message containing links and I will:");
stringBuilder.AppendLine("• Download the best audio quality available for the video in the speecified link");
stringBuilder.AppendLine("• Download the best video quality available for the video in the speecified link");
stringBuilder.AppendLine("• Send the direct download URL for every link specified in the message");
stringBuilder.AppendLine("• Supported links are available here: https://ytdl-org.github.io/youtube-dl/supportedsites.html");
var formattedText = await client.ExecuteAsync(new TdLib.TdApi.ParseTextEntities { Text = stringBuilder.ToString(), ParseMode = new TdLib.TdApi.TextParseMode.TextParseModeMarkdown() });
await client.ExecuteAsync(new TdLib.TdApi.SendMessage { ChatId = message.ChatId, InputMessageContent = new TdLib.TdApi.InputMessageContent.InputMessageText { Text = formattedText } });
}
}
}
Full TelegramClient class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using TdLib;
using YoutubeDl_Bot.Handlers.CallbackHandlers;
using YoutubeDl_Bot.Handlers.CommandHandlers;
using YoutubeDl_Bot.Handlers.MessageHandlers;
using YoutubeDl_Bot.Settings;
using YoutubeDl_Bot.Utils;
namespace YoutubeDl_Bot
{
class TelegramBotClient
{
private static TdClient _client;
private static TdLib.TdApi.User _me;
private static int _apiId;
private static string _apiHash;
private static string _token;
#if DEBUG
private static readonly int _verbosityLevel = 4;
#else
private static readonly int _verbosityLevel = 0;
#endif
public static bool AuthCompleted = false;
public TelegramBotClient(int apiId, string apiHash)
{
_apiId = apiId;
_apiHash = apiHash;
}
public async Task<TdClient> CreateClient()
{
_client = new TdClient();
await _client.ExecuteAsync(new TdApi.SetLogVerbosityLevel { NewVerbosityLevel = _verbosityLevel });
return _client;
}
public void StartListening(string botToken)
{
_token = botToken;
_client.UpdateReceived += _client_UpdateReceived;
}
private async void _client_UpdateReceived(object sender, TdApi.Update update)
{
switch (update)
{
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdApi.AuthorizationState.AuthorizationStateWaitTdlibParameters):
await _client.ExecuteAsync(new TdApi.SetTdlibParameters
{
Parameters = new TdApi.TdlibParameters
{
ApiId = _apiId,
ApiHash = _apiHash,
ApplicationVersion = "0.0.1",
DeviceModel = "Bot",
SystemLanguageCode = "en",
SystemVersion = "Unknown"
}
});
break;
case TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitEncryptionKey):
await _client.ExecuteAsync(new TdLib.TdApi.CheckDatabaseEncryptionKey());
break;
case TdLib.TdApi.Update.UpdateAuthorizationState updateAuthorizationState when updateAuthorizationState.AuthorizationState.GetType() == typeof(TdLib.TdApi.AuthorizationState.AuthorizationStateWaitPhoneNumber):
await _client.ExecuteAsync(new TdLib.TdApi.CheckAuthenticationBotToken { Token = _token });
break;
case TdLib.TdApi.Update.UpdateConnectionState updateConnectionState when updateConnectionState.State.GetType() == typeof(TdLib.TdApi.ConnectionState.ConnectionStateReady):
// To Do Settings
var botSettings = new BotSettings(_apiId, _apiHash, _token);
_me = await _client.ExecuteAsync(new TdLib.TdApi.GetMe());
Helpers.Print($"Logged in as: {_me.FirstName}");
SettingsManager.Set<BotSettings>("BotSettings.data", botSettings);
break;
case TdLib.TdApi.Update.UpdateNewMessage message:
if (!message.Message.IsOutgoing)
await ProcessMessage(message);
break;
case TdApi.Update.UpdateNewCallbackQuery callbackQuery:
await ProcessCallbackQuery(callbackQuery);
break;
default:
break;
}
}
#region PROCESS_MESSAGE
private async Task ProcessMessage(TdApi.Update.UpdateNewMessage message)
{
var command = GetCommand(message.Message);
var textMessage = GetMessageText(message.Message);
#region COMMAND_HANDLERS
if (!String.IsNullOrWhiteSpace(command))
{
var commandHandlers = GetAll<ICommandHandler>();
foreach (var handler in commandHandlers)
{
if (command == handler.Command)
await handler.ExecuteAsync(_client, message.Message);
}
}
#endregion
#region MESSAGE_HANDLERS
else if (!String.IsNullOrWhiteSpace(textMessage))
{
var messageHandlers = GetAll<IMessageHandler>();
foreach (var handler in messageHandlers)
{
var outgoing = handler.Outgoing && message.Message.IsOutgoing;
var incoming = handler.Incoming && !message.Message.IsOutgoing;
if (outgoing || incoming)
{
if (!String.IsNullOrEmpty(handler.Pattern))
{
var match = Regex.Match(textMessage, handler.Pattern);
if (match.Success)
await handler.ExecuteAsync(_client, message.Message);
}
}
}
}
#endregion
}
#endregion
#region PROCESS_CALLACK
private async Task ProcessCallbackQuery(TdApi.Update.UpdateNewCallbackQuery callbackQuery)
{
if (callbackQuery.Payload.GetType() == typeof(TdApi.CallbackQueryPayload.CallbackQueryPayloadData))
{
var payload = callbackQuery.Payload as TdApi.CallbackQueryPayload.CallbackQueryPayloadData;
var callbackHandlers = GetAll<ICallbackHandler>();
foreach (var handler in callbackHandlers)
{
if (handler.DataIsRegex)
if (Regex.Match(System.Text.Encoding.UTF8.GetString(payload.Data), handler.Data).Success)
await handler.ExecuteAsync(_client, callbackQuery);
else if (handler.Data == System.Text.Encoding.UTF8.GetString(payload.Data))
await handler.ExecuteAsync(_client, callbackQuery);
}
}
}
#endregion
#region COMMAND_PARSER
public string GetCommand(TdApi.Message message)
{
string command = null;
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
foreach (var entity in formattedText.Entities)
{
if (entity.Type.GetType() == typeof(TdLib.TdApi.TextEntityType.TextEntityTypeBotCommand) && String.IsNullOrWhiteSpace(command))
{
if (entity.Offset == 0)
{
var splitCommand = formattedText.Text.Split();
if (splitCommand[0].EndsWith($"#{_me.Username}"))
{
command = splitCommand[0].Split('#')[0];
}
else
{
command = splitCommand[0];
}
}
}
}
return command;
}
#endregion
#region MESSAGE_PARSER
public string GetMessageText(TdApi.Message message)
{
TdLib.TdApi.FormattedText formattedText = new TdLib.TdApi.FormattedText();
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageText))
{
var messageText = message.Content as TdLib.TdApi.MessageContent.MessageText;
formattedText = messageText.Text;
}
else
{
if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessagePhoto))
{
var messagePhoto = message.Content as TdLib.TdApi.MessageContent.MessagePhoto;
formattedText = messagePhoto.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageDocument))
{
var messageDocument = message.Content as TdLib.TdApi.MessageContent.MessageDocument;
formattedText = messageDocument.Caption;
}
else if (message.Content.GetType() == typeof(TdLib.TdApi.MessageContent.MessageVideo))
{
var messageVideo = message.Content as TdLib.TdApi.MessageContent.MessageVideo;
formattedText = messageVideo.Caption;
}
}
return formattedText.Text;
}
#endregion
#region REFLECTION
// https://stackoverflow.com/a/41650057/15306888
public static IEnumerable<T> GetAll<T>()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type))
.Where(type =>
!type.IsAbstract &&
!type.IsGenericType &&
type.GetConstructor(new Type[0]) != null)
.Select(type => (T)Activator.CreateInstance(type))
.ToList();
}
#endregion
}
}
Since it's always null I think that the problem is that you're not creating an instance of your handler. I prepared a demo for you where I did that and it works.
public interface ICommandHandler
{
string Command { get; }
Task ExecuteAsync();
}
public class FirstCommandHandler : ICommandHandler
{
public string Command => "First";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from first.");
await Task.Delay(10);
}
}
public class SecondCommandHandler : ICommandHandler
{
public string Command => "Second";
public async Task ExecuteAsync()
{
Console.WriteLine("Hello from second.");
await Task.Delay(10);
}
}
public class Program
{
static async Task Main(string[] args)
{
var handlers = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => typeof(ICommandHandler).IsAssignableFrom(p) && p.IsClass);
foreach (var handler in handlers)
{
var handlerInstance = (ICommandHandler)Activator.CreateInstance(handler);
if (handlerInstance.Command == "First")
{
await handlerInstance.ExecuteAsync();
}
}
}
}
If it's not the case, could you show some more code? Are you trying to check Command value by reflection?
I have had a static version of this type of code working in a static version. However the API calls were just incredibly slow. I am trying to move to asynchronous now that C# 7 supports console async tasks (where I add code to connect to my DB and store data. I want to see this code output on the console to ensure it's working so I can assign variables for loading. I can't seem to figure out how to access the list from main. Here is the code I have so far:
Wrapper (or C# library):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace AlphaVantageApiWrapper
{
public static class AlphaVantageApiWrapper
{
public static async Task<AlphaVantageRootObject> GetTechnical(List<ApiParam> parameters, string apiKey)
{
var stringRequest = parameters.Aggregate(#"https://www.alphavantage.co/query?", (current, param) => current + param.ToApiString());
stringRequest += "&apikey=" + apiKey;
var apiData = await CallAlphaVantageApi(stringRequest);
var technicalsObject = new AlphaVantageRootObject
{
MetaData = new MetaData
{
Function = parameters.FirstOrDefault(x => x.ParamName.Equals("function"))?.ParamValue ?? "NA?",
Interval = parameters.FirstOrDefault(x => x.ParamName.Equals("interval"))?.ParamValue ?? "NA?",
SeriesType = parameters.FirstOrDefault(x => x.ParamName.Equals("series_type"))?.ParamValue ?? "NA?",
Symbol = parameters.FirstOrDefault(x => x.ParamName.Equals("symbol"))?.ParamValue ?? "NA?"
},
TechnicalsByDate = apiData.Last.Values().OfType<JProperty>().Select(x => new TechnicalDataDate
{
Date = Convert.ToDateTime(x.Name),
Data = x.Value.OfType<JProperty>().Select(r => new TechnicalDataObject
{
TechnicalKey = r.Name,
TechnicalValue = Convert.ToDouble(r.Value.ToString())
}).ToList()
})
.ToList()
};
return technicalsObject;
}
public class ApiParam
{
public string ParamName;
public string ParamValue;
public ApiParam(string paramNameIn, string paramValueIn)
{
ParamName = paramNameIn;
ParamValue = paramValueIn;
}
public string ToApiString()
{
return $"&{ParamName}={ParamValue}";
}
}
public static string ToDescription(this Enum enumeration)
{
var type = enumeration.GetType();
var memInfo = type.GetMember(enumeration.ToString());
if (memInfo.Length <= 0) return enumeration.ToString();
var attrs = memInfo[0].GetCustomAttributes(typeof(EnumDescription), false);
return attrs.Length > 0 ? ((EnumDescription)attrs[0]).Text : enumeration.ToString();
}
public static async Task<JObject> CallAlphaVantageApi(string stringRequest)
{
try
{
using (var client = new HttpClient())
{
var res = await client.GetStringAsync(stringRequest);
return JsonConvert.DeserializeObject<JObject>(res);
}
}
catch (Exception e)
{
//fatal error
return null;
}
}
public class AlphaVantageRootObject
{
public MetaData MetaData;
public List<TechnicalDataDate> TechnicalsByDate;
}
public class MetaData
{
public string Function;
public string Interval;
public string SeriesType;
public string Symbol;
}
public class TechnicalDataDate
{
public DateTime Date;
public List<TechnicalDataObject> Data;
}
public class TechnicalDataObject
{
public string TechnicalKey { get; set; }
public double TechnicalValue { get; set; }
}
public class EnumDescription : Attribute
{
public string Text { get; }
public EnumDescription(string text)
{
Text = text;
}
}
public enum AvFuncationEnum
{
[EnumDescription("SMA")] Sma,
[EnumDescription("EMA")] Ema,
[EnumDescription("MACD")] Macd,
[EnumDescription("STOCH")] Stoch,
[EnumDescription("RSI")] Rsi,
}
public enum AvIntervalEnum
{
[EnumDescription("1min")] OneMinute,
[EnumDescription("5min")] FiveMinutes,
[EnumDescription("15min")] FifteenMinutes,
[EnumDescription("30min")] ThirtyMinutes,
[EnumDescription("60min")] SixtyMinutes,
[EnumDescription("daily")] Daily,
[EnumDescription("weekly")] Weekly,
[EnumDescription("monthly")] Monthly
}
public enum AvSeriesType
{
[EnumDescription("close")] Close,
[EnumDescription("open")] Open,
[EnumDescription("high")] High,
[EnumDescription("low")] Low,
}
}
}
`
The c# async main task (which obviously isn't working)...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AlphaVantageApiWrapper.Test
{
public static class AlphaVantageApiDbLoader
{
public static async Task Main(string[] args)
{
var API_KEY = "EnterAPIHERE";
var StockTickers = new List<string> { "AAPL" }; //eventualy becomes a list pulled in from the DB for processing
foreach (var ticker in StockTickers)
{
var parameters = new List<AlphaVantageApiWrapper.ApiParam>
{
new AlphaVantageApiWrapper.ApiParam("function", AlphaVantageApiWrapper.AvFuncationEnum.Sma.ToDescription()),
new AlphaVantageApiWrapper.ApiParam("symbol", ticker),
new AlphaVantageApiWrapper.ApiParam("interval", AlphaVantageApiWrapper.AvIntervalEnum.Daily.ToDescription()),
new AlphaVantageApiWrapper.ApiParam("time_period", "5"),
new AlphaVantageApiWrapper.ApiParam("series_type", AlphaVantageApiWrapper.AvSeriesType.Open.ToDescription()),
};
//Start Collecting SMA values
var SMA_5 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
///var SMA_5Result = AlphaVantageApiWrapper.TechnicalDataObject() // can't all method error just want values fron list
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "20";
var SMA_20 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "50";
var SMA_50 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
parameters.FirstOrDefault(x => x.ParamName == "time_period").ParamValue = "200";
var SMA_200 = await AlphaVantageApiWrapper.GetTechnical(parameters, API_KEY);
//Change function to EMA
//Change function to RSI
//Change function to MACD
}
}
}
}
Any help would be greatly appreciated! I know the code runs in the background, I just can't seem to get it to a point to view it on the console screen. Eventually I would assign the symbol, date, value returned variable and read these to a DB. I'm used to using DataTables, but the async and .ToList is new to me. Thanks!!
i have working CompileAssemblyFromSource code. But when i use any code protector like RedGate SmartAssembly or Themida it's stop working and i get error "Could not load file or assembly or one of its dependencies". Can you please help me with that?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
try
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{ }
}
var res = foo.CompileAssemblyFromSource(parameters, source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);;
}
Console.ReadLine();
}
}
}
sorry for my question. I just understand why it happens)
This is code is example. The main problem i have with code that i get from server.
So when i obfuscate my vars i don't obfuscare them at my "online" code that i use with CompileAssemblyFromSource. So this just can't work. Because vars don't have same names.
i try to use CompileAssemblyFromSource to change 1 value at my main class.
But when i compile i get error "Could not load file or assembly or one of its dependencies" and this only happens when i try change static value of other class. But if i return some output or wrote anything at Console from this FooClass than all work's fine. But how can i change value of other class?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
You can't find the type because you have compilation error in your code.You can't access the classes in your current code in this manner. You should at least reference the current assembly in your in-memory assembly.
UPDATE
You have two issues in your code. First, you have to make the class Program public. Then you should specify the full name of type in GetType method.
This code works fine:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}