Please note that I only have minor experience with the .NET framework and that this answer only relates to the architecture part of your question.
As far as I understood it, you are basically applying the following architectural patterns in your application:
Layers: It seems you have a persistence layer (Repository Pattern), a business logic layer and a view layer.
Model-View-Controller: This pattern is applied in the view layer using ASP.NET MVC.
I guess the question is: what does a BLL look like and what does the MVC controller that uses it look like?
First of all, this architecture is intended for large-scale, enterprise applications as Robert Harvey mentioned in his comment to your question. What is characteristic for such systems is that the domain logic (which you encapsulated in the business logic layer) should be accessible through various interfaces.
Consider Twitter for instance - Twitter could have a business logic layer which provides services for registering and validating users, posting tweets and much more. On top of this business logic layer, multiple other components could exist in the view layer, e.g. a web interface and web service component. Since all business logic is encapsulated in the business logic layer, you can follow the DRY principle and improve maintainability and other quality attributes.
To come back to your question - you should encapsulate business logic and data access in the BLL. The controller (think MVC) should make use of the BLL and should not have direct access to the database. Consider the view layer as an interface to your application.
So I feel like if I have a BLL, I should be passing it the IUnitOfWork, so that it can use that to create the IRepository instances that it needs. But how will the BLL (separate DLL from the front end) 'know' what implementation of IRepository to build?
The BLL should know which repository (data source?) it uses. The layers above shouldn't control this. Again, consider the view layer only as an interface to your business logic.
If you don't need the overhead of a business logic layer, you may also choose to use it solely for data access in form of data access objects.
I hope this helped clarifying the various components' responsibilities.
Generic repository is even useless (and IMHO also bad) for Entity Framework. It doesn't bring any additional value to what is already provided by IDbSet<T>
(which is btw. generic repository).
As you have already found the argument that generic repository can be replaced by implementation for other data access technology is pretty weak because it can demand writing your own Linq provider.
The second common argument about simplified unit testing is also wrong because mocking repository / set with in-memory data storage replaces Linq provider with another one which has different capabilities. Linq-to-entities provider supports only subset of Linq features - it even doesn't support all methods available on IQueryable<T>
interface. Sharing expression trees between data access layer and business logic layers prevents any faking of data access layer - query logic must be separated.
If you want to have strong "generic" abstraction you must involve other patterns as well. In this case you need to use abstract query language which can be translated by repository to specific query language supported by used data access layer. This is handled by Specification pattern. Linq on IQueryable
is specification (but the translation requires provider - or some custom visitor translating expression tree into query) but you can define your own simplified version and use it. For example NHibernate uses Criteria API. Still the simplest way is using specific repository with specific methods. This way is the simplest to implement, simplest to test and simplest to fake in unit tests because the query logic is completely hidden and separated behind the abstraction.
Best Answer
Returning IQueryable will definitely afford more flexibility to the consumers of the repository. It puts the responsibility of narrowing results off to the client, which naturally can both be a benefit and a crutch.
On the good side, you won't need to be creating tons of repository methods (at least on this layer) — GetAllActiveItems, GetAllNonActiveItems, etc — to get the data you want. Depending on your preference, again, this could be good or bad. You will (/should) need to define behavioral contracts which your implementations adhere to, but where that goes is up to you.
So you could put the gritty retrieval logic outside the repository and let it be used however the user wants. So exposing IQueryable gives the most flexibility and allows for efficient querying as opposed to in-memory filtering, etc, and could reduce the need for making a ton of specific data fetching methods.
On the other hand, now you have given your users a shotgun. They can do things which you may not have intended (overusing .include(), doing heavy heavy queries and doing in-memory filtering in their respective implementations, etc), which would basically side-step the layering and behavioral controls because you have given full access.
So depending on the team, their experience, the size of the app, the overall layering and architecture … it depends :-\