Skip to content
This repository has been archived by the owner on Aug 15, 2023. It is now read-only.

Could not load actions: class with EasyTcpAction doesn't have have parameterless constructor #43

Closed
daredloco opened this issue Jul 21, 2020 · 6 comments

Comments

@daredloco
Copy link

daredloco commented Jul 21, 2020

The examples
https://github.com/Job79/EasyTcp/blob/master/EasyTcp3/EasyTcp3.Examples/Actions/ActionsExample.cs
and
https://github.com/Job79/EasyTcp/blob/master/EasyTcp3/EasyTcp3.Examples/Actions/AuthorizationExample.cs
will both throw me this error:
error

I did copy the code from the examples 1:1 and added using EasyTcp3; as a using but I couldn't get it running

EasyTcp version: 3.6.3
EasyTcp.Actions version: 2.5.2
Net Framework: 4.7.2

Edit: This only happens if EasyTcp is called from inside a library.

  1. I made a test library (Net Framework 4.7.2)
  2. I did copy the content inside of it
  3. I did create a Console Application (NET Framework 4.7.2)
  4. I did call the functions inside the library from the Console Application
@Job79
Copy link
Owner

Job79 commented Jul 21, 2020

Thanks for creating an issue!

I found 1 problem in ActionsExample.cs, this is fixed
Couldn't find any problem in AuthorizationExample.cs

Does this minimal example work?

using System;
using System.Text;
using EasyTcp3;
using EasyTcp3.Actions;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            using var server = new EasyTcpActionServer();
            server.ExecuteAction("Print", new Message(Encoding.UTF8.GetBytes("Success")));
            Console.ReadKey();
        }

        [EasyTcpAction("Print")]
        public void Print(Message message)
            => Console.WriteLine(message);
    }
}

Edit:

This only happens if EasyTcp is called from inside a library.

EasyTcp takes the calling assembly as default assembly, do this when actions are located in the executing assembly:

 using var server = new EasyTcpActionServer(assembly: Assembly.GetExecutingAssembly());

@Job79
Copy link
Owner

Job79 commented Jul 21, 2020

Edit: This only happens if EasyTcp is called from inside a library.
I made a test library (Net Framework 4.7.2)
I did copy the content inside of it
I did create a Console Application (NET Framework 4.7.2)
I did call the functions inside the library from the Console Application

Could you provide the location of the call to the EasyTcpActionServer constructor and the location of the action methods?

Also, could you try moving the class outside the other class? Could be that the instance can't be created using reflection because it is inside another class.

@daredloco
Copy link
Author

Could you provide the location of the constructor and the location of the action methods?

Also, could you try moving the class outside the other class? Could be that the instance can't be created using reflection because it is inside another class.

The Console Application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole
{
	class Program
	{
		static void Main(string[] args)
		{
			SINCMultiplayer.AuthorizationExample.Start();
			SINCMultiplayer.AuthorizationExample.Connect();
			Console.ReadKey();
		}
	}
}

The code inside the library, now outside of the other class but with the same error, if i check "Assembly.GetExecutingAssembly().FullName" it will give me the name of the right assembly.

using System;
using System.Reflection;
using System.Threading.Tasks;
using EasyTcp3;
using EasyTcp3.Actions;
using EasyTcp3.Actions.ActionUtils;
using EasyTcp3.ClientUtils;
using EasyTcp3.Server;
using EasyTcp3.Server.ServerUtils;

namespace SINCMultiplayer
{
        public class AuthorizationExample
        {
            private const ushort Port = 52512;
            static EasyTcpActionServer server;

            public static void Start()
            {
                server = new EasyTcpActionServer(assembly: Assembly.GetExecutingAssembly()).Start(Port);
            }

            /* Example login action
             * Set userRole inside session
             */
            [EasyTcpAction("Login")]
            public void Login(Message message)
            {
                if (message.ToString() == "user")
                    message.Client.Session["UserRole"] = UserRole.User;
                else if (message.ToString() == "admin")
                    message.Client.Session["UserRole"] = UserRole.Admin;
                Console.WriteLine($"Authenticated {message}");
            }

		[EasyTcpAuthorization] // User does need to login before using this action
		[EasyTcpAction("Print")]
		public void UserOnlyThing(Message message) => Console.WriteLine(message);

		[EasyTcpAuthorization(UserRole.Admin)] // User does need to be admin for this action
		[EasyTcpAction("Clear")]
		public void AdminOnlyThing() => Console.Clear();

		[EasyTcpAuthorization]
		[EasyTcpAction("Logout")]
		public void Logout(Message message) => message.Client.Session.Remove("UserRole");

		public static void Connect()
            {
                var client = new EasyTcpClient();
                if (!client.Connect("127.0.0.1", Port)) return;

                client.SendAction("Print", "This is ignored because the user is not logged in");
                client.SendAction("Login", "user");
                client.SendAction("Print", "Hello server, I am now logged in");
                client.SendAction("Clear"); // Ignored by server
                client.SendAction("Logout");
                client.SendAction("Print", "This is ignored because user is logged out");
                Console.ReadLine();
            }
        }

