Search code examples
c#nuget.net-assemblyprojects-and-solutionsasp.net5

Why can I reference an assembly in an ASP.NET 5 project that doesn't include a reference to that assembly?


I have a DNX 4.5.1 ASP.NET 5 project called Foo and a class library project called Bar. Foo has a reference to Bar, and Bar references a nuget package called Baz.

From within Foo, I can use types defined in Baz, despite that nuget package not being referenced by Foo. It seems like Foo can access any type referenced by Bar. Why is that? Doesn't this completely break the concept of abstraction?


Solution

  • No, it doesn't break abstraction as long as you install the Baz NuGet package in Foo as well, since you are declaring a dependency. The simple fact that the Baz library has public classes means any exe or dll file that references it can use those classes.

    Since Foo requires Bar, and Bar requires Baz, the Baz.dll file should appear in the bin/ output folder when compiling Foo, since Foo needs classes in Bar. This means a simple using directive at the top of a file in Foo causes the CLR to import the Baz.dll file.

    Now if Foo needs to directly use classes defined in Baz, I would recommend installing the Baz project as a dependency to Foo. It's OK for two libraries to need the same third party library. You just need to make sure both Foo and Bar need the same version.

    If Foo and Bar need different versions, then I would recommend creating proxy classes or interfaces in Bar to encapsulate the behavior you need from Baz, so that the Foo project doesn't directly need to know about Baz.

    Let's do a concrete example.

    Example: A Blog web application using NHibernate

    Without diving too deep or making any judgements about "best practices" this example revolves around an ASP.NET MVC project for a blog. It has a class library for the data access and business classes. The library installs the NHibernate NuGet packages.

    • Foo is the "Blog.Mvc" project, an ASP.NET MVC application

    • Bar is the "Blog.Core" project, a class library containing repository classes and interfaces, as well as the "Domain Models" or business classes.

    • Baz is NHibernate

    So the question becomes:

    Should the "Blog.Mvc" application directly uses classes from NHiberate?

    No. The "Blog.Core" project should provide a layer of abstraction using repository interfaces:

    Blog.Core

    First, the "Domain Model" or business class:

    public class Blog
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
    }
    

    The public interface for the blog repository:

    public interface IBlogRepository
    {
        Blog Find(int id);
    }
    

    Now the implementation of the interface, referencing classes and interfaces directly related to NHibernate:

    public class BlogRepository : IBlogRepository
    {
        private NHibernate.ISession session;
    
        private NHibernate.ISession Session
        {
            get
            {
                if (session == null)
                    session = NHibernateSessionHelper.GetSession();
    
                return session;
            }
        }
    
        public Blog Find(int id)
        {
            return Session.Get<Blog>(id);
        }
    }
    

    The MVC project controllers use the repositories through their interface:

    public class BlogsController : Controller
    {
        public BlogsController(IBlogRepository repository)
        {
            this.repository = repository;
        }
    
        private IBlogRepository repository;
    
        public ActionResult Edit(int id)
        {
            Blog blog = repository.Find(id);
    
            if (blog == null)
                return HttpNotFound();
    
            BlogForm viewModel = new BlogForm(blog);
    
            return View(viewModel);
        }
    }
    

    While this might not be your exact situation, the Separation of Concerns outlined in this example makes your question moot.

    If Foo requires classes in Baz, declare that as a dependency. If not, don't use classes declared in Baz in the Foo project. If Foo requires functionality from Bar which delegates some behavior to Baz, this doesn't break abstraction as long as Foo doesn't get passed any objects for classes defined in Baz. Bar needs to create it's own classes as either Data Transfer Objects, proxy classes, or some sort of object that implements an interface defined in Bar for consumption in Foo.