I'm with an irritating problem. It might be something stupid, but I couldn't find out.
I'm using Linq to NHibernate, and I would like to count how many items are there in a repository. Here is a very simplified definition of my repository, with the code that matters:
public class Repository {
private ISession session;
/* ... */
public virtual IQueryable<Product> GetAll() {
return session.Linq<Product>();
}
}
All the relevant code in the end of the question.
Then, to count the items on my repository, I do something like:
var total = productRepository.GetAll().Count();
The problem is that total is 0. Always. However there are items in the repository. Furthermore, I can .Get(id) any of them.
My NHibernate log shows that the following query was executed:
SELECT count(*) as y0_ FROM [Product] this_ WHERE not (1=1)
That must be that "WHERE not (1=1)" clause the cause of this problem.
What can I do to be able .Count() the items in my repository?
Thanks!
EDIT: Actually the repository.GetAll() code is a little bit different... and that might change something! It is actually a generic repository for Entities. Some of the entities implement also the ILogicalDeletable interface (it contains a single bool property "IsDeleted"). Just before the "return" inside the GetAll() method I check if if the Entity I'm querying implements ILogicalDeletable.
public interface IRepository<TEntity, TId> where TEntity : Entity<TEntity, TId> {
IQueryable<TEntity> GetAll();
...
}
public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : Entity<TEntity, TId>
{
public virtual IQueryable<TEntity> GetAll()
{
if (typeof (ILogicalDeletable).IsAssignableFrom(typeof (TEntity)))
{
return session.Linq<TEntity>()
.Where(x => (x as ILogicalDeletable).IsDeleted == false);
}
else
{
return session.Linq<TEntity>();
}
}
}
public interface ILogicalDeletable {
bool IsDeleted {get; set;}
}
public Product : Entity<Product, int>, ILogicalDeletable
{ ... }
public IProductRepository : IRepository<Product, int> {}
public ProductRepository : Repository<Product, int>, IProductRepository {}
Edit 2: actually the .GetAll() is always returning an empty result-set for entities that implement the ILogicalDeletable interface (ie, it ALWAYS add a WHERE NOT (1=1) clause.
I think Linq to NHibernate does not like the typecast.
It looks like you've got a soft delete model going on here, and you're trying to filter these out from being returned by the GetAll() method. I agree with your analysis that NHibernate.Linq doesn't properly process the typecast, but you may want to try replacing this with a query filter.
public virtual IQueryable<TEntity> GetAll()
{
if (typeof(ILogicalDeletable).IsAssignableFrom(typeof(TEntity)))
{
return session.Linq<TEntity>().OfType<ILogicalDeletable>()
.Where(x => !x.IsDeleted).Cast<TEntity>();
}
return session.Linq<TEntity>();
}
Try that.
Related
I am trying to apply additional server-side filtering to entities exposed by an asp.net OData service, using Linq to Entities.
So far so good, but the Linq query that I build for the filter can sometimes get to an enormous length, causing a StackOverflowException.
As a workaround, I want to execute the query in a separate thread, giving that thread a bigger stack size limit. To achieve this, I implemented an IQueryable and IQueryProvider wrappers, so that when the IQueryable's Execute method is called, it would be executed in a separate thread.
To keep it simple and focused on my problem, I will omit the threading part in the following code samples, as this is not the issue here.
Here's the relevant parts of the code:
public class MyQueryable<T> : IQueryable<T> {
private IQueryable<T> _Inner { get; set; }
private IQueryProvider _Provider { get; set; }
public MyQueryable(IQueryable<T> inner) {
_Inner = inner;
_Provider = new MyQueryProvider(inner.Provider);
}
public Type ElementType {
get { return _Inner.ElementType; }
}
public Expression Expression {
get { return _Inner.Expression; }
}
public IQueryProvider {
get { return _Provider; }
}
/* ... Implementations of GetEnumerator ... */
}
public class MyQueryProvider : IQueryProvider {
private IQueryProvider _Inner { get; set; }
public MyQueryProvider(IQueryProvider inner) {
_Inner = inner;
}
public IQueryable<T> CreateQuery<T>(Expression expression) {
return new MyQueryable<T>(_Inner.CreateQuery<T>(expression));
}
public T Execute<T>(Expression expression) {
// The problem occurs on the following line.
return _Inner.Execute<T>(expression);
}
/* ... Implementation of the non-generic versions of CreateQuery, Execute ... */
}
When I run this, I get an InvalidOperationException in MyQueryProvider.Execute method:
Cannot compare elements of type 'System.Collections.Generic.ICollection`1'.
Only primitive types (such as Int32, String, and Guid) and entity types are supported.
This is how I build the original IQueryable that is being passed to MyQueryable:
I get the DbSet from my entities context, apply OData filter query option on it, and call Count(). Something along the lines of:
((odataQueryOptions.Filter.ApplyTo(
context.Entities.AsQueryable(), new ODataQuerySettings()))
as IQueryable<Entity>).Count()
I partially identified that the problem is that the odata filter query option contains a nested 'any' filter, something like:
Entities/$filter=RelatedEntities/any(entity: (entity/ID eq 1))
which adds a check to the IQueryable's Where expression whether the RelatedEntities collection is null. This check is suppostedly what causes the above exception.
What I cannot understand, is why the original IQueryable fails when I try to delegate the Execute method's execution to it from the MyQueryable.Execute method, but it all works fine when I use the original IQueryable directly.
Any help on this would be highly appreciated. Thanks in advance.
I'm try to do this : I'm using EF code first to map an old existing database. There's many fields with the wrong type (ex: char(1) used as boolean) so I've created a wrapper class for my db context that maps perfectly to the database table. Now, I want to expose an IQueryable of my Entity type on my repository. See my example :
public class MyContext:DbContext
{
public DbSet<WrappedEntity> WrapperEntities;
}
public class Repository
{
private MyContext _context;
//contructors omitted
public IQueryable<Entity> GetEntities()
{
return _context.WrapperEntities; //doesn't compile, I need some convertion here
}
}
I already have my convertion routine, the only thing missing is a way to query my DbContext thought my repository without exposing the WrappedEntity class, Is it possible and how ?
Thanks.
Usually you project with Queryable.Select to change the type of your query...
public IQueryable<Entity> GetEntities()
{
return _context.WrapperEntities.Select(x => new Entity(){...});
}
How can I use LINQ if I have wrapped my Entity Framework data context with a Repository class?
I want to do something like:
class A
{
public IRepositiry<T> GetRepository<T>()
{
DbContextAdapter adapter = new DbContextAdapter(ctx);
return new Repository<T>(adapter);
}
}
class B
{
void DoSomething()
{
A a = new A();
IRepository<House> rep = a.GetRepository<House>();
// Do some linq queries here, don't know how.
rep.[get Linqu] (from ...);
}
}
To keep your repository LINQ friendly you need to have some methods or properties on it that return IQueryable<T> or IEnumerable<T>
So in class Repository<T> you would have a method like this:
public class Repository<T>
{
DbContextAdapter ctx;
// other methods omitted
IEnumerable<Houses> GetHouses()
{
return ctx.Houses
}
}
Then in DoSomething you could do this:
void DoSomething()
{
A a = new A();
IRepository<House> rep = a.GetRepository<House>();
var q = from house in rep.GetHouses()
where house.Color = "Purple"
select house;
foreach(var h in q)
{
house.SetOnFire();
}
}
The standard query operators allow queries to be applied to any
IEnumerable-based information source. - MSDN
As long as you write methods that return IEnumerable Collections you will be compatible with LINQ.
at the risk of been completely lazy, what you want to implement is known as the repository pattern, check out Huyrya as its a good article.
Also it's possible to extend the entity classes, so they return instances or lists of themselves (singleton pattern). Aka:
public partial class FOO : FOO
{
public IEnumerable<Foo> GetFooList()
{
using (var context = new FooEntities())
{
return // YOU CODE TO GET LIST OF FOO
}
}
}
Or something like that (code syntax is not right but should give you the general idea). If your entity classes are going to implement similar methods, abstract them into interface contract and get your partial entity classes to implement that interface.
I was working on a Unit of Work implementation that works both in Entity Framework 4.1 and NHibernate. Find below the skeleton of my implementation details
IUnitOfWork definition
public interface IUnitOfWork
{
IRepository<LogInfo> LogInfos { get; }
IRepository<AppInfo> AppInfos { get; }
void Commit();
void Rollback();
}
IRepository definition
public interface IRepository<T> where T : class, IEntity
{
IQueryable<T> FindAll();
IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate);
T FindById(int id);
void Add(T newEntity);
void Remove(T entity);
}
Implementation of UoW in NHibernate
public class NHibernateUnitOfWork : IUnitOfWork, IDisposable
{
public ISession Session { get; private set; }
public NHibernateUnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
Session = _sessionFactory.OpenSession();
_transaction = Session.BeginTransaction();
}
public IRepository<LogInfo> LogInfos
{
get
{
if (_logInfo == null)
{
_logInfo = new NHibernateRepository<LogInfo>(Session);
}
return _logInfo;
}
}
public void Commit()
{
if (_transaction.IsActive)
_transaction.Commit();
}
}
Unit of Work in Entity Framework 4.1
public class SqlUnitOfWork : IUnitOfWork
{
private readonly ObjectContext _context;
public SqlUnitOfWork()
{
_context = new ObjectContext(connectionString);
_context.ContextOptions.LazyLoadingEnabled = true;
}
private SqlRepository<LogInfo> _logInfo = null;
public IRepository<LogInfo> LogInfos
{
get
{
if (_logInfo == null)
{
_logInfo = new SqlRepository<LogInfo>(_context);
}
return _logInfo;
}
}
public void Commit()
{
_context.SaveChanges();
}
}
Repository using NHibernate
public class NHibernateRepository<T> : IRepository<T> where T : class, IEntity
{
protected ISession Session;
public NHibernateRepository(ISession session)
{
Session = session;
}
public IQueryable<T> FindAll()
{
return Session.Query<T>();
}
public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate)
{
return Session.Query<T>().Where<T>(predicate);
}
public T FindById(int id)
{
return Session.Get<T>(id);
}
public void Add(T newEntity)
{
Session.Save(newEntity);
}
public void Remove(T entity)
{
Session.Delete(entity);
}
}
Repository using Entity Framework
public class SqlRepository<T> : IRepository<T> where T : class, IEntity
{
protected ObjectSet<T> ObjectSet;
public SqlRepository(ObjectContext context)
{
ObjectSet = context.CreateObjectSet<T>();
}
public IQueryable<T> FindAll()
{
return ObjectSet;
}
public IQueryable<T> FindWhere(Expression<Func<T, bool>> predicate)
{
return ObjectSet.Where(predicate);
}
public T FindById(int id)
{
return ObjectSet.Single(i => i.Id == id);
}
public void Add(T newEntity)
{
ObjectSet.AddObject(newEntity);
}
public void Remove(T entity)
{
ObjectSet.DeleteObject(entity);
}
}
With this implementation I could get most of the features like saving, deleting, transaction working on both EF and NH. But when I start writing complex LINQ queries against Repositories NH fails most of the time. Some features like OrderBy and ToList throws errors when Repository is returning NhQueryable.
In the following code is called from ASP.NET MVC controller to which I'm injecting instance of IUnitOfWork using StructureMap. When NHibernateUnitOfWork is injected Where condition does not get applied where as it works as expected when SqlUnitOfWork is injected.
var query = from a in _unitOfWork.AppInfos.FindAll()
join l in _unitOfWork.LogInfos.FindAll()
on a.Id equals l.ApplicationId
where l.Level == "ERROR" || l.Level == "FATAL"
group l by new { a.Id, a.ApplicationName } into g
select new LogInfoSummaryViewModel()
{
ApplicationId = g.Key.Id,
ApplicationName = g.Key.ApplicationName,
ErrorCount = g.Where(i => i.Level == "ERROR").Count(),
FatalCount = g.Where(i => i.Level == "FATAL").Count()
};
return query.AsEnumerable();
As a side not building solution supporting different provides on top of the linq is way to disaster. Linq and IQueryable are leaky abstractions - each Linq provider can have its own "features" and limitations. Moreover EF itselfs adds some logic via custom extension methods for IQueryable (like Include or AsNoTracking in EFv4.1). These methods internally converts IQueryable to ORM specific classes.
If you want to have universal solution you must abandon Linq and add third pattern to form the abstraction. In addition to Repository and Unit of Work patterns you need custom Specification pattern. Generally you will reimplement NHibernate's Criteria API.
From an IoC point of view and a desire for elegance your way is the way to go. However, all I read about NHibernate's linq provider is that it is still "beta-ish", because it is so damn hard to write Linq providers in the first place. So it might well be that you're just running into a bug here. Currently I would be very reluctant to write production code with Linq2Nhibernate. The new QueryOver feature is much more powerful. But of course, sadly, QueryOver doesn't fit seamlessly into your architecture, because you would have to use NHibernate syntax all the way. Complex Linq queries outside your repo would be useless because they would never get translated to SQL.
I'm afraid this effectively is the kiss of death to the elegance of your design, because, to start with, it would be useless to let a repository return an IQueryable<T>. But returning IEnumerable<T> would cripple your EF implementation. So, what is boils down to, I think that for querying both implementations are too different to fit behind one neat generic interface.
Here is a very useful post on QueryOver and Linq.
BTW: this is a very interesting question and design. I wish I could give more than one vote!
In addition to technical difficulties with QueryOver mentioned by Ladislav there may be a design issue. You would not have this problem if you approach it from Domain Driven Design perspective where Repository interface is based on Ubiquitous Language and does not expose things like IQueryable which is a pure data access concept. This answer has information and links that you may find interesting.
Using .NET 3.5, but the Select call gives the error. Shouldn't the compiler be clever enough to infer this? If not, why not?
public IEnumerable<Customer> TableToCustomers(Table table)
{
return table.Rows.Select(RowToCustomer);
}
private Customer RowToCustomer(TableRow row)
{
return new Customer { ... };
}
The Rows property is defined as TableRowCollection Rows {get;}
public sealed class TableRowCollection : IList, ICollection, IEnumerable
It is not IEnumerable<TableRow>, so it is just IEnumerable, therefore it cannot infer the type as being TableRow.
You can do this instead:
public IEnumerable<Customer> TableToCustomers(Table table)
{
return table.Rows.Cast<TableRow>().Select(RowToCustomer);
}
table.Rows.OfType<DataRow>().Select(RowToCustomer);