1

Let's say that we have 3 projects in our visual studio solution. Project A, B and C.

Project A code:

public class A
{
  public void someMethodA()
  {
    B b = new B();
    b.someMethodB(new C());
  }
}

Project B code:

public class B
{
  public void someMethodB(C c)
  {
    c.foo();  // we do something with C, call C's method
  }
}

Project C code:

public class C
{
  public void foo()
  {
    return; // just for testing
  }
}

Project B has a reference to project C, as it needs it (class C is needed in someMethodB). Project A has a reference to project B as it creates new object B and calls B's method - someMethodB().

At this point our solution won't compile, because project A needs reference to project C - it calls b.someMethodB(new C()) which needs project C. Does project A really needs reference to project C? Can't it get it from project B which also references it. I know that each project generates its own dll file and in bin directory there are 3 files, but still it could get what it needs from project B.

Project C isn't B domain, but we could get reference to C throught B.

broadband
  • 3,266
  • 6
  • 43
  • 73
  • *"we could get reference to C throught B"*, that is correct, you can get an instance of `C` through assembly `B` (although you won't be able to do much with it, apart from passing it to other parts of assembly `B`. But you are trying to **instantiate** it inside assembly `A` using the `new` keyword. – vgru Oct 20 '15 at 11:04
  • In my experience it's a good idea to add direct references even in cases where the C# compiler doesn't require it. While that's ugly, the .net build system sometimes doesn't rebuild the stuff it needs to rebuild otherwise. – CodesInChaos Oct 20 '15 at 15:29
  • @CodesInChaos does this have to do something with sometimes deleting bin/ and obj/ directories. Sometimes exception occurs and when we delete bin/ and /obj directorijes everything works ok (of course rebuild is required after deleting directories) – broadband Nov 02 '15 at 11:03
  • @Groo I just thought that assembly B could return C (and all C's metadata and whatever information is needed) without A referencing C. I just tried B returning new C(), but A still needs a reference to C, as A needs to know how to work with C. I believe James Thorpe's answer has some valid point. – broadband Nov 02 '15 at 11:09
  • @broadband: it needs the reference because it needs to know which members the type `C` has. *But*, if all three assemblies reference a common assembly which defines a certain interface (e.g. `IC`), then `A` can get and instance of `C` through the interface `IC`, as long as `C` implements `IC` and the `B` methods' return type is `IC` instead of `C`. This is what I had in mind, but should have been more explicit. – vgru Nov 02 '15 at 11:14
  • @broadband: I have added an answer with an example. – vgru Nov 02 '15 at 11:32

4 Answers4

1

If A were just calling a method on B without having to pass in C, even if that method itself used C, you wouldn't need a direct reference. However, since A needs to construct a C, it needs a direct reference to C in order to have the required metadata available to do so - this metadata is contained in C, and isn't copied to B.

If all metadata were copied from all referenced libraries up the chain so you only ever needed a direct reference to the dll you're using, the sizes of dlls would balloon, even if that functionality was never used - it would need to be there "just in case".

James Thorpe
  • 31,411
  • 5
  • 72
  • 93
0

Why not make a method in project B that returns a new C()? Then you can avoid using C in A.

Tom
  • 7,994
  • 8
  • 45
  • 62
  • Just tried it. It says I still need to add a refererence to C (from project A). B's method: `public void someMethodB() { return new C(); }` – broadband Nov 02 '15 at 10:13
0

Yes, A needs the direct reference because it needs to know what kind of C it is dealing with.
For example there might be a System.Whatever.C and a MyProject.C - now what constructor should it call at new C() if you don't specify it?

If you want to avoid the reference in A you could create the instance of C in someMethodB instead of passing it as a parameter.

Breeze
  • 2,010
  • 2
  • 32
  • 43
  • We have ConnectionInfo class which holds information about sql database e.g. username, schema, password, database,.... Next we create for example UserService project which uses this ConnectionInfo class to read users from database. Finally we have some web service e.g. UsersWebService which uses UserService to get/search users. This is the story from which this question has come to my mind. – broadband Nov 02 '15 at 10:57
0

If your C assembly implements a certain interface (e.g. IC), and assembly A references the assembly where the interface is defined, then A won't have to know anything about C, as long as B method's return type is not concrete C, but instead the interface IC.

Schematically, it should be something like this:

If you have a common assembly where the interface is defined, you don't need to reference C explicitly

This is most likely how you should organize your application anyway. I've noticed that you mentioned passing around a certain ConnectionInfo which contains information needed for the database connection, but you certainly don't want to have access to database credentials from your UI layer, or even be able to create actual DbConnection instances.

For example, you might implement a sort of a repository pattern, or a simple ORM like this:

// IC assembly (common assembly containing entities and repo interfaces)
public interface ISession : IDisposable
{
     // It can even be an empty, marker interface for the
     // outside world. Only `C` will know its internals anyway.
}

public interface IRepoFactory
{
     ISession OpenSession();
     IRepo<T> GetRepo<T>(ISession session);
}

Your B assembly can return a concrete Session instance to A:

// B assembly
// RepoFactory should also be internal to B assembly,
// the only thing that A needs is a method which will
// provide the implementation of IRepoFactory
internal RepoFactory : IRepoFactory
{
    public ISession OpenSession()
    {
        // this creates a concrete instance,
        // but callers will only get the `ISession` interface
        return C.CreateActualSession();
    }

    // other methods should also accept the session
    // object through its interface
    public IRepo<T> GetRepo<T>(ISession session)
    {
        return C.GetRepo(session);
    }
}

But your A assembly can use ISession without any idea how it's implemented under the hood:

// somewhere in `A` assembly
var repoFactory = B.GetRepoFactory();
using (var session = repoFactory.OpenSession())
{
    var ordersRepo = repoFactory.GetRepo<IOrdersRepo>(session);
    ordersRepo.Save(something);
}
vgru
  • 49,838
  • 16
  • 120
  • 201
  • I see your point, the share the same interface/contract. Nice one with schematic approach. Shouldn't B assembly public RepoFactory inherit IRepoFactory interface e.g. public RepoFactory : RepoFactory – broadband Nov 02 '15 at 18:04
  • I believe the correct would be that UserWebService which is UI layer shouldn't be aware of database credentials, yes. But database connection string is read from web.config file from caller. Caller is in this case UI layer - Userwebservice or winforms for that matter. Maybe a better approach would be to creating app.config file in UserService project? Apparantly Microsoft has this kind of pattern. In reads all data from caller config file. I don't know what is better. So basically using Microsoft pattern enforces you to keep all necessary settings in the UI application. – broadband Nov 02 '15 at 18:30
  • Just wanted to add that we create ConnectionInfo object from settings defined in web.config caller file. – broadband Nov 02 '15 at 18:36
  • @broadband: you should let your data layer use its own settings, not require the UI to load them and pass them forward. However, if you use .NET app settings (app.config or web.config), then the actual values read at runtime will always be read from the setting file for the entry point. In other words, you can add `app.config` to your data layer, but depending on whether you start the process from a web project or a winforms app, settings will be read from a different place (the `appname.exe.config` in your release folder). But this doesn't mean UI needs to actually load them. – vgru Nov 03 '15 at 12:54
  • @broadband: you can also check [this thread](http://stackoverflow.com/a/33237394/69809) for a better explanation of how config files work in .NET. Our company doesn't use .NET settings, but instead each assembly stores its own settings in a separate file - this means that assembly settings are confined to a single file, but it also means each entry point must have its own copy of assemblies and their settings in our case. On the other hand, with .NET you need to merge settings into a single file per entry point, but you can easily keep multiple entry points in a single release folder. – vgru Nov 03 '15 at 13:03