How to implement Service inherit from generic repository? - asp.net-core

I work on asp.net core mvc blazor application , I have issue I can't implement service inherit from generic repository .
meaning how to inherit from IRepository to get functions below on class server names service :
Insert
Update
GetById
GetList
GetListAsync
Interface generic repository
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
public interface IRepository<TEntity> where TEntity : class
{
Task<int> Count(Expression<Func<TEntity, bool>> where);
TEntity GetByID(object id);
TEntity Insert(TEntity entity);
void Update(TEntity entityToUpdate);
Task<ICollection<TType>> Get<TType>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TType>> select) where TType : class;
Task<bool> Any(Expression<Func<TEntity, bool>> where);
TEntity GetFirst(Expression<Func<TEntity, bool>> where);
TEntity Single(Expression<Func<TEntity, bool>> where);
Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> where);
List<TEntity> GetList(Expression<Func<TEntity, bool>> where);
Task<bool> UpdateBasedOnCondition(Expression<Func<TEntity, bool>> where, Action<TEntity> select);
void Save();
}
class that implement interface as below :
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using UC.AppRepository.Core;
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
internal AppsRepositoryDBContext _context;
internal DbSet<TEntity> dbSet;
public BaseRepository(AppsRepositoryDBContext context)
{
_context = context;
this.dbSet = _context.Set<TEntity>();
}
public virtual TEntity GetByID(object id)
{
return dbSet.Find(id);
}
public virtual TEntity Insert(TEntity entity)
{
var result = dbSet.AddAsync(entity).Result.Entity;
Save();
return result;
}
public virtual void Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual async Task<bool> UpdateBasedOnCondition(Expression<Func<TEntity, bool>> where, Action<TEntity> select)
{
try
{
var ListOfRecord = await dbSet.Where(where).ToListAsync();
if (null != ListOfRecord && ListOfRecord.Count > 0)
{
ListOfRecord.ForEach(select);
// Save();
await _context.SaveChangesAsync();
return true;
}
return false;
}
catch (Exception ex)
{
return false;
throw;
}
}
public async Task<ICollection<TType>> Get<TType>(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TType>> select) where TType : class
{
if(where == null)
{
return await dbSet.Select(select).ToListAsync();
}
return await dbSet.Where(where).Select(select).ToListAsync();
}
public async Task<int> Count(Expression<Func<TEntity, bool>> where)
{
return await dbSet.Where(where).CountAsync();
}
public async virtual Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> where)
{
// var test = dbSet.Where(where).ToList();
return await dbSet.Where(where).ToListAsync();
}
public virtual List<TEntity> GetList(Expression<Func<TEntity, bool>> where)
{
return dbSet.Where(where).ToList();
}
public async Task<bool> Any(Expression<Func<TEntity, bool>> where)
{
return await dbSet.AnyAsync(where);
}
public TEntity GetFirst(Expression<Func<TEntity, bool>> where)
{
return dbSet.FirstOrDefault(where);
}
public TEntity Single(Expression<Func<TEntity, bool>> where)
{
return dbSet.Single(where);
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (Exception ex)
{
throw;
}
}
}
so I have class ServerNameService and class interface IserverNamesService
I need to implement insert,update,selectById,selectall functions for server name models
from base repository
public class ServerNameService:IRepository
{
// what i write here
}
public interface IserverNamesService:IRepository
{
// what i write here
}
public class ServerNames
{
[Key]
public int ServerID { get; set; }
public string Server_Name{ get; set; }
public string Server_Type { get; set; }
public string Operating_System { get; set; }
public string Version { get; set; }
public bool IsActive { get; set; }
}

