-1

I am developing two libraries in .Net

Firs one it's a library with core functionality (named it Library.Core.dll)

Let's focus in User class

public class User
{
     //set of constructors and methods
     public void LogOff()
    {
        //does not know the functionality
    }
}

In the other library I am implementing some other functionalities (let's call it Library.Functionalities.dll), and I want to custom implement the LogOff method of User class. So whenever User it's instanstiated it has the LogOff method implemented.

Library.Core must not depend on anything (avoiding circular dependencies

Important: the libraries are being used at the moment by multiple applications, so creating new constructors (with dependency injection ) in Library.Funcionalities.dll it not possible(as far as I know) . I have to respect the way the others are using the library, so i dont break anything, but I have to ensure everybody is executing the code I want in LogOff.

What do you suggest? If you need more explanation tell me!

7
  • Do your third party clients instantiate a user with something like var user = new Library.Core.User() or do they use var user = new Library.Functionalities.User()? Or do they use a factory that you provide? Commented Sep 28, 2017 at 6:36
  • The instantiate using constructors as this var user = new Library.Core.User() Commented Sep 28, 2017 at 7:05
  • Hmmm... but it is a requirement that nothing references Library.Core except Library.Functionalities. That would seem to break the requirement. Unless you are implementing the namespace in Library.Functionalities.dll? Please don't tell me you have two classes with the exact same name and namespace. Commented Sep 28, 2017 at 7:16
  • There are many projects where User is being referenced...Library.Core acts as a common classes for some projects Commented Sep 28, 2017 at 7:17
  • So is "It's a requirement that only Library.Funcionalities depends on Library.Core" an incorrect statement? Sounds like all sorts of things depend on it. Just trying to understand. Commented Sep 28, 2017 at 7:20

3 Answers 3

0

Such requirements cannot be fulfilled with clean code, I think. There is a technical possibility, but it has some smells:

Define an interface ILogOff with a function void LogOff(User user) in the core library, and write some implementations in the functionalities library.

Create a ServiceLocator in the core library, make that static. When the ServiceLocator is created, it reads a configuration file, and creates the classes by reflection (hence they can be located in any other library).

Change the Logoff function to:

public void LogOff()
{
    ILogOff logOff = ServiceLocator.GetLogOff();
    logOff.LogOff(this);
}

That's not a great solution. Analyze it and find out if you really want to go that way.

2
  • Wouldn't that require to update the configuration of all client applications? Just asking to understand what are the boundaries! (in fact this question is more at @Badulake rather that Bernard Hiller) Commented Sep 28, 2017 at 10:03
  • Additional changes are not allowed in client applications. A library update of version is the only change allowed. Commented Sep 28, 2017 at 10:35
0

If:

  1. You cannot update Core
  2. You cannot update the other applications
  3. The other applications create users via new Library.Core.User(...)

Then, no. You cannot change the behavior of the LogOff method. You are stuck.

0

OP, I still don't fully understand your design, but I gather that your goals are:

  1. Pave the way for occasional updates to your business logic
  2. Minimize impact to third party applications which already reference one of your DLLs, Library.Core.dll.

Your current plan seems to be to deploy another DLL side by side with Library.Core.dll and leave the original DLL unchanged. This will not work. Unless a DLL is specifically coded to allow it, another DLL can't override its functionality-- that would be a huge burden to developers and would cause a security risk, since anyone could put a DLL out there that breaks an application, potentially in a malicious way.

So you will need to release at least one new version of Library.Core.dll. If you use the same version number and signing key, your clients won't need to recompile. Or if you would like to up the version number (generally recommended), you can tell your clients to use a binding redirect if they don't want to recompile.

Given the above, the new plan is to

  1. Write one new version of Library.Core.dll that is interface-identical to the original. This DLL must support future functionality without having to be changed.
  2. Write a net new Library.Functionalities.dll that will contain the implementation. You may end up releasing new versions of this in the future.

Here's how I would go about it:

First, write Library.Functionalities.dll using modern IoC techniques, introducing new constructors as needed but keeping the original object model.

Secondly, write a new Library.Core.dll that will be a facade to Library.Functionalities.dll. Here's an example:

[assembly:InternalsVisibleTo("Library.UnitTests")]
namespace Library.Core
{
    class User
    {
        private readonly IUser _innerUser;

        public User() : base (Factory.Resolve<IUser>());  //For public consumption

        internal User(IUser innerUser) //For your unit testing projects        
        {
            _innerUser = innerUser;
        }

        public void LogOff()
        {
            _innerUser.LogOff();  //Wrap the inner user object
         }
    }

    internal class Factory
    {
        private static MyIoCContainer _container = new MyIoCContainer(); //AutoFac, Unity, Ninject, whatever

        static Factory() //Composition root here
        {
            ContainerBuilder builder = new ContainerBuilder();
            Library.Functionalities.Application.RegisterDependencies(builder);
            _container = builder.Build();
        }

        static public T Resolve<T>()
        {
            return _container.Resolve<T>(); 
        } 
    }
}

And in Library.Functionalities.dll:

namespace Library.Functionalities
{
    public class Application
    {
        static void RegisterDependencies(ContainerBuilder builder)
        {
            builder.RegisterType<IUser, User>();
        }
    }
}

In the above example, Library.Core.User is just a thin wrapper for Library.Functionalities.User. If the caller constructs it using the old method (with no injection), it'll substitute a default injection from a factory.

You can write the factory any way you want. In my example I use a simple IoC container approach.

When you're done, actual resolution of the dependency on User is hidden from the client, who can continue to reference Library.Core.dll, completely unaware he is only talking to a facade.

When you have new functionality to distribute, you can simply update Library.Functionalities.dll, and if you have coded the factory properly the new version should automatically be picked up. And you will still be able to use all the modern IoC techniques, inject shims for unit testing, etc.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.