I'm having trouble running a join linq query against EF 4.0. Below is the code, stripped of all unnecessary statements, still reproducing the error)
using (var threadRepo = new Repo<Thread>())
using (var postRepo = new Repo<Post>())
{
var query = threadRepo
.Join(postRepo, t => t.PostId, s => s.Id, (t, s) => 1);
var array = query.ToArray();
}
Repo is my implementation of the repository pattern, and the join method looks like this:
public IEnumerable<TResult> Join<TInner, TKey, TResult>(
IEnumerable<TInner> inner,
Expression<Func<TEntity, TKey>> outerSelector,
Expression<Func<TInner, TKey>> innerSelector,
Expression<Func<TEntity, TInner, TResult>> result)
{
return _objectSet.Join(inner, outerSelector, innerSelector, result);
}
The error I get is
Unable to create a constant value of type 'Post'.
Only primitive types ('such as Int32, String, and Guid')
are supported in this context.
The same query works in LinqPad against the same database (though offcourse, there is not EF 4.0 there)
from t in Thread
join p in Post on t.PostId equals p.Id
select 1
Any clues on why Linq is giving me this exception?
Update
Based on a suggestion below, I tried using a common datacontext for both the repositories using a unit of work. However that doesn't seem to fix the issue. Below is the code I used
using (var uow = new UnitOfWork<CommunicationEntities>())
{
using (var threadRepo = new Repo<Thread>(uow))
using (var postRepo = new Repo<Post>(uow))
{
var query = threadRepo
.Join(postRepo, t => t.PostId, s => s.Id, (t, s) => 1);
var array = query.ToArray();
}
}
This gives me the same error as before.
Thanks
Jaspreet
A common mistake I have seen is that the data context is on the repository level and each repository uses a different data context - in this case that would explain the error. Instead your repositories all should share the same data context using the Unit of Work pattern.
Related
We're using MvvmCross in our app, and using the MvxSimpleIoCContainer
In the app startup, we register all of our Migrations.
it's easy do do since all migrations inherit from IMigration
typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.RegisterAsLazySingleton();
After the migrations are registered, we need to run them consecutively, and therefore the MigrationRunner looks a little something like this.
Mvx.Resolve<IMigrationRunner>().RunAll(SystemRole.Client, new List<IMigration>
{
Mvx.IocConstruct<Migration001>(),
Mvx.IocConstruct<Migration002>()
});
as you can see, I'm explicitely constructing each Migration using Mvx. This get's tedious and is prone to mistakes when a bunch of migrations end up in the app.
What I'd prefer to be able to do is resolve the entire collection in one fell swoop, and not have to touch it every time I create a new Migration.
Is there a way to do this via MvvmCross?
Pseudo Code
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client, Mvx.ResolveAll<IMigration>());
I would use LINQ to get the list of types. Unfortunately there's no way to get a list of registered types, so you'll have to enumerate the types again like you do for registration. You can even sort by type name. Now that you have a list of types, you can create a new list of instantiated/resolved types to pass into RunAll(). Something like:
var migrationTypes = typeof (IMigration)
.Assembly
.CreatableTypes()
.Inherits<IMigration>()
.AsTypes()
.OrderBy(t => t.Name)
.ToList();
Mvx.Resolve<IMigrationRunner>()
.RunAll(SystemRole.Client,
migrationTypes.Select(t => Mvx.Resolve(t)).ToList());
This is "browser" code, so no guarantees, but you get the gist.
Ok, so reflection is the answer to this problem for now, and eventually, I'd like to either extend our custom MvxServiceLocator : IServiceLocator to include something like
public IEnumerable<object> GetAllInstances(Type serviceType){...}
but for now I've just got a RunMigrations() method in the app
private void RunMigrations()
{
var migrationType = typeof (IMigration); // IMigration is in a separate assembly
var migrations = GetType().Assembly
.GetTypes()
.Where(
t => migrationType.IsAssignableFrom(t) && !t.IsAbstract)
.OrderBy(t => t.Name)
.Select(m => _serviceLocator.GetInstance(m) as IMigration)
.ToList();
var migrationRunner = new MigrationRunner(Mvx.Resolve<IDbProvider>());
migrationRunner.RunAll(SystemRole.Client, migrations);
}
where _serviceLocator.GetInstance(m) just lives in our custom MvxServiceLocator
public object GetInstance(Type serviceType)
{
return _ioCProvider.Resolve(serviceType);
}
Edit: here's how I extended our service locator wrapper.
public class MvxServiceLocator : IServiceLocator
{
private readonly IMvxIoCProvider _ioCProvider;
public MvxServiceLocator(IMvxIoCProvider ioCProvider)
{
_ioCProvider = ioCProvider;
}
public IEnumerable<TService> GetAllInstances<TService>()
{
var serviceType = typeof(TService);
var registrations = GetType().Assembly
.GetTypes()
.Where(
t => serviceType.IsAssignableFrom(t) && !t.IsAbstract)
.Select(m => (TService)_ioCProvider.Resolve(m));
return registrations;
}
}
I'm new to Fluent Hibernate And I'm stuck with a problem I want to get the email id of a user by using his user name means I want to implement the following code using fluent Hibernate
Select emailId from Table where username="User"
I tried the following code but its not give me what i want
public string ForgetPassword(string user)
{
var factory = CreateSessionFactory();
using (var session = factory.OpenSession())
{
var getEmail = session.Query<ClsAccountBL>()
Select(u => u.Email).Where(u => u.User == user).ToString();
return getMail;
}
}
Please help me to solve this
Instead of .ToString() use FirstOrDefault(). The LINQ does not return "single" element.
Since I felt adventurous the other day I decided compiling ActiveRecord 3 RC 1 with NHibernate 3.2 and see what happens.
Besides the breaking changes which I fixed I encountered a very strange behavior regarding SessionScopes and Linq queries.
Usually I don't have to use a session scope when using a Linq query but after I compiled ActiveRecord 3 RC 1 with NHibernate 3.2 I got the following error:
Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.
Stack Trace: at Castle.ActiveRecord.Framework.ActiveRecordLinqBase`1.get_Queryable()
at Castle.ActiveRecord.Framework.ActiveRecordLinq.AsQueryable[T]()
at Danel.Nursing.Scheduling.Actions.DataServices.BranchDataService.GetBranches() in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling.Actions\DataServices\BranchDataService.cs:line 21
at Danel.Nursing.Scheduling.Controllers.SmallHoursAmountController.<>c__DisplayClassb.<SetBranches>b__a() in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling\Controllers\SmallHoursAmountController.cs:line 275
at Danel.Nursing.Scheduling.Viewlets.WaitForAction.Worker_DoWork(Object sender
DoWorkEventArgs e) in D:\Work\Default\Scheduling\Danel.Nursing.Scheduling\Viewlets\WaitForAction.cs:line 40
It seems that the error comes from here:
public class ActiveRecordLinqBase<T> : ActiveRecordBase<T>
{
public static IOrderedQueryable<T> Queryable
{
get
{
var activeScope = holder.ThreadScopeInfo.GetRegisteredScope(); // The registered scope is null...
if (activeScope == null)
throw new ActiveRecordException("Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.");
var key = holder.GetSessionFactory(typeof(T));
var session = activeScope.IsKeyKnown(key) ? activeScope.GetSession(key) : SessionFactoryHolder.OpenSessionWithScope(activeScope, key);
return session.AsQueryable<T>();
}
}
}
What has changed that now I have to open a new SessionScope?
I had some trouble too with the Queryable function. Although I did not have the sessions scope problem, I had trouble saving update to objects retrieved by IQueryable. It seems that the new session was never registered with the active scope. I also changed the scope retrieval so maybe this also helps for you:
public new IOrderedQueryable<T> Queryable
{
get
{
ISessionFactory key = ActiveRecordMediator<T>.GetSessionFactoryHolder().GetSessionFactory(typeof(T));
ISessionScope activeScope = SessionScope.Current;
if (activeScope == null)
throw new ActiveRecordException("Could not found a registered Scope. Linq queries needs a underlying a scope to be functional.");
var session = activeScope.IsKeyKnown(key) ? activeScope.GetSession(key) : OpenSessionWithScope(activeScope, key);
if (!activeScope.IsKeyKnown(key))
{
activeScope.RegisterSession(key,session);
}
return session.AsQueryable<T>();
}
}
I'm using NHibernate Search for NHibernate 3.0 GA.
I have this code in my product repository:
public IList<Product> Find(string term)
{
var productFields = new[] { "Name", "Description" };
var importance = new Dictionary<String, float>(2) { { "Name", 4 }, { "Description", 1 } };
var analyzer = new StandardAnalyzer();
var parser = new MultiFieldQueryParser(
productFields,
analyzer,
importance);
var query = parser.Parse(term);
var session = Search.CreateFullTextSession(NHibernateSession.Current);
var tx = session.BeginTransaction();
var fullTextQuery = session.CreateFullTextQuery(query);
fullTextQuery.SetFirstResult(0).SetMaxResults(20);
var results = fullTextQuery.List<Product>();
tx.Commit();
return results;
}
In NH Profiler, I can see that a select statement is issued for every product found. What am I missing?
I found this thread from 2009 but presumably this bug has been fixed.
EDIT [06/06/2011]:
My property mappings as far as associations go are as follows:
mapping.HasManyToMany(c => c.Categories)
.Table("CatalogHierarchy")
.ParentKeyColumn("child_oid")
.ChildKeyColumn("oid")
.Cascade.None()
.Inverse()
.LazyLoad()
.AsSet();
mapping.HasMany(c => c.Variants)
.Table("CatalogProducts")
.Where("i_ClassType=2")
.KeyColumn("ParentOID")
.Cascade.SaveUpdate()
.AsSet();
I don't really want to eager fetch all categories.
As NHibernate.Search is unstable, I would use (and I did) Lucene.NET directly from my application - just to ask Lucene for objects' Ids and then load it using Session.Load().
Also you could change LazyLoad settings at class mapping level, not at relationship level.
Hope it helps.
EDIT: you could specify batch-size for relationships, so they wouldn't cause select N+1 problem
Are you referencing any lazy loaded properties in the product list that is returned by this method? If so, you can change the property mapping to "eager fetch".
I solved this in the end. I realised I had not wrapped the whole thing into a transaction. Once I did, the N+1 issue was gone.
Schoolboy error, but hey, you learn by your mistakes.
I've been struggling with the following issue for hours now. I tried this with different NHibernate/NHibernate.Search assemblies (3.0.0.4 / 2.1.2), all of them result in the same error. Used Lucene version is 2.9.2.2
All of them compiled from source.
NHibernate is setup to use NHibernate Search, configuration goes through Fluent NHibernate.
FluentConfiguration fc = Fluently.Configure()
. (mappings, db config, etc.)
.ExposeConfiguration
(
cfg =>
{
cfg.SetProperty("hibernate.search.default.directory_provider", typeof(FSDirectoryProvider).AssemblyQualifiedName);
cfg.SetProperty("hibernate.search.default.indexBase", "~/Index");
cfg.SetProperty("hibernate.search.default.indexBase.create", "true");
cfg.SetListener(NHibernate.Event.ListenerType.PostUpdate, new FullTextIndexEventListener());
cfg.SetListener(NHibernate.Event.ListenerType.PostInsert, new FullTextIndexEventListener());
cfg.SetListener(NHibernate.Event.ListenerType.PostDelete, new FullTextIndexEventListener());
}
);
So far so good, an index is created in the Index directory in the bin folder (segments.gen & segments_1 files).
After the configuration has been created I fetch the NHibernate Session and try to index something:
var _session = _factory.OpenSession();
using (ITransaction tx = _session.BeginTransaction())
{
var fts = Search.CreateFullTextSession(_session);
fts.PurgeAll(typeof(User));
var coll = fts.CreateCriteria<User>().List<User>();
foreach (var item in coll)
{
fts.Index(item);
}
tx.Commit();
}
This goes fine until the fts.PurgeAll or fts.Index is hit, which gives this error:
Object reference not set to an instance of an object.
Line 602: // TODO: Cache that at the FTSession level
Line 603: // not strictly necesary but a small optmization
Line 604: DocumentBuilder builder = searchFactoryImplementor.DocumentBuilders[clazz];
Line 605: if (builder != null)
Line 606: {
This error is thrown from the NHiberate.Search.dll, it looks like the SearchFactory was not initialized. The code that should create the SearchFactory returns null:
if (searchFactory == null)
{
searchFactory = ContextHelper.GetSearchFactory(session);
}
Came across several possible solutions where I need to initialize the SearchFactory with SearchFactory.Initialize, but no such method exists in my (2.0 / 3.0) NHibernate.Search assemblies.
NHibernate.Search.Search.CreateFullTextSession(_session)
.CreateFullTextQuery<User>("Firstname:Cees").List<User>();
Also throws a 'null exception' (of course), above is calling:
IDictionary<System.Type, DocumentBuilder> builders = searchFactory.DocumentBuilders;
Where searchFactory == null
There is a SearchFactoryImpl
SearchFactoryImpl searchFactory = NHibernate.Search.Impl.SearchFactoryImpl.GetSearchFactory(config);
Which return a SearchFactoryImpl instance, no idea what to do with it though...
Maybe I'm missing something? Any help is much appreciated.
Hmm, seems like Ninject had something to do with it. Not sure why/how. I've got a working solution with NH 3.0.1.4000 + Search + Lucene 2.9.2.2, if interested, send me an email.
http://ceesplug.nl/LuceneNHibernateTest.rar
Complete solution, working for NHibernate with and without FluentNHibernate.