I have issue I can't implement service inherit from generic repository.I have class ServerNameService and class interface IserverNamesService I need to implement insert,update,selectById,selectall functions for server name models from base repository
Well, to directly answer your question, to implement your ServerNameService which derived from IRepository that would would be as following:
IserverNamesService:
public interface IserverNamesService : IRepository<ServerNames>
{
}
Note: Keep it empty because we will use of IRepository and BaseRepository in ServerNamesService class to implement its members.
ServerNamesService:
public class ServerNamesService : BaseRepository<ServerNames>, IserverNamesService
{
public ServerNamesService(ApplicationDbContext context) : base(context)
{
}
public override ServerNames GetByID(object id)
{
return _context.ServerNames.Where(sn => sn.ServerID == (int)id).FirstOrDefault();
}
}
Controller:
[Route("api/[controller]")]
[ApiController]
public class ServerNamesServiceController : ControllerBase
{
private readonly IserverNamesService _serverNamesService;
public ServerNamesServiceController(IserverNamesService namesService)
{
_serverNamesService = namesService;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(object id)
{
var item = _serverNamesService.GetByID(id);
if (item == null)
return NotFound();
return Ok(item);
}
}
Program.cs:
builder.Services.AddScoped<IserverNamesService, ServerNamesService>();
Note: Register your ServerNamesService class in your program.cs
UnitOfWork Pattern Implementation:
As long your application would continue evolving and there would be ton of service class, in that scenario, you have to introduce lot of service in your controller. But if you would like to handle those smoothly, you could use UnitOfWork pattern which would contain all of your service together.
Interface:
public interface IUnitOfWork
{
IserverNamesService ServerNamesService { get; }
Task CompleteAsync();
}
Implementation:
public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly ApplicationDbContext _context;
public IserverNamesService ServerNamesService { get; private set; }
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
ServerNamesService = new ServerNamesService(context);
}
public async Task CompleteAsync()
{
await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}
Controller:
[Route("api/[controller]")]
[ApiController]
public class ServerNamesServiceController : ControllerBase
{
private readonly IUnitOfWork _unitOfWork;
public ServerNamesServiceController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(object id)
{
var item = _unitOfWork.ServerNamesService.GetByID(id);
if (item == null)
return NotFound();
return Ok(item);
}
}
Output:
Note: If you would like to know more details on repository pattern you could check our official document here and working sample here.

You should pass your Entity as generic type to IRepository in IServerNamesService, and then inheritance ServerNameService from IServerNamesService.
public interface IServerNamesService:IRepository<ServerNames>
{
// your methods interface
}
public class ServerNameService:IServerNamesService{
private readonly IRepository<ServerNames> _repository;
public class ServerNameService(IRepository<ServerNames> repository)
{
_repository = repository;
}
//body of your methods
}

Related

Cascading DropDownList in .NET Core

I am trying to use the tutorial linked below but adapting it to my SQL DB for States, Districts, Schools tables that I already have in place. I am new to .NET Core MVC and do not understand the error nor how to debug it. Any help appreciated.
Cascading DropDownList In .NET Core
Error:
Microsoft.Data.SqlClient.SqlException: 'Invalid object name 'State'.'
This exception was originally thrown at this call stack:
[External Code]
CascadingExample.Controllers.HomeController.Index() in HomeController.cs
[External Code]
using CascadingExample.Entities;
using CascadingExample.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace CascadingExample.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly MyDBContent _con;
public HomeController(ILogger<HomeController> logger, MyDBContent con)
{
_logger = logger;
_con = con;
}
public IActionResult Index()
{
ViewBag.StateList = _con.State.ToList();
return View();
}
public JsonResult GetDistrictByStateID(int statedID)
{
var data = _con.District.Where(x => x.StateID == statedID).ToList();
return Json(data);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
The error means the table named 'State' has not created or the Database you are referring has not created
I checked the dbcontext in your tutorial,and it will not create the table"State".
public class MyDBContent:DbContext
{
private IConfigurationRoot _config;
public MyDBContent(IConfigurationRoot config, DbContextOptions options) : base(options)
{
_config = config;
}
public DbSet<Category> Category { get; set; }
public DbSet<SubCategory> SubCategory { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["ConnectionStrings:DefaultConnection"]);//connection string get by appsetting.json
}
}
You need to add these codes:
public DbSet<State> State { get; set; }

Invalid column name 'EmailAddress' when using generic repository, but works fine with context

