QueryOver: select columns from subquery - c#

How can I select / project values from a subquery from a different table into my main query?
I have an NH-model like this:
[Serializable]
public class MyModel
{
public virtual int Id {get; set;}
//more mapped values
....
//unmapped values
public virtual string ValueFromOtherTable {get;set;}
}
And I want to fill ValueFromOtherTable with a left join like this:
Select mt.*, ..., ot.ValueFromOtherTable from MyModelTable mt left
join OtherTable ot ON (somecondition)
where MyModelTable is the table mapped to MyModel-class. I want to fill ValueFromOtherTable (no NH-mapping) by selecting all values from mt (to fill the NH-mapped columns) and then by using OtherTable I want to fill ValueFromOtherTable.
I can´t join both tables via QueryOver as there exists no direct parent-child correlation in the model, so JoinAlias or JoinQueryOver won´t work. My MainQueryOver queries MyModelTable.
ALTERNATIVE:
The alternative is to first get all values from MyModelTable and then using the properties there to query OtherTable. However this will result in an SELECT N+1 problem (for each model from MyModel select some OtherTable...) and also makes the code very complicated.
Is there a good way to solve this problem or is the only way to fill MyModel by using described alternative
?

One way would be to use Projections, Subquery and DTO. So let's say, that we have DTO (almost the same as MyModel, but with new extern property ... e.g. Count). Then we can do it like this:
MyModel main = null;
MyModelDTO dto = null;
// the main query
var query = session.QueryOver<MyModel>(() => main);
// the subquery used for projection
var subquery = QueryOver.Of<OtherModel>()
// select something, e.g. count of the ID
.SelectList(selectGroup => selectGroup.SelectCount(o => o.ID))
// some condition
// kind of JOIN inside of the subquery
.Where(o => o.xxx == main.yyy); // just example
// now select the properties from main MyModel and one from the subquery
query.SelectList(sl => sl
.SelectSubQuery(subquery)
.WithAlias(() => dto.Count)
.Select(() => main.ID)
.WithAlias(() => dto .ID)
....
);
// we have to use transformer
query.TransformUsing(Transformers.AliasToBean<MyModelDTO >())
// and we can get a list of DTO
var list = query.List<MyModelDTO>();

Related

Equivalent of INNER JOIN ... WHERE in NHibernate

I have class Category that contains a List<Subcategory>.
Using LINQ (preferrably), I want to get all Categoriess but filter their Subcategories such that the resulting query would look like:
SELECT * FROM categories
LEFT JOIN subcategories ON subcategories.category_Id = categories.id WHERE subcategories.Retired = false
I have tried:
session.QueryOver<Category>()
.Left.JoinQueryOver(c => c.Subcategories, () => subcategoryAlis, s => !s.Retired)
.List<Category>())
And a few other hacky looking tricks but I can't seem to get the results I need. The above query produces completely useless results with the same entries appearing multiple times and many missing.
It's been a while since I've used NHibernate, but I would do it with some dedicated DTO classes for converting to Json, not directly from the entities. I think something along the lines of the following linq query should work:
from c in session.Query<Category>
select new CategoryDto {
Name = c.Name, //and other properties
SubCategories = c.SubCategories
.Where(sc => !sc.Retired)
.Select(sc => new SubCategoryDto { ... })
.ToList()
}

Select specific columns from table, Entity Framework

I have a question about selecting specific columns from table using entity framework. The problem is, that I'm using Find() method to get my desired table, by primary key, then taking from it some data.
I have one table with massive amounts of columns and if I call Find() method, it will return all columns of that row, but I want to use only, for example, the data from 2 columns.
MyTable table = context.MyTable.Find(id); //Get MyTable object from context, id = primary key
string p1 = table.Prop1;
string p2 = table.Prop2;
This will return single object with all (for example it has Prop1, Prop2,...,PropN) properties filled (if its filled in database).
So I know that I can use anonymous objects or data transfer objects (DTO), but [question1] is there any other (yet simple) method to get specific columns? [question2] Is it affecting on performance if I use Find() (or I should use Where()/Select())?
var items = context.MyTable.Where(x => x.Id == id)
.Select(x => new
{
P1 = table.Prop1,
P2 = table.Prop2
});
This will translate into a sql call like:
SELECT p.Prop1, p.Prop2 FROM mytable p WHERE p.Id = id
Use Data Transfer Objects: DTO, which is a recommened microsoft pattern.
Putting it simple, they are just objects that hold data.
Then do like someone suggested:
public class MyDto
{
public string Prop1 {get;set;} = String.Empty
public string Prop2 {get;set;} = String.Empty
}
MyDto x = new MyDto();
x = context.MyTable.Where(x => x.Id == id)
.Select(x => new MyDto
{
P1 = table.Prop1
//I don't want prop 2, for example
});
And pass around the object. Set defaults for Auto Properties (C# 6 and up) and initialize only the properties you want.
EDIT:
I've read you don't want to use anonymous and DTO, then how you want to do it. You either use objects or anonymous.
Other ways is just build a layered structure and call the query method directly where you need it. Patterns exists for a reason.
You can call queries against Dynamic objects. With these you may assign fields that will be resolved at runtime, at the cost of losing strong typing.
You might also want to check if it's performance-whorty to use dynamics.
Another option is to project the class back to itself, and only provide the columns you want.
var table = context.MyTable.Where(mt => mt.Id == id)
.Select(mt => new MyTable
{
Prop1 = mt.Prop1,
Prop2 = mt.Prop2
})
.FirstOrDefault();
string p1 = table.Prop1;
string p2 = table.Prop2;
Effectively, you get the strong typing of a DTO without having to create/maintain another class. All columns not specified will be populated with the default value of the column's type.
It translates to the following in SQL:
SELECT TOP(1) m.Prop1, m.Prop2 FROM MyTable m WHERE m.Id = #id
Which indeed gives a performance boost over Find() assuming you're not specifying all the columns.
EDIT: As Gert mentioned, use with caution, as it's not always obvious when a "partial entity" is being passed around.
You can use free AutoMapper's ProjectTo<> extension, so the query would look like this:
context.OrderLines
.Where(ol => ol.OrderId == orderId)
.ProjectTo<OrderLineDTO>(configuration)
.ToList();

Dynamic Join of multiple entities based on some filter in Entity Framework

I am pretty new to Entity Framework and LINQ and I have an entity with more than 10+ other associated entities (one-to-many relationships). Now, I'm planning to make a search page in my application in which users could select which fields (i.e. those 10+ tables) they want to be considered when searching.
Now, I'm trying to write a query to achieve the above goal. Any help how I could sort this out using LINQ method syntax? I mean, to write a multiple join query based on user's choice. (i.e. which of Class1, Class2, ... to join with main Entity to finally have all the related fields in one place). Below is a sample code (Just a hunch, in fact)
if(somefilter#1)
result = db.Companies.Join(db.Channels, p => p.Id, k => k.CId,
(p, k) => new {Company = p, Channels=k});
if(somefilter#2)
result = result.Join(db.BusinnessType, ........);
if(somefilter#3)
result = result.Join(db.Values, .......);
For complex queries it may be easier to use the other LINQ notation. You could join multiple entities like this:
from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new {
myEntity.Id,
myEntity.Name,
myOtherEntity.OtherProperty,
oneMoreEntity.OneMoreProperty
}
You can join in other entities by adding more join statements.
You can select properties of any entity from your query. The example I provided uses a dynamic class, but you can also define a class (like MyJoinedEntity) into which you can select instead. To do it you would use something like:
...
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
}
EDIT:
In case when you want to have conditional joins you can define MyJoinedEntity with all the properties you will need if you were to join everything. Then break up the join into multiple methods. Like this:
public IEnumerable<MyJoinedEntity> GetEntities() {
var joinedEntities = from myEntity in dbContext.MyEntities
join myOtherEntity in dbContext.MyOtherEntities on myEntity.Id equals myOtherEntity.MyEntityId
join oneMoreEntity in dbContext.OneMoreEntities on myEntity.Id equals oneMoreEntity.MyEntityId
select new MyJoinedEntity {
Id = myEntity.Id,
Name = myEntity.Name,
OtherProperty = myOtherEntity.OtherProperty,
OneMoreProperty = oneMoreEntity.OneMoreProperty
};
if (condition1) {
joinedEntities = JoinWithRelated(joinedEntities);
}
}
public IEnumerable<MyJoinedEntity> JoinWithRelated(IEnumerable<MyJoinedEntity> joinedEntities) {
return from joinedEntity in joinedEntities
join relatedEntity in dbContext.RelatedEntities on joinedEntity.Id equals relatedEntity.MyEntityId
select new MyJoinedEntity(joinedEntity) {
Comments = relatedEntity.Comments
};
}

Silverlight using Entity to LINQ on Joins

I have the following code which works and gets me the data for a single entity.
LoadOperation<TimeForm> loadoperation = _cc.Load(_cc.GetTimeFormsQuery()
.Where(o => o.Start>= weekrange.startdate
&& o.End<= weekrange.enddate
&& o.USERID== "TEST"));
However I have 3 tables which connect to this TimeForm table, in sql my query looks like this:
SELECT T* FROM TimeForm
INNER JOIN CLIENT ON TimeForm.CODEID= CLIENT.CODEID
INNER JOIN RATE ON TimeForm.RATEID= RATE.RATEID
INNER JOIN TASK ON TimeForm.TASKID = TASK.TASKID
How can this be possible with the above syntax? I need some values from these tables.
Try something like this:
var query = context.TimeForm.
Join(context.CLIENT,
t => t.CODEID, c => c.CODEID ,
(t, c) => new
{
PropertyA = t.ColumnA,
PropertyB = c.ColumnB
}).Join(context.RATE,
b => b.RATEID, r => r.RATEID,
(b, r) => new
{
PropertyC = b.ColumnC,
PropertyD = r.ColumnD
}).Join(context.TASK,
x => x.TASKID, t => t.TASKID,
(x,t) => new
{
PropertyE = x.ColumnE,
PropertyF = t.ColumnF
});
PropertyA, B, etc are just properties present in the type, which you use to store the data returned from the query. Whereas ColumnA, B, etc are columns present in the tables involved in the join. You can substitute actual values for these in your query.
You need to go to the Domain Services file (where the GetTimeFormsQuery() is defined). It'll look something like:
public IQueryable<TimeForm> GetTimeForms() {
return this.Context.TimeForm;
}
, and add to it so it is like this:
public IQueryable<TimeForm> GetTimeForms() {
return this.Context.TimeForm
.Include("Client") // Assuming your property to see the client is called "Client"
.Include("Rate") // Same for "Rate"
.Include("Task"); // and "Task
}
Or whatever the navigation properties are called in your TimeFrom entity.
Silverlight doesn't do lazy loading, so you'll have to explicitly include these properties in the query in the domain service. Also it's probably wise to create an extra method on the domain service that accepts the start and end date and userid so that you don't pull the entire table over the wire every time.
public IQueryable<TimeForm> GetTimeFormsWithStartAndEnd(DateTime start, DateTime end, string userId) {
return this.Context.TimeForm
.Include("Client") // Assuming your property to see the client is called "Client"
.Include("Rate") // Same for "Rate"
.Include("Task") // and "Task
.Where(o => o.Start>= start
&& o.End<= end
&& o.USERID== userId));
}
After you rebuild your web-project, you'll have a method called GetTimeFormsWithStartAndEndQuery in your silverlight with these 3 as parameters.
Goodluck!

How can I query this hierarchical data using LINQ?

I have 3 kinds of objects: Agency, BusinessUnit and Client (each with their own respective table)
In terms of hierarchy, Agencies own BusinessUnits, and BusinessUnits own Clients.
I have 3 C# POCO Objects to represent them (I usually select new {} into them, rather than use the LINQ generated classes):
public class Agency
{
public IEnumerable<BusinessUnit> BusinessUnits { get; set; }
}
public class BusinessUnit
{
public IEnumerable<Client> Clients { get; set; }
}
public class Client
{
public int NumberOfAccounts { get; set; }
public Decimal AmountOfPlacement { get; set; }
public Decimal AvgBalance { get; set; }
public Double NeuPlacementScore { get; set; }
}
You can see that Agencies contain a list of BusinessUnits, and BusinessUnits contain a list of Clients.
I also have a mapping table called BAC_Map in the database which says which owns which, and it looks something like this:
How can I construct a query, so I can query for and return a list of Agencies? Meaning that, I want each Agency to have its list of BusinessUnit objects set, and I want the list of BusinessObjects to have its list of Clients set.
I can do basic LINQ queries, but this is a little over my head concerning the Map table and the multiple? queries.
How could I construct a method like GetAllAgencies() which would query, for not only all agencies, but populate its BusinessUnits that Agency owns, and the Clients those BusinessUnits own?
Edit: Any tips or info is appreciated. Do I need to do joins? Does this need to be multiple queries to return an Agency list, with its submembers populated?
If you drop all four tables (Agency, BusinessUnit, Client, Map) on the linq to sql designer, and draw relationships from Map to the other three, there will be some useful properties on Map.
//construct a query to fetch the row/column shaped results.
var query =
from m in db.map
//where m.... ?
let a = m.Agency
let b = m.BusinessUnit
let c = m.Client
// where something about a or b or c ?
select new {
AgencyID = a.AgencyID,
AgencyName = a.Name,
BusinessUnitID = b.BusinessUnitID,
ClientID = c.ClientID,
NumberOfAccounts = c.NumberOfAccounts,
Score = c.Score
};
//hit the database
var rawRecords = query.ToList();
//shape the results further into a hierarchy.
List<Agency> results = rawRecords
.GroupBy(x => x.AgencyID)
.Select(g => new Agency()
{
Name = g.First().AgencyName,
BusinessUnits = g
.GroupBy(y => y.BusinessUnitID)
.Select(g2 => new BusinessUnit()
{
Clients = g2
.Select(z => new Client()
{
NumberOfAccounts = z.NumberOfAccounts,
Score = z.Score
})
})
})
.ToList();
If approriate filters are supplied (see the commented out where clauses), then only the needed portions of the tables will be pulled into memory. This is standard SQL joining at work here.
I created your tables in a SQL Server database, and tried to recreate your scenario in LinqPad. I ended up with the following LINQ statements, which basically result in the same structure of your POCO classes:
var map = from bac in BAC_Maps
join a in Agencies on bac.Agency_ID equals a.Agency_ID
join b in BusinessUnits on bac.Business_Unit_ID equals b.Business_Unit_ID
join c in Clients on bac.Client_ID equals c.Client_ID
select new
{
AgencyID = a.Agency_ID,
BusinessUnitID = b.Business_Unit_ID,
Client = c
};
var results = from m in map.ToList()
group m by m.AgencyID into g
select new
{
BusinessUnits = from m2 in g
group m2 by m2.BusinessUnitID into g2
select new
{
Clients = from m3 in g2
select m3.Client
}
};
results.Dump();
Note that I called map.ToList() in the second query. This actually resulted in a single, efficient query. My initial attempt did not include .ToList(), and resulted in nine separate queries to produce the same results. The query generated by the .ToList() version is as follows:
SELECT [t1].[Agency_ID] AS [AgencyID], [t2].[Business_Unit_ID] AS [BusinessUnitID], [t3].[Client_ID], [t3].[NumberOfAccounts], [t3].[AmountOfPlacement], [t3].[AvgBalance], [t3].[NeuPlacementScore]
FROM [BAC_Map] AS [t0]
INNER JOIN [Agencies] AS [t1] ON [t0].[Agency_ID] = [t1].[Agency_ID]
INNER JOIN [BusinessUnits] AS [t2] ON [t0].[Business_Unit_ID] = [t2].[Business_Unit_ID]
INNER JOIN [Clients] AS [t3] ON [t0].[Client_ID] = [t3].[Client_ID]
Here is a screenshot of the results:
alt text http://img411.imageshack.us/img411/5003/agencybusinessunitclien.png
If you are doing this with direct LINQ to SQL, there is no way to do this without some kind of recursion, whether you do it yourself or you hide it behind an extension method. Recursive SQL is very bad (many round trips, many single queries).
There are two options here. One is to pull the entire table(s) with the hierarchy into memory and use LINQ to Objects on it. Leave the "details" tables in SQL. If you have less than several thousand entities, this is probably the most efficient way to go. You can keep a single copy of the table(s) in cache and refresh them when necessary. When you need to fetch more detailed data from the DB for a single record, you can reattach that entity from your cached hierarchy to a new DataContext and fetch it.
The other option is to use a more complex relationship model in your database. Storing parent only by nature demands recursion, but you can use the adjacency list model to construct a single query which can span many levels of inheritance. This will mean your LINQ to SQL queries become less intuitive (querying against Entity.Right and Entity.Left isn't quite as pretty as Parent or Children...) but you can do in one query what might take hundreds or thousands in the literal recursive approach.

Categories

Resources