Why does .Fetch() throws an null reference exception? - nhibernate

I have the following nhibernate linq query and it throws a null reference exception
promotions = (from a in session.Query<Application>()
from ap in a.Promotions
where a.Id == applicationId
&& ap.EndDate >= DateTime.Now && ap.StartDate <= DateTime.Now
select ap).Fetch(ap => ap.LandingPage).ToList();
The same query without the .Fetch() works fine. I am passing the same id both times, so it's not a data issue.
Is this a bug, or by design? How can I make it not throw an exception?

If you move the .Fetch(ap => ap.LandingPage) to immediately after the declaration does that change the outcome?
from ap in a.Promotions.Fetch(ap => ap.LandingPage)

from a in session.Query<Application>().Fetch(ap => ap.LandingPage)
//the rest of your code

Related

CrossDB queries with EFCore = no / instantiating context entites as async lists = works - What is the true approach should I be taking?

I have a system with two database servers I am working with:
One of them is database first - a database managed by a legacy enterprise application and I don't have full control over changing the database structure.
The second is code first and I have full control in the code first database to make changes.
Security policies prevent me from making a view that joins tables from the two database servers in the code first DB which might be a way to make this better according to what i've seen on SO posts.
I have one context for each database since they are separate.
The data and structure in the code first tables is designed to be able to join to the non-code first database as if they were all in one database.
I CAN get what I need working using this set of queries:
// Set up EF tables
var person = await _context1.Person.ToListAsync();
var labor = await _context1.Labor.ToListAsync();
var laborCraftRate = await context1.LaborCraftRate.ToListAsync();
var webUsers = await context2.WebUsers.ToListAsync();
var workOrders = await _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC")
.ToListAsync();
var specialRequests = await _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now)
.ToListAsync();
var distributionListQuery = (
from l in labor
from p in person.Where(p => p.Personid == l.Laborcode).DefaultIfEmpty()
from wu in webUsers.Where(wu => wu.Laborcode == l.Laborcode).DefaultIfEmpty()
from lcr in laborCraftRate.Where(lcr => lcr.Laborcode == l.Laborcode).DefaultIfEmpty()
select new
{
Laborcode = l.Laborcode,
Displayname = p.Displayname,
Craft = lcr.Craft,
Crew = l.Crewid,
Active = wu.Active,
Admin = wu.FrIsAdmin,
FrDistLocation = wu.FrDistLocation,
}).Where(r => r.Active == "Y" && (r.FrDistLocation == "IPC" || r.FrDistLocation == "IPC2" || r.FrDistLocation == "both"))
.OrderBy(r => r.Craft)
.ThenBy(r => r.Displayname);
// Build a subquery for the next query to use
var ptoSubQuery =
from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
select new
{
workOrder.Wonum,
Laborcode = workOrder.Wolablnk,
specialRequest.Requestdate
};
// Build the PTO query to join with the distribution list
var ptoQuery =
from a in ptoSubQuery
group a by a.Wonum into g
select new
{
Wonum = g.Key,
StartDate = g.Min(x => x.Requestdate),
EndDate = g.Max(x => x.Requestdate),
Laborcode = g.Min(x => x.Laborcode)
};
// Join the distribution list and the object list to return
// list items with PTO information
var joinedQuery = from dl in distributionListQuery
join fl in ptoQuery on dl.Laborcode equals fl.Laborcode
select new
{
dl.Laborcode,
dl.Displayname,
dl.Craft,
dl.Crew,
dl.Active,
dl.Admin,
dl.FrDistLocation,
fl.StartDate,
fl.EndDate
};
// There are multiple records that result from the join,
// strip out all but the first instance of PTO for all users
var distributionList = joinedQuery.GroupBy(r => r.Laborcode)
.Select(r => r.FirstOrDefault()).OrderByDescending(r => r.Laborcode).ToList();
Again, this works and gets my data back in a reasonable but clearly not optimal timeframe that I can work with in my UI that needs this by preloading the data before it is needed. Not the best, but works.
If I change the variable declarations to not be async which I was told I should do in another SO post, this turns into a cross db query and netcore says no:
// Set up EF tables
var person = _context1.Person;
var labor = _context1.Labor;
var laborCraftRate = context1.LaborCraftRate;
var webUsers = context2.WebUsers;
var workOrders = _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC");
var specialRequests = _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now);
Adding ToListAsync() is what allows the join functionality I need to work.
Q - Can anyone elaborate on possible downsides and problems with what I am doing?
Thank you for helping me understand!
It's not that calling ToList() "doesn't work." The problem is that it materializes (I think that's the right word) the query and returns a potentially larger than intended amount of data to the client. Any further LINQ operations are done on the client side. This can increase the load on the database and network. In your case, it works because you're bringing all that data to the client side. At that point, it no longer matters that it was a cross-database query.
This was a frequent concern during the transition from .NET Core 2.x to 3.x. If an operation could not be performed server side, .NET Core 2.x would silently insert something like ToList(). (Well, not completely silently. I think it was logged somewhere. But many developers weren't aware of it.) 3.x stopped doing that and would give you an error. When people tried to upgrade to 3.x, they often found it difficult to convert the queries into something that could run server side. And people resisted throwing in an explicit ToList() because muh performance. But remember, that's what it was always doing. If there wasn't a performance issue before, there isn't one now. And at least now you're aware of what it's actually doing, and can fix it if you really need to.

.NET Core - EntityFrameworkCore - Unable to cast object of type 'Query.Internal.EntityQueryable` to type 'DbSet`

I try to implement a search with entity when a search field is provided
but I get a weird casting error I just dont understand
Unable to cast object of type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[SomeApp.Models.Partner]' to type 'Microsoft.EntityFrameworkCore.DbSet`1[SomeApp.Models.Partner]'.
here is the code of my controller entry point
I tried forcing the cast, but apparently there is something wrong with my code
[HttpPost]
public async Task<ActionResult<PartnersFetch>> GetPartners(PartnersSearch partnersSearch)
{
DbSet<Partner> data = _context.Partners;
if (partnersSearch.SearchById != null)
{
// the following line causes problems :
data = (DbSet <Partner>) data.Where( p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
thanks for helping me on this
I forgot to use AsQueryable
var data = _context.Partners.AsQueryable();
if (partnersSearch.SearchById != null)
{
data = data.Where( p => p.Id == partnersSearch.SearchById.GetValueOrDefault());
}
data.Where(...) will return an IQueryable which you can materialize as follows
List<Partner> myResult = data.Where(...).ToList();
The DbSet<Partner> is only the set on which you can query data. Your goal very likely is to get the partners out of it, right?

Upgrading to Dot Net Core Sql Problems

So, I am trying to update my data project to use dot net core. I am slowly getting through all the errors. I am having trouble with this one though. I need to make an SqlQuery that returns a list, but as far as I am aware dot net core doesn't support that yet.
Here is the code
List<DateTime> result = this.Database.SqlQuery<DateTime>(#"SELECT c.DateId FROM Schedule.Calendar c
WHERE c.DateId > #startDate AND c.DateId < #endDate AND c.IsWorkDay = 0",
new SqlParameter("#startDate", startDate), new SqlParameter("#endDate", endDate)).ToList();
I have a few more that are kinda like this, but I can't figure those out either. I am getting an error on the SqlQuery
It says thatDatabaseFacade does not contain a definition for SqlQuery. How would I go about updating this code?
I understand that you're trying to use Raw SQL but my preference would be to approach this scenario using Linq as:
List<DateTime> GetDateTimeResult(DateTime startDate, DateTime endDate, int isWorkDay)
{
using (var db = new YourScheduleDatabaseContext())
{
List<DateTime> result = db.Calendar
.Where(c => c.DateId > startDate)
.Where(c => c.DateId < endDate)
.Where(c => c.IsWorkDay.Equals(isWorkDay))
.Select(c => new DateTime(c.DateId)).ToList();
return result;
}
}
I might have misunderstood your context name and table names but hope you get the idea and happy to help further.
I ended up using the following code:
List<MyDateTime> result = this.Set<MyDateTime>().FromSql(#"SELECT Schedule.Workday(#startDate, #days)",
new SqlParameter("#startDate", startDate), new SqlParameter("#days", days)).ToList();
List<DateTime> finalResult = new List<DateTime>();
foreach(MyDateTime myDate in result)
{
finalResult.Add(myDate.DateTime);
}
This way I don't have to have a using or a certain context. I just created class with DateTime property to use as MyDateTime.

How do I do a String.IsNullOrEmpty() test in an NHibernate Queryover where clause?

I hit a situation today where a field in our legacy db that should never be empty... was empty.
I am using NHibernate 3.2 against this database and the queries that are affected are written in QueryOver.
My current query is this
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName)
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
I want it to be this:
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName &&
!string.IsNullOrEmpty(fg.Code))
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
When I try this I get an exception "Unrecognised method call: System.String:Boolean IsNullOrEmpty(System.String)"
So NHibernate can't translate string.IsNullOrEmpty. Fair enough. However when I try this
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId &&
fg.UserName == Token.UserName &&
!(fg.Code == null || fg.Code.Trim() == "" ))
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);
I get an InvalidOperationException "variable 'fg' of type 'Domain.Entities.FacilityGroup' referenced from scope '', but it is not defined"
Any thoughts?
Ok... I guess I asked this question too soon. I figured out a way around this.
What I was able to do was invoke the 'trim' function from hql via a SQL Function Projection. I ended up writing it as IQueryOver Extention method to keep it flexible. I will post it here in case anyone needs it.
public static class QueriesExtentions
{
public static IQueryOver<E, F> WhereStringIsNotNullOrEmpty<E, F>(this IQueryOver<E, F> query, Expression<Func<E, object>> propExpression)
{
var prop = Projections.Property(propExpression);
var criteria = Restrictions.Or(Restrictions.IsNull(prop), Restrictions.Eq(Projections.SqlFunction("trim", NHibernateUtil.String, prop), ""));
return query.Where(Restrictions.Not(criteria));
}
}
and here it is in use
return Session
.QueryOver<FacilityGroup>()
.Where(fg => fg.Owner.Id == Token.OwnerId && fg.UserName == Token.UserName )
.WhereStringIsNotNullOrEmpty(fg => fg.Code)
.OrderBy(fg => fg.Code).Asc
.TransformUsing(Transformers.DistinctRootEntity);

How add only not empty/null restriction to query

Do you have an idea how to write the following code nicer/shorter :)
I need to check if the field form form is not empty and then add
criterion to the query,
thanks :)
AbstractCriterion restrictions = null;
if (model.DateFrom != null)
AddRestriction(ref restrictions,
Restrictions.Ge(Projections.Property<Invoice>(x => x.DateIn), model.DateForm));
if (model.DateTo != null)
AddRestriction(ref restrictions,
Restrictions.Le(Projections.Property<Invoice>(x => x.DateIn), model.DateTo));
if (!string.IsNullOrEmpty(model.Prop1))
AddRestriction(ref restrictions,
Restrictions.Eq(Projections.Property<Invoice>(x => x.Prop1), model.Prop1));
// ... many more conditions :)
return m_session.QueryOver<Invoice>().Where(restrictions).List();
You don't need the ref keyword for starters. I think this is nicer without sacrificing readability:
var query = session.QueryOver<Invoice>();
Action<object, ICriterion> addIfNonNull = (o, c) =>
{
if (o != null)
{
query.And(c);
}
};
addIfNonNull(model.Prop1, Restrictions.Eq(Projections.Property<Invoice>(x => x.Prop1), model.Prop1));
etc.