        /* EasyTcpAuthorization,
         * example filter attribute for authorization
         */
        [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
        public class EasyTcpAuthorization : EasyTcpActionFilter
        {
            private readonly UserRole[] _allowedRoles;

            /// <summary>
            /// Accept any logged in user 
            /// </summary>
            public EasyTcpAuthorization() : base() => _allowedRoles = new[] { UserRole.User, UserRole.Admin };

            /// <summary>
            /// Accept user with a specific role
            /// </summary>
            /// <param name="role">allowed roles</param>
            public EasyTcpAuthorization(params UserRole[] role) : base() => _allowedRoles = role;

            /// <summary>
            /// Determines whether user has access to this action
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="message"></param>
            /// <returns></returns>
            public override bool HasAccess(object sender, ActionMessage message)
            {
                var hasRole = message.Client.Session.TryGetValue("UserRole", out object userRole);
                if (!hasRole) return false;

                if (_allowedRoles.Any(x => x == userRole as UserRole?)) return true;
                else return false;
            }
        }

        /* Enum with different roles */
        public enum UserRole
        {
            User,
            Admin
        }
}

If i put the same code inside the console application (the calling assembly) it works without problems

using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using EasyTcp3;
using EasyTcp3.Actions;
using EasyTcp3.Actions.ActionUtils;
using EasyTcp3.ClientUtils;
using EasyTcp3.Server;
using EasyTcp3.Server.ServerUtils;

namespace TestConsole
{
    public class AuthorizationExample
    {
        private const ushort Port = 52512;
        static EasyTcpActionServer server;
        public static void Start()
        {
            server = new EasyTcpActionServer().Start(Port);
        }

        /* Example login action
         * Set userRole inside session
         */
        [EasyTcpAction("Login")]
        public void Login(Message message)
        {
            if (message.ToString() == "user")
                message.Client.Session["UserRole"] = UserRole.User;
            else if (message.ToString() == "admin")
                message.Client.Session["UserRole"] = UserRole.Admin;
            Console.WriteLine($"Authenticated {message}");
        }

        [EasyTcpAuthorization] // User does need to login before using this action
        [EasyTcpAction("Print")]
        public void UserOnlyThing(Message message) => Console.WriteLine(message);

        [EasyTcpAuthorization(UserRole.Admin)] // User does need to be admin for this action
        [EasyTcpAction("Clear")]
        public void AdminOnlyThing() => Console.Clear();

        [EasyTcpAuthorization]
        [EasyTcpAction("Logout")]
        public void Logout(Message message) => message.Client.Session.Remove("UserRole");

        public static void Connect()
        {
            var client = new EasyTcpClient();
            if (!client.Connect("127.0.0.1", Port)) return;

            client.SendAction("Print", "This is ignored because the user is not logged in");
            client.SendAction("Login", "user");
            client.SendAction("Print", "Hello server, I am now logged in");
            client.SendAction("Clear"); // Ignored by server
            client.SendAction("Logout");
            client.SendAction("Print", "This is ignored because user is logged out");
            Console.ReadLine();
        }
    }

    /* EasyTcpAuthorization,
     * example filter attribute for authorization
     */
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public class EasyTcpAuthorization : EasyTcpActionFilter
    {
        private readonly UserRole[] _allowedRoles;

        /// <summary>
        /// Accept any logged in user 
        /// </summary>
        public EasyTcpAuthorization() => _allowedRoles = new[] { UserRole.User, UserRole.Admin };

        /// <summary>
        /// Accept user with a specific role
        /// </summary>
        /// <param name="role">allowed roles</param>
        public EasyTcpAuthorization(params UserRole[] role) => _allowedRoles = role;

        /// <summary>
        /// Determines whether user has access to this action
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public override bool HasAccess(object sender, ActionMessage message)
        {
            var hasRole = message.Client.Session.TryGetValue("UserRole", out object userRole);
            if (!hasRole) return false;

            if (_allowedRoles.Any(x => x == userRole as UserRole?)) return true;
            else return false;
        }
    }

    /* Enum with different roles */
    public enum UserRole
    {
        User,
        Admin
    }
}

@daredloco
Copy link
Author

So I did test it with a new library and it's working now, it seems like another library did make some problems.

Those are working without a problem:
works

Those make a problem:
doesntwork

At least this is my conclusion to that problem, because now it's working fine inside the (new) library.

@Job79
Copy link
Owner

Job79 commented Jul 21, 2020

...

Provided examples work fine on my machine. (.net core 3.1, .net standard 2.0 library)
Sadly I can't test .net framework because I am using linux.

So I did test it with a new library and it's working now, it seems like another library did make some problems.

Those are working without a problem:
works

Those make a problem:
doesntwork

At least this is my conclusion to that problem, because now it's working fine inside the (new) library.

Does adding EasyTcp.Encryption bring back the issue?

@daredloco
Copy link
Author

Does adding EasyTcp.Encryption bring back the issue?

No, that doesn't bring it back

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants