benoitr
benoitr

Reputation: 6105

Is my context too coupled with my repository?

Learning Entity Framework, Repositories and IOC, what I am trying to achieve is to implement the repository pattern in order to change my data source provider.

Please see this example from an open-source project:

namespace MyApp.Domain.Interfaces
{
    public interface IEFContext : IDisposable
    {
    }
}

EFContext (implementing IEFContext)

namespace MyApp.Data.Context
{
    public class EFContext : DbContext, IEFContext
    {
        public const string DBConnectionName = @"MyDBName";

        public EFContext() : base(DBConnectionName)      
        {
        }

        public DbSet<Member> Member { get; set; }
    }
}

IMemberRepository

namespace MyApp.Domain.Interfaces.Repositories
{
    public interface IMemberRepository
    {
        Member GetUser(string username);
    }
}

MemberRepository (implementing IMemberRepository)

namespace MyApp.Data.Repositories
{
    public class MemberRepository : IMemberRepository
    {
        private readonly EFContext _context;

        public MemberRepository(IEFContext context)
        {
            _context = context as EFContext;
        }

        // update
        public Member GetUser(string username)
        {
            return _context.Member.SingleOrDefault(name => name.UserName.ToUpper().Contains(username.ToUpper()));
        }
    }
}

and this is my application console using Unity

Console App

namespace MyApp.ConsoleApp
{
    public class Program
    {
        static void Main(string[] args)
        {
            var container = new UnityContainer();
            container.RegisterType<IEFContext, EFContext>();
            container.RegisterType<IMemberRepository, MemberRepository>();
            var repository = container.Resolve<MemberRepository>();
            var user = repository.GetUser("johnDoe");
            Console.WriteLine(user.Email);
            Console.ReadLine();
        }
    }
}

My question is the following:

if I decide to add a new context, such as:

namespace MyApp.Data.Context
{
    public class EFContext2 : DbContext, IEFContext
    {
        public const string DBConnectionName = @"MyNewDBName";

        public EFContext2() : base(DBConnectionName)      
        {
        }

         public DbSet<Member> Member { get; set; }
    }
}

I should have only to change the dependency like:

namespace MyApp.ConsoleApp
{
    public class Program
    {
        static void Main(string[] args)
        {
            ...
            container.RegisterType<IEFContext, EFContext2>();
            ...         
        }
    }
}

but in this example my MemberRepository is loosely-coupled with my first context. So if I have to change my Context I must also change my repository.

I would be please to have your point of view on this. Kind regards

Upvotes: 0

Views: 413

Answers (1)

Erre Efe
Erre Efe

Reputation: 15557

but in this example my MemberRepository is loosely-coupled with my first context. So if I have to change my Context I must also change my repository.

Yes it is, but it's a one line change from:

private readonly EFContext _context;  

to

private readonly IEFContext _context;  

And not casting on your constructor:

public MemberRepository(IEFContext context)            
{                
    _context = context;            
}

Now, you can inject any kind of concrete context that implements IEFContext.

Just think about this: What you're trying to do with dependency injection is, well, inject dependencies. But, how do you do that? Well, you do that by generalizating those dependencies. I mean, you want to be able to use different context, then you use an interface IEFContext instead of a concrete context. That's why your constructor expects an interface.

But, that's the first part, know, the problem with your code is that when you say

_context = context as EFContext;

You're downcasting the interface and saying: This interface is an EFContext. You're loosing the generality. Know when you try to inject a, say, SQLContext, you'll not be able since although it's and IEFContext it isn't an EFContext. That's why you remove the as EFContext part and just let

_context = context;

Now as for the second part, being general and expecting and receiving an interface is just the first part. You now need to tell the compiler that you're expecting a generic context IEFContext, but you need also tell him that no matter which context you receive you should be able to, say, GetUsers. That's where your interface comes into play. You know declare the property public Member GetUser{} within your interface forcing and guaranteeing then that no matter which context arrive you'll be able to get your users.

I hope this to be a little simple to understand. Good luck.

Upvotes: 5

Related Questions