Getting the mentioned error when trying to do a GetAll on accounts. It works fine if I go directly to the dbcontext, but gives me the error if I try to work with the repo. I have about 20 others that use just the generic repo and are working great. Because I have additional actions for Accounts, I have created its own repository that implements the generic. I also have several others that work like this and have no problem. The problem is specific to the accounts.
Database of course does have the EmailAddress column, since I can return it if I use dbcontext from the controller instead of the repo.
Any help would be much appreciated.
AccountsController:
public class AccountsController : ControllerBase
{
private readonly AccountRepository _repo;
public AccountsController(DatabaseContext context)
{
_repo = new AccountRepository(context);
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Account>>> GetAccount()
{
// return _context.Account.ToListAsync(); works fine if _context is defined
var accounts = await _repo.GetAll();
if (accounts == null)
return NoContent();
return Ok(accounts); // Gives invalid column error
}
[HttpGet("getaccount")]
public async Task<ActionResult<Account>> GetCurrentAccount()
{
var account = await _repo.GetCurrentAccount(HttpContext.User.Identity.Name);
if (account == null)
{
return NotFound();
}
return account; // Works fine
}
}
Account:
public partial class Account
{
public string Name { get; set; }
public string RefId { get; set; }
public string Position { get; set; }
public bool IsActive { get; set; }
public string EmailAddress { get; set; }
[Key]
public string UserId { get; set; }
}
IAccountRepository:
public interface IAccountRepository : IRepository<Account>
{
Task<Account> GetCurrentAccount(string emailAddress);
}
AccountRepository:
public class AccountRepository : Repository<Account>, IAccountRepository
{
private DatabaseContext _context;
public AccountRepository(DatabaseContext context)
{
_context = context;
}
public async Task<Account> GetCurrentAccount(string emailAddress)
{
var account = await _context.Account
.Where(a => a.EmailAddress == emailAddress)
.FirstOrDefaultAsync();
return account; // this works just fine, and returns with EmailAddress
}
}
IRepository (generic):
public interface IRepository<T>
{
Task<IEnumerable<T>> GetAll();
Task<T> GetById(object id);
void Add(T entity);
void Update(T entity);
void Delete(T entity);
Task<bool> Save();
}
Repository (generic):
public class Repository<T> : IRepository<T> where T : class
{
private DatabaseContext _context;
public Repository()
{
_context = new DatabaseContext();
}
public Repository(DatabaseContext context)
{
_context = context;
}
public void Add(T obj)
{
_context.Set<T>().Add(obj);
}
public void Delete(T entity)
{
_context.Set<T>().Remove(entity);
}
public async Task<IEnumerable<T>> GetAll()
{
return await _context.Set<T>().ToListAsync();
}
public async Task<T> GetById(object id)
{
return await _context.Set<T>().FindAsync(id);
}
public void Update(T obj)
{
_context.Set<T>().Update(obj);
}
public async Task<bool> Save()
{
try
{
await _context.SaveChangesAsync();
}
catch (Exception)
{
return false;
}
return true;
}
}
EDIT
I should mention that EmailAddress was added to the database via EF migration.

Autofac and ASP .Net MVC 4 Web API

I am using Autofac for IoC in my ASP .Net MVC 4 project. Autofac is having some trouble initializing the repository and passing it to the API Controller.
I am sure I am missing something in my configuration.
Here is the error I get when I navigate to: https://localhost:44305/api/integration
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'
on type 'EL.Web.Controllers.API.IntegrationController' can be invoked with
the available services and parameters: Cannot resolve parameter
'EL.Web.Infrastructure.IRepository`1[EL.Web.Models.Integration] repository' of
constructor 'Void .ctor(EL.Web.Infrastructure.IRepository`1[EL.Web.Models.Integration])'.
</ExceptionMessage>
<ExceptionType>Autofac.Core.DependencyResolutionException</ExceptionType>
<StackTrace>
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</Error>
Here are some relevant bits of code:
IoC Bootstrapper:
public static class Bootstrapper
{
public static void Initialize()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.Register(x => new SharePointContext(HttpContext.Current.Request)).As<ISharePointContext>().SingleInstance();
builder.RegisterType<SharePointRepository<IEntity>>().As<IRepository<IEntity>>();
builder.RegisterType<SharePointContextFilter>().SingleInstance();
builder.RegisterFilterProvider();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
}
}
IRepository:
public interface IRepository<T>
{
void Add(T entity);
void Delete(int id);
IEnumerable<T> Find(Expression<Func<T, bool>> filter = null);
void Update(int id, T entity);
}
SharePointRepository:
internal class SharePointRepository<T> : IRepository<T> where T : IEntity
{
private readonly ISharePointContext _context;
private readonly string _listName;
internal SharePointRepository(ISharePointContext context)
{
_context = context;
object[] attributes = typeof (T).GetCustomAttributes(typeof (SharePointListAttribute), false);
if (!attributes.Any())
{
throw new Exception("No associated SharePoint list defined for " + typeof (T));
}
_listName = ((SharePointListAttribute) attributes[0]).ListName;
}
public void Add(T entity)
{
throw new NotImplementedException();
}
public void Delete(int id)
{
throw new NotImplementedException();
}
public IEnumerable<T> Find(Expression<Func<T, bool>> filter)
{
throw new NotImplementedException();
}
public void Update(int id, T entity)
{
throw new NotImplementedException();
}
}
IntegrationController:
public class IntegrationController : ApiController
{
private readonly IRepository<Integration> _repository;
public IntegrationController(IRepository<Integration> repository)
{
_repository = repository;
}
public void Delete(Guid integrationId)
{
_repository.Delete(Get(integrationId).Id);
}
public IEnumerable<Integration> Get()
{
return _repository.Find();
}
public Integration Get(Guid integrationId)
{
return _repository.Find(i => i.IntegrationId == integrationId).FirstOrDefault();
}
public void Post([FromBody] Integration integration)
{
_repository.Add(integration);
}
public void Put(Guid integrationId, [FromBody] Integration integration)
{
_repository.Update(Get(integrationId).Id, integration);
}
}
IEntity:
internal interface IEntity
{
int Id { get; }
}
Entity:
public abstract class Entity : IEntity
{
protected Entity(int id)
{
Id = id;
}
public int Id { get; private set; }
}
Integration:
[SharePointList("Integrations")]
public class Integration : Entity
{
public Integration(int id) : base(id)
{
}
public string ApiUrl { get; set; }
public bool DeletionAllowed { get; set; }
public Guid IntegrationId { get; set; }
public string Key { get; set; }
public string List { get; set; }
public bool OutgoingAllowed { get; set; }
public string RemoteWeb { get; set; }
public string Web { get; set; }
}
You have registered your IRepository wrong. With the line:
builder.RegisterType<SharePointRepository<IEntity>>().As<IRepository<IEntity>>();
You told Autofac that whenever somebody will request an IRepository<IEntity> give them a SharePointRepository<IEntity>, but you are requesting a concrete IRepository<Integration> so you get an exception.
What you need is the open generic registration feature of Autofac. So change your registration to:
builder.RegisterGeneric(typeof(SharePointRepository<>))
.As(typeof(IRepository<>));
It will work as you would expect you when you ask for a IRepository<Integration> it will give a SharePointRepository<Integration>.
You also have a second unrelated problem: your SharePointRepository has only an internal constructor.
Autofac by default only looks for public constructors so you either change your constructor and class to public or you need to tell to Autofac to look for NonPublic constructors with the FindConstructorsWith method:
builder
.RegisterType<SharePointRepository<IEntity>>()
.FindConstructorsWith(
new DefaultConstructorFinder(type =>
type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)))
.As<IRepository<IEntity>>();

NHibernate repository not compile

I don't understand why the code below not compile (on the QueryOver line), I tried wih NHibernate 3.1 and 3.2
public interface IRepository<T> where T : class
{
IQueryable<T> FindAll<T>();
void Save(T obj);
}
public class RepositoryBase<T> : IRepository<T> where T : class
{
protected ISession _session = null;
public RepositoryBase(ISession session)
{
_session = session;
}
public void Save(T obj)
{
_session.Save(obj);
}
public IQueryable<T> FindAll<T>()
{
- return _session.QueryOver<T>().List<T>().AsQueryable();
}
}
Error :
You do not need <T> in FindAll declarations because they already declared at the class level. You may also be missing some using statements. And there is a dash ( - ) in the QueryOver line. Following should compile in .NET 3.5 project:
using System.Linq;
using NHibernate;
public interface IRepository<T> where T : class {
IQueryable<T> FindAll();
void Save(T obj);
}
public class RepositoryBase<T> : IRepository<T> where T : class {
protected ISession _session = null;
public RepositoryBase(ISession session) {
_session = session;
}
public void Save(T obj) {
_session.Save(obj);
}
public IQueryable<T> FindAll() {
return _session.QueryOver<T>().List<T>().AsQueryable();
}
}

How do I wrap Linq2NHibernate's .Fetch and .ThenFetch inside my abstract repository?

I'm using a generic repository that exposes an IQueryable<T> like this:
public IQueryable<T> AllEntities
{
get
{
return session.Query<T>();
}
}
I can query like this:
var results =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e).ToList();
However, if T has a parent and grandparent entity and I want to load them eagerly, I have to do this:
var results =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e)
.Fetch(x => x.Parent)
.ThenFetch(x => x.Grandparent)
.ToList();
This works, but .Fetch and .ThenFetch are both Linq2Nhibernate specific extension methods, which is causing two problems:
I have to include a using NHibernate.Linq; statement at the top of my file. However, at the point that I'm doing this query, it should be implementation agnostic.
When I try to unit test this, the .Fetch and .ThenFetch methods fail when executed against the IQueryable<T> that my mock repository provides.
How can I wrap these inside of my IRepository<T> interface, or inside of some generic extension methods?
Update:
So far all I've come up with is to add this to my repository interface:
IQueryable<T> EagerLoadParent<U>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression);
IQueryable<T> EagerLoadParent<U, V>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression,
Expression<Func<U, V>> grandparentExpression);
... and this to my NHibernate repository implementation:
public IQueryable<T> EagerLoadParent<U>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression)
{
return query
.Fetch(parentExpression);
}
public IQueryable<T> EagerLoadParent<U, V>(IQueryable<T> query,
Expression<Func<T, U>> parentExpression,
Expression<Func<U, V>> grandparentExpression)
{
return query
.Fetch(parentExpression)
.ThenFetch(grandparentExpression);
}
The consumer of this API now does this:
var query =
(from e in repository.AllEntities
where e.SomeProperty == "some value"
select e);
var results = repository
.EagerLoadParent(query, e => e.Parent, p => p.Grandparent)
.ToList();
But this lacks the nice extension method syntax I'd prefer. I'm looking for something closer to the .Fetch and .ThenFetch syntax.
After some investigation I think I have a recipe: simply follow closely NHibernate.Linq implementation in order to have your own implementation and avoid an explicit NHibernate.Linq dependency in your client code. You just need to reproduce very closely
NHibernate.Linq.EagerFetchingExtensionMethods class.
It takes an interface: IFetchRequest, a class FetchRequest implementing IFetchRequest and a static class EagerFetch implementing extension methods. This is a sort of clone of NHibernate.Linq.EagerFetchingExtensionMethods class.
Just define:
public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried> {}
which mimics NHibernate.Linq.INhFetchRequest<TQueried, TFetch>
then define an implementation:
public class FetchRequest<TQueried, TFetch> : IFetchRequest<TQueried, TFetch> {
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator(){
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return NhFetchRequest.ElementType; }
}
public System.Linq.Expressions.Expression Expression {
get { return NhFetchRequest.Expression; }
}
public IQueryProvider Provider {
get { return NhFetchRequest.Provider; }
}
#endregion
public FetchRequest(INhFetchRequest<TQueried, TFetch> nhFetchRequest){
NhFetchRequest = nhFetchRequest;
}
public INhFetchRequest<TQueried, TFetch> NhFetchRequest { get; private set; }
}
This one simply holds a nHibernate implementation and forwards every method to that member.
Finally:
public static class EagerFetch {
/*
replacing methods from NHibernate.Linq.EagerFetchingExtensionMethods
private static INhFetchRequest<TOriginating, TRelated> CreateFluentFetchRequest<TOriginating, TRelated>(MethodInfo currentFetchMethod, IQueryable<TOriginating> query, LambdaExpression relatedObjectSelector);
public static INhFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector);
public static INhFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector);
public static INhFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(this INhFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector);
public static INhFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(this INhFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector);
*/
public static IFetchRequest<TOriginating, TRelated> Fetch<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, TRelated>> relatedObjectSelector){
var fetch = EagerFetchingExtensionMethods.Fetch(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fetch);
}
public static IFetchRequest<TOriginating, TRelated> FetchMany<TOriginating, TRelated>(this IQueryable<TOriginating> query, Expression<Func<TOriginating, IEnumerable<TRelated>>> relatedObjectSelector){
var fecth = EagerFetchingExtensionMethods.FetchMany(query, relatedObjectSelector);
return new FetchRequest<TOriginating, TRelated>(fecth);
}
public static IFetchRequest<TQueried, TRelated> ThenFetch<TQueried, TFetch, TRelated>(this IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, TRelated>> relatedObjectSelector){
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetch(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public static IFetchRequest<TQueried, TRelated> ThenFetchMany<TQueried, TFetch, TRelated>(this IFetchRequest<TQueried, TFetch> query, Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector){
var impl = query as FetchRequest<TQueried, TFetch>;
var fetch = EagerFetchingExtensionMethods.ThenFetchMany(impl.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
}
Building on guido's answer, here's one which disconnects all NHibernate dependencies from the repository interface. Quite a bit of boiler plate though and probably not a good technique if you want to make use of a lot of NHibernate specific functionality; then referencing the NHibernate.dll might be more appropriate.
First the interfaces:
public interface IFetchableQueryable<TQueried> : IQueryable<TQueried> {
IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector);
}
public interface IFetchRequest<TQueried, TFetch> : IOrderedQueryable<TQueried> {
IFetchRequest<TQueried, TRelated> ThenFetch<TRelated>(Expression<Func<TFetch, TRelated>> relatedObjectSelector);
IFetchRequest<TQueried, TRelated> ThenFetchMany<TRelated>(Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector);
}
And then the implementation:
public class FetchableQueryable<TQueried> : IFetchableQueryable<TQueried> {
public FetchableQueryable(IQueryable<TQueried> query) {
this.Query = query;
}
public IQueryable<TQueried> Query { get; private set; }
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator() {
return this.Query.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return this.Query.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return this.Query.ElementType; }
}
public Expression Expression {
get { return this.Query.Expression; }
}
public IQueryProvider Provider {
get { return this.Query.Provider; }
}
#endregion
#region IFetchableQueryable<TQueried> Members
public IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector) {
return new FetchRequest<TQueried, TRelated>(this.Query.Fetch<TQueried, TRelated>(relatedObjectSelector));
}
public IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector) {
return new FetchRequest<TQueried, TRelated>(this.Query.FetchMany<TQueried, TRelated>(relatedObjectSelector));
}
#endregion
}
public class FetchRequest<TQueried, TFetch> : IFetchRequest<TQueried, TFetch> {
public FetchRequest(INhFetchRequest<TQueried, TFetch> nhFetchRequest) {
NhFetchRequest = nhFetchRequest;
}
public INhFetchRequest<TQueried, TFetch> NhFetchRequest { get; private set; }
#region IEnumerable<TQueried> Members
public IEnumerator<TQueried> GetEnumerator() {
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return NhFetchRequest.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType {
get { return NhFetchRequest.ElementType; }
}
public System.Linq.Expressions.Expression Expression {
get { return NhFetchRequest.Expression; }
}
public IQueryProvider Provider {
get { return NhFetchRequest.Provider; }
}
#endregion
#region IFetchRequest<TQueried,TFetch> Members
public IFetchRequest<TQueried, TRelated> Fetch<TRelated>(Expression<Func<TQueried, TRelated>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.Fetch(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public IFetchRequest<TQueried, TRelated> FetchMany<TRelated>(Expression<Func<TQueried, IEnumerable<TRelated>>> relatedObjectSelector) {
var fecth = EagerFetchingExtensionMethods.FetchMany(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fecth);
}
public IFetchRequest<TQueried, TRelated> ThenFetch<TRelated>(Expression<Func<TFetch, TRelated>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.ThenFetch(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
public IFetchRequest<TQueried, TRelated> ThenFetchMany<TRelated>(Expression<Func<TFetch, IEnumerable<TRelated>>> relatedObjectSelector) {
var fetch = EagerFetchingExtensionMethods.ThenFetchMany(this.NhFetchRequest, relatedObjectSelector);
return new FetchRequest<TQueried, TRelated>(fetch);
}
#endregion
}
What I've done to work around this is to create a public virtual function in my repository to EagerlyFetch my object. Then, in my unit tests I use that Stub instead that passes through everything except for my EagerlyFetch method, which just returns a list. Here is an example of what I've done:
public class PersistenceBroker
{
private ISession _session;
public IQueryable<T> Query<T>()
{
return Session.Query<T>();
}
.
.
.
}
public class PersonRepository : IPersonRepository
{
private PersistenceBroker _persistenceBroker;
public List<Person> PeopeWhoLiveIn(string city)
{
var people = _persistenceBroker.Query<Person>()
Where(x => x.City == city)l
return EagerlyFetch(people);
}
public virtual List<Person> EagerlyFetch(IQueryable<Person> people)
{
return people.Fetch(x => x.Mom)
.FetchMany(x => x.Children)
.ToList();
}
}
And then in my tests, I simply provide a PersonRepositoryStub:
public class PersonRepositoryStub : PersonRepository
{
public override List<Person> EagerlyFetch(IQueryable<Person> people)
{
return people.ToList();
}
}
This would be an alternative to some of the above answers (which I have not tried), but this works for me.
Cheers,
Levi
Alternatively wrap your test data IEnumerable in a stub that implements IFutureValue (NB this uses remotion http://relinq.codeplex.com/).
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate;
using Remotion.Linq;
namespace SomeNameSpaceNearYou
{
public class NhStubQueryable<TData> : QueryableBase<TData>, IEnumerable<TData>, IFutureValue<TData>
{
private readonly IEnumerable<TData> _enumerable;
public NhStubQueryable(IEnumerable<TData> enumerable)
: base(new NhStubQueryProvider())
{
_enumerable = enumerable;
}
/// <summary>
/// This constructor is called by Provider.CreateQuery().
/// </summary>
//public NhStubQueryable(NhStubQueryProvider<TData> provider, Expression expression)
public NhStubQueryable(NhStubQueryProvider provider, Expression expression)
: base(provider, expression)
{
if (provider == null)
{
throw new ArgumentNullException("provider");
}
if (expression == null)
{
throw new ArgumentNullException("expression");
}
if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
{
throw new ArgumentOutOfRangeException("expression");
}
}
#endregion
#region Enumerators
IEnumerator<TData> IEnumerable<TData>.GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
public new IEnumerator<TData> GetEnumerator()
{
if (_enumerable != null)
return _enumerable.GetEnumerator();
return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
}
#endregion
public IEnumerable Enumerable { get { return _enumerable; } }
public TData Value { get { return this.FirstOrDefault(); } }
}
public class NhStubFutureValue<TData> : IFutureValue<TData>
{
public NhStubFutureValue(TData value)
{
Value = value;
}
public TData Value { get; private set; }
}
}
(I didn't write this, credit to a colleague far more skilled than myself)