I am getting an error on what looks to be quite a simple QueryOver. The complication is that the select involves a CompositeId.
The error I am getting is:
System.InvalidOperationException: variable 'x' of type 'nha.cs.utility.mole.QueryParameters' refrenced from scope '', but it is not defined.
I am mapping a query that has many query parameters. The composite id is with the query and the query parameter name, shown below.
public class Query
{
public virtual int id { get; protected set; }
public virtual string name { get; set; }
public virtual string query { get; set; }
public virtual IList<QueryParameters> parameters { get; set; }
public virtual IList<Application> applicationsUsedIn { get; set; }
public Query()
{
this.parameters = new List<QueryParameters>();
this.applicationsUsedIn = new List<Application>();
}
public virtual void AddParameter(QueryParameters qp)
{
qp.query = this;
this.parameters.Add(qp);
}
}
public class QueryParameters
{
public virtual Query query { get; set; }
public virtual string name { get; set; }
public virtual string type { get; set; }
public virtual int order { get; set; }
public QueryParameters()
{
}
public QueryParameters(Query qry, string nm)
{
this.query = qry;
this.name = nm;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as QueryParameters;
if (t == null)
return false;
if (query == t.query && name == t.name)
return true;
return false;
}
public override int GetHashCode()
{
return (query.id + "|" + name)).GetHashCode();
}
}
public QueryMap()
{
Table("dbo.Queries");
Id(x => x.id);
Map(x => x.name);
Map(x => x.query);
HasMany(x => x.parameters)
.Cascade.All()
;
HasManyToMany(x => x.applicationsUsedIn)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("qryid")
.ChildKeyColumn("appid")
.Inverse()
.LazyLoad();
}
public QueryParametersMap()
{
Table("dbo.QueryParameters");
CompositeId()
.KeyReference(x => x.query)
.KeyProperty(x => x.name)
;
References(x => x.query).Column("qryid").Not.Insert();
Map(x => x.name);
Map(x => x.order);
Map(x => x.type);
}
public Query addParameterToQuery(Query qry, string paramname, string paramtype, int paramorder)
{
if (null != qry)
{
string qryname = qry.name;
string qrystr = qry.query;
using (ISessionFactory isf = getSessionFactory())
{
using (var sess = isf.OpenSession())
{
using (var tran = sess.Transaction)
{
try
{
tran.Begin();
var critqry = sess.CreateCriteria<Query>()
.Add(Restrictions.Eq("name", qryname));
Query qryd = (Query)critqry.UniqueResult();
if (null == qryd)
{
qryd = new Query();
qryd.name = qryname;
qryd.query = qrystr;
sess.Save(qryd);
Console.Out.WriteLine("Query was null, added new query with name '" + qryname + "'");
}
else
{
Console.Out.WriteLine("Query was not null, has name '" + qryname + "'");
}
//EXCEPTION THROWN ON THIS LINE BELOW
var cp = sess.QueryOver<QueryParameters>()
.Select(x => x.query == qryd, x => x.name == paramname);
QueryParameters qryparam = cp.List().First<QueryParameters>();
}
catch (Exception ex)
{
tran.Rollback();
lws.logMessage(AppName, "addParameterToQuery", ex.ToString(), MessageType.Info, MessageLevel.Error);
Console.Out.WriteLine(ex.ToString());
}
}
}
}
}
return (null);
}
Any thoughts or pointers would be greatly appreciated.
Thanks,
Bruce.
UPDATED CODE
public class QueryMap : ClassMap<Query>
{
public QueryMap()
{
Table("dbo.Queries");
Id(x => x.id);
Map(x => x.name);
Map(x => x.query);
HasMany(x => x.parameters)
.Cascade.All()
.KeyColumn("qryid");
HasManyToMany(x => x.applicationsUsedIn)
.Table("dbo.ApplicationsQueries")
.ParentKeyColumn("qryid")
.ChildKeyColumn("appid")
.Inverse()
.LazyLoad();
}
}
public class QueryParametersMap : ClassMap<QueryParameters>
{
public QueryParametersMap()
{
Table("dbo.QueryParameters");
Id(x => x.id);
References(x => x.query).Column("qryid").Not.Insert();
Map(x => x.name);
Map(x => x.type);
Map(x => x.order).Column("ordr");
}
}
var cp = sess.QueryOver<QueryParameters>()
.Where(x => x.query.id == qryd.id)
.And(x => x.name == paramname);
One issue is usage of the .Select()
as a filtering section. When using QueryOver
we have to use .Where()
instead:
var cp = sess.QueryOver<QueryParameters>()
//.Select(x => x.query == qryd, x => x.name == paramname)
.Where(x => x.query.Id == qryd.ID)
.And(x => x.name == paramname)
.List<QueryParameters>();
See 16.2. Simple Expressions. The .Select()
could narrow the resultset, because its definition will be used for a SELECT clause creation: 16.6. Projections
To include more related objects/entities into the query we can use 16.4. Associations (ie JOIN related tables)
NOTE: Bruce, if possible, I would try to re-think about the domain model. The many-to-many and composite-id are signs of something beeing over-complex (if not over-complicated). I tried many time before descourage people from using the exotic mapping... it is there for legacy purposes. Maybe check this section: 24. Best Practices