NHibernate Session.Query<>, column list? - c#

We have some code written which depends on working with the IQueryable instances, so I suppose we are stuck with having to use ISession.Query<>().
In one particular case I would like to only partially hydrate the DBOs and exclude certain columns from the SELECT statement which NHibernate will generate.
Is it possible to achieve that while using Query<>?
Alternatively, is it possible to somehow go from ICriteria to IQueryable? (I think for ICriteria it is possible to achieve what I need by using Projections?)

Projections are supported in IQueryable as well. Syntax should be like this:
var query = session.Query<Employee>();
var list = query.Select(s => new Employee
{
FirstName = s.FirstName,
LastName = s.LastName,
...
})
.ToList();
The new Employee could be even some DTO...
Some basic info about QueryOver projection API:
16.6. Projections

Related

Entity Framework, Repository pattern and let statements

Trying to implement a correct Repository pattern with Entity Framework, I'm stumbling over some issues with let statements. What I want to do is:
var customer = (from cus in Customers.GetAll()
let brokerExists = InsuredBrokers.GetAll().Any(ib => ib.INS_Id == cus.INS_Id)
// ... more stuff
But this will give me an error
System.NotSupportedException: 'LINQ to Entities does not recognize the
method
'System.Linq.IQueryable`1[SNIP.DataModel.EA_INB_InsuredBrokers_TB]
GetAll()' method, and this method cannot be translated into a store
expression.'
What I instead can do is:
var customer = (from cus in Customers.GetAll()
let brokerExists = _context.Set<EA_INB_InsuredBrokers_TB>().Any(ib => ib.INS_Id == cus.INS_Id)
// ... more stuff
However, this breaks any point in using the Repository pattern. When I search for answers, people say to put it in a query on its own and reference it from memory, but since I actually have the customer's Id (INS_Id) in the let statement, I cannot do that.
GetAll() is like:
public IQueryable<T> GetAll()
{
return _context.Set<T>().AsQueryable();
}
Are there any clever ways to get around this?
You have to move InsuredBrokers.GetAll() out of the query:
var allBrokers = InsuredBrokers.GetAll();
var customer = (from cus in Customers.GetAll()
let brokerExists = allBrokers.Any(ib => ib.INS_Id == cus.INS_Id)
// ... more stuff
Then it will work fine. Since GetAll returns IQueryable and you don't enumerate it - this has no negative effect and there will still be one query to database, just like in your example with _context.
The reason is let statement is compiled like this:
Customers.GetAll().Select(cus => new {cus, brokerExists = InsuredBrokers.GetAll().Any(ib => ib.INS_Id == cus.INS_Id)}
Which means your call to InsuredBrokers.GetAll() is part of expression tree (it's inside Select expression), and entity framework cannot (will not) just call it to obtain value. It will try to translate it to SQL query, but has no idea what to do with GetAll method.
I assume you are trying to do a left join.
Your query:
var query = from customer in Customers
join broker in InsuredBrokers
on customer.InsertId equals broker.InsertId
into resutGroups
select new { Name = broker.Name, InsertId= broker.InsertId};
If you need entries for no maches use DefaultIfEmpty.
You can refer to this for more on the same.

Join vs Navigation property for sub lists in Entity Framework

I have a sql statement like this:
DECLARE #destinations table(destinationId int)
INSERT INTO #destinations
VALUES (414),(416)
SELECT *
FROM GroupOrder grp (NOLOCK)
JOIN DestinationGroupItem destItem (NOLOCK)
ON destItem.GroupOrderId = grp.GroupOrderId
JOIN #destinations dests
ON destItem.DestinationId = dests.destinationId
WHERE OrderId = 5662
I am using entity framework and I am having a hard time getting this query into Linq. (The only reason I wrote the query above was to help me conceptualize what I was looking for.)
I have an IQueryable of GroupOrder entities and a List of integers that are my destinations.
After looking at this I realize that I can probably just do two joins (like my SQL query) and get to what I want.
But it seems a bit odd to do that because a GroupOrder object already has a list of DestinationGroupItem objects on it.
I am a bit confused how to use the Navigation property on the GroupOrder when I have an IQueryable listing of GroupOrders.
Also, if possible, I would like to do this in one trip to the database. (I think I could do a few foreach loops to get this done, but it would not be as efficient as a single IQueryable run to the database.)
NOTE: I prefer fluent linq syntax over the query linq syntax. But beggars can't be choosers so I will take whatever I can get.
If you already have the DestinationGroupItem as a Navigation-property, then you already have your SQL-JOIN equivalent - example. Load the related entities with Include. Use List's Contains extension method to see if the desired DestinationId(s) is(are) hit:
var destinations = new List<int> { 414, 416 };
var query = from order in GroupOrder.Include(o => o.DestinationGroupItem) // this is the join via the navigation property
where order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId)
select order;
// OR
var query = dataContext.GroupOrder
.Include(o => o.DestinationGroupItem)
.Where(order => order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId));

LINQ 'AsEnumerable' and the Entity Framework

I am writing a LINQ query similar to the following:
var test = from o in dbcontext.Orders.AsEnumerable()
where o.ID==1
select new Order
{
Name = GetName(o.ID)
};
In order to call an external function within the LINQ query, I am using AsEnumerable() within the query.
I understand that normally the query is not executed until an enumeration function like ToList(), etc. is called. But here I am calling the enumeration within the query.
Can anyone tell me if it is considered bad practice to use AsEnumerable() like this? Will it suffer in performance compared to calling ToList() after creating the query?
I'd do
var ids = from o in dbcontext.Orders
where o.ID==1
select new { ID = o.ID };
var names = from i in ids.AsEnumerable()
select new Order { Name = GetName(i.ID) };
i.e. do as much querying as possible in the database, and then only perform the ID-to-name transformation in C#.

Retrieving all values as strings from SQL Server

I'm currently using EF Code-First, and I'd like to SELECT data from many tables in the database. This query is a customizable query, and hence I can't predict what kind of data I will be retrieving.
At first, when I tried running ctx.Database.SqlQuery<string>(sql, param), I ran into an exception when facing a DateTime value. I'd like to do this without casting it on the server side.
Does anybody have any idea how I can go about doing it? It can be in LINQ, LINQ-SQL, or purely SQL--so long as it gets the job done! Thanks guys...
You will not get it. Linq-to-entities will not make transformation to list of strings. Your best chance is executing normal queries and do conversion and transformation your application.
Argument that you don't know which columns user selects just means you need more dynamic solution - Linq-to-entities is not a good tool for you (except if you try to use Dynamic Linq or build expression trees manually). Use ESQL or SQL directly.
When selecting data from many tables, use anonymous types to encapsulate the properties(fields) you want to select from these tables into a new entity, something like:
var query = _db.Categories.Join(
_db.Products,
c => c.CategoryId,
p => p.CategoryId,
(category, product) =>
new
{
ProductName = product.Name,
CategoryName = category.Name,
ExpiryDate = product.ExpiryDate
});
You can achieve string values by casting your data fields to string in this way:
var query = _db.Categories.Join(
_db.Products,
c => c.CategoryId,
p => p.CategoryId,
(category, product) =>
new
{
ProductName = product.Name.toString(),
CategoryName = category.Name.toString(),
ExpiryDate = product.ExpiryDate.toString()
});

Use LINQ-to-SQL to return an object that has child objects filtered

I have a MembershipGroups table that is associated with a child Members table. The Members table has a Status column which can be set to Active or Inactive.
I want to select all MembershipGroups and only their active Members
As an example,
MembershipGroups
ID----Title
1-----Group #1
2-----Group #2
Members
MembershipGroupID-Name--Status
1-------------------------John----Active
1-------------------------Sally----Inactive
1-------------------------David---Inactive
I'm trying to create a query that looks something like the following (which doesn't currently work):
var query = from mg in db.MembershipGroups
where mg.Members.Status = "Active"
select mg
The result for this example should return a MembershipGroup of ID#1 with only one child Member entity
How can use LINQ-to-SQL to select a parent object that filters on child objects? If I were using straight T-SQL then this would be a simple join with a where clause but it seems to be much more difficult to do using LINQ-to-SQL.
Edit - Updated answer to return the MemberShipGroup object
var query = (from mg in db.MembershipGroups
join m in db.Members.Where(mem => mem.Status == "Active")
on mg.ID equals m.MembershipGroups into members
select new
{
MembershipGroup = mg,
Members = members
}).AsEnumerable()
.Select(m => new MembershipGroup
{
ID = m.MembershipGroup.ID,
Title = m.MembershipGroup.Title,
Members = m.Members
});
In LINQ to SQL, you can use the AssociateWith method on the DataLoadOptions to set your child filter at the context level.
DataLoadOptions opt = new DataLoadOptions();
opt.AssociateWith<Member>(m => m.Status == "Active");
db.LoadOptions = opt;
With this in place, you can simply return your member groups (or filter them for the active ones using where mg.Any(group => group.Members.Status == "Active"). Then when you try to drill into the Members of that group, only the Active ones will be returned due to the LoadOptions.
See also http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.associatewith.aspx .
One word of warning, once you set the LoadOptions on a context instance, you can not change it. You may want to use a customized context to use this option.
As an alternative, you could use LINQ to SQL's inheritance model to create an ActiveMember type using the Status column as your discriminator and then create an association between the MemberGroups and ActiveMembers types. This would be the approach you would need to use to model this with the Entity Framework if you though about going that route as well as EF doesn't support the concept of the LoadOptions.
Make sure you are including the child objects you are trying to filter on, inside the query.
E.g.
var query = db.MembershipGroups
.Include("Members")
.Where(m => m.Members.Status == "Active");

Categories

Resources