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
}
I am creating the WebApplication, with many layers (for now important are Model, Repository, BusinessLayer)
Having ClassService, ClassRepository and StudentService, StudentRepository, should ClassServiceMethod call methods from StudentService or StudentRepository?
Please provide as many arguments or additional links/blogs/informations as possible :)
Thanks in advance.
Here is my example code, some generics are added. The question is about GetClassAndBestStudent method:
Services - Business Layer
public class ClassService : BaseService<Class>, IClassService
{
IClassRepository classRepository; // Resolved by IoC, will be injected to BaseService
IStudentRepository studentRepository;
IStudentService studentService;
public virtual Class GetClassWithHighestNotes() { ... } // Do some stuff and call classRepository.GetClassWithHighestNotes()
public virtual Teacher GetTeachersByClass(int classId) { ... } // Do some stuff and call classRepository.GetTeachersByClass()
public virtual GetClassAndBestStudent(int classId)
{
// Question here: Which call is valid?
var best = studentRepository.GetStudentWithHighestNotes()
var best = studentService.GetStudentWithHighestNotes();
}
}
public class StudentService : BaseService<Student>, IStudentService
{
IStudentRepository studentRepository; // Resolved by IoC, will be injected to BaseService
public virtual IEnumerable<Student> GetStudentsByClass(int classId) { ... } // Do some stuff and call studentRepository.GetStudentsByClass()
public virtual Student GetStudentWithHighestNotes() { ... } // Do some stuff and call studentRepository.GetStudentWithHighestNotes()
}
// Abstract, generic CRUD service
public abstract class BaseService<T> : IBaseService<T> where T : MyBase
{
IRepository<T> repository;
public virtual IEnumerable<T> GetAll() { ... } // Do some stuff and call repository.GetAll()
public virtual T GetById(int id) { ... } // Do some stuff and call repository.GetById()
public virtual T Insert(T entity) { ... } // Do some stuff and call repository.Insert()
public virtual T Update(T entity) { ... } // Do some stuff and call repository.Update()
public virtual bool Delete(T entity) { ... } // Do some stuff and call repository.Delete()
public virtual bool Delete(int id) { ... } // Do some stuff and call repository.Delete()
}
Repositories - Data Layer
public class ClassRepository : BaseRepository<Class>, IClassRepository
{
public virtual Class GetClassWithHighestNotes() { ... }
public virtual Teacher GetTeachersByClass(int classId) { ... }
}
public class StudentRepository: BaseRepository<Student> IStudentRepository
{
public virtual IEnumerable<Student> GetStudentsByClass(int classId) { ... }
public virtual Student GetStudentWithHighestNotes() { ... }
}
// Abstract, generic CRUD repository
public abstract class BaseRepository<T> : IRepository<T> where T : MyBase
{
public virtual IEnumerable<T> GetAll() { ... }
public virtual T GetById(int id) { ... }
public virtual T Insert(T entity) { ... }
public virtual T Update(T entity) { ... }
public virtual bool Delete(T entity) { ... }
public virtual bool Delete(int id) { ... }
}
The best practice is calling StudentService from ClassServiceMethod. If the implementation in StudentRepository changes in the future, you can create another repository method like StudentRepositoryNew and utilize the same StudentService.
I am using EF Core. I am using DbContext.Set() method but it is giving me the error - "Cannot create a DbSet for 'MediaDate' because this type is not included in the model for the context.'"
Below is my code:
var context = new GoldentaurusContext();
DbSet<MediaDate> set = context.Set<MediaDate>();
mediaDateList = set.FromSql("[dbo].[sp_GetMediaDate]")
.Select(x => new SelectListItem { Text = x.DateText, Value = x.DateValue })
.ToList();
The MediaDate class:
public class MediaDate
{
public string DateText { get; set; }
public string DateValue { get; set; }
}
Why it is requiring me to add the MediaDate class to the DbContext class?
Please help what I am doing wrong?
Your DB Context class should be like below.
public partial class DatabaseContext : DbContext
{
public DatabaseContext (string ConnectionString) : base(new DbContextOptionsBuilder().UseSqlServer(ConnectionString).Options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Query<MediaData>();
}
Add your models in the DatabaseContext using model builder.
This is how I have resolved this isssue
For EF in DotNet Core 3.1+ make sure you add your non-table entity to the OnModelCreating override on your DbContext and call .HasNoKey() on the fluent API. This will allow you to call a stored procedure using DataContext.Set<MediaData>().FromSqlRaw("dbo.MyStoredProc") and return your entity list.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MediaData>().HasNoKey();
}
A simple DatabaseContext would look like this:
using YourProject.Model;
using System.Data.Entity;
namespace YourProject.Data
{
public class DatabaseContext : DbContext
{
public DatabaseContext() :
base("name=YourDatabase")
{
}
public DbSet<MediaData> MediaDates{ get; set; }
}
}
You always need to include your models in the DatabaseContext to create the DbSets. Make sure you've declared the right namespaces and imported the right ones.
First, you should introduce your model (MediaDate) to DbContext.
add a DbSet<MediaDate> property to your context:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<MediaDate> MediaDates { get; set; }
}
I have base class for my services. Attribute OperationBehavior doesn't apply if it defined in base class and I override method in derived class. Of cause, I can duplicate code, but maybe there is other way...
[ServiceContract]
public interface IMyService
{
[OperationContract]
void DoWork();
}
public class MyServiceBase
{
[OperationBehavior(TransactionScopeRequired = true)]
public virtual void DoWork()
{
}
}
public class MyService : MyServiceBase, IMyService
{
public override void DoWork()
{
//No Transaction, because attribute OperationBehavior doesn't apply.
}
}
You will need to do something like the following:
public class MyServiceBase
{
[OperationBehavior(TransactionScopeRequired = true)]
public void DoWork()
{
DoWorkImpl();
}
protected virtual DoWorkImpl()
{
}
}
public class MyService : MyServiceBase, IMyService
{
protected override void DoWorkImpl()
{
//Should have a Tx here now
}
}
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)