Replacing IUnitOfWork with a Factory Pattern
Bad idea...?

Generally, I belong to the camp thinking generic repositories are a bad idea:


The abstraction will be leaky at best.
If you really need a repository per type, why not let the client new it up directly?
Except from basic CRUD, there is no generic functionality, so a generic interface will reduce your options.


The abstractions doesn't seem right. I know it follows the usual conventions and examples given on EF, but do you want the database to dictate the shape of your domain objects?

If you split the responsibilities between domain logic and persistence, it may be clearer what to do. A split like that will also open new possibilities on how to implement and optimize.

Here are some ideas:

Handle different responsibilities in different places

Given an imaginary customer and two use cases:


Upgrade to gold customer.
Get a list of gold customers.


Those are different responsibilities which can be expressed like:

public class Responsibilities
{
    public void UpdateCustomer()
    {
        var repository = new Repository();
        var customer = repository.GetCustomerById(42);

        customer.UpgradeToGold();

        repository.Save(customer);
    }

    public void QueryGoldCustomers()
    {
        var query = new CustomerQuery();
        var goldCustomers = query.GetGoldCustomers();
    }
}


Repository and state management

This is a naive implementation; it will only work with customers already stored in the database, plus it generates multiple calls to the database. But the implementation is separated from everything else, so we are free to optimize the database access when it starts to be a problem.

I haven't used EF in a while, so the Save method may be way off.

public class Repository
{
    readonly Database context = new Database();

    public Customer GetCustomerById(int id)
    {
        var state = context.CustomerState.Single(x =&gt; x.Id == id);
        var address = context.Addresses.Single(x =&gt; x.Id == state.AddressId);
        var snapshot = new Customer.Snapshot
            {
                State = state,
                Address = address
            };

        return new Customer(snapshot);
    }

    public void Save(Customer customer)
    {
        var provider = (IProvideSnapshot&lt;Customer.Snapshot&gt;) customer;
        var snapshot = provider.GetSnapshot();

        context.Addresses.Attach(snapshot.Address);
        context.CustomerState.Attach(snapshot.State);
        context.SaveChanges();
    }
}


Isolated queries

Same thing here; there are better ways to implement the query. Note that this query is only meaningful inside of the domain boundary, like when you want to send special offers to all gold customers. 

UI views, reports and such would probably benefit from using dtos and not the domain object directly.

If it turns out to be hard to generate efficient queries with EF, we are free to select other tools, even inline SQL.

public class CustomerQuery
{
    readonly Database context = new Database();

    public IEnumerable&lt;Customer&gt; GetGoldCustomers()
    {
        var states = context.CustomerState.Where(state =&gt; state.IsGold);
        var addressIds = states.Select(state =&gt; state.AddressId);
        var addresses = context.Addresses.Where(address =&gt; addressIds.Contains(address.Id));

        return states
            .Select(state =&gt; new Customer.Snapshot
                {
                    State = state,
                    Address = addresses.Single(address =&gt; address.Id == state.AddressId)
                })
            .Select(snapshot =&gt; new Customer(snapshot))
            .ToList();
    }
}


Other classes for completeness

In this example, the shape of the domain class, the Customer, is not dictated by the database.

The shape of the Snapshot is, though. But that could be a good thing, because the object-to-database impedance mismatch is lower.

public class Customer : IProvideSnapshot&lt;Customer.Snapshot&gt;
{
    readonly Snapshot snapshot;

    public Customer(Snapshot snapshot)
    {
        this.snapshot = snapshot;
    }

    public void UpgradeToGold()
    {
        snapshot.State.IsGold = true;
    }

    Snapshot IProvideSnapshot&lt;Snapshot&gt;.GetSnapshot()
    {
        return snapshot;
    }

    public class Snapshot
    {
        public CustomerState State { get; set; }
        public Address Address { get; set; }
    }
}

public interface IProvideSnapshot&lt;out T&gt;
{
    T GetSnapshot();
}

public class Database : DbContext
{
    public DbSet&lt;CustomerState&gt; CustomerState { get; set; }
    public DbSet&lt;Address&gt; Addresses { get; set; }
}

public class CustomerState
{
    public int Id { get; set; }
    public int AddressId { get; set; }
    public bool IsGold { get; set; }
}

public class Address
{
    public int Id { get; set; }
}