Is it possible to Load References when instead of using the code below:
SqlExpression<Customer> q = db.From<Customer>();
q.Join<Customer,CustomerAddress>((cust,address) => cust.Id == address.CustomerId);
List<Customer> dbCustomers = db.LoadSelect(q);
Using this:
public class KpiTotal : IKpiTotal
{
public DateTime Date { get; set; }
public int TeamId { get; set; }
public Team Team { get; set; }
public int AccountId { get; set; }
public Account Account { get; set; }
public double Total { get; set; }
}
var result = dbCon.SelectFmt<KpiTotal>(#"select convert(date, t.TransactionDate) [Date], tm.TeamId,a.AccountNumber, count(distinct(t.RequisitionNumber)) Total
from task.tblTransactions t
inner join task.tblRequisitions r on r.RequisitionNumber = t.RequisitionNumber
inner join task.tblAccounts a on a.AccountNumber = r.AccountNumber
inner join Team tm on tm.DivisionId = a.DivisionId
where t.TransactionTypeNumber = 201 and a.IsActive = 1
and t.TransactionDate between {0} and {1}
group by convert(date, t.TransactionDate), tm.TeamName, a.AccountName
order by 1,2 desc", dateRange.Start, dateRange.End);
Because my result object (KpiTotal) has references to two child tables, and I would like to automatic load the references, instead of getting it with a foreach block.
I'm assuming you want to load in Team and Account from the above query. The LoadSelect method sniffs the POCO model and generates a query that pulls back all related DB records based on the foreign key relationships to the core object you're querying. It generates a query similar to this for each referenced / joined POCO (very pseudo-coded):
SELECT * FROM Team /* Related POCO */
WHERE Team.Id IN (SELECT TeamId FROM [original query with WHERE clase])
Basically, it does a single query to bring back all Teamss or Accounts.
With ServiceStack.OrmLite v4.0.40, there is now a new Merge extension method that will stitch together object references based in a more manual process.
In your case, you can query your KpiTotal results, then run just two separate queries to fetch back Team and Account lists, then merge them in. Basically:
var result = dbCon.SelectFmt<KpiTotal>(/* gnarly SQL */);
var teams = dbCon.Select(/* get all relevant teams */);
var accounts = dbCon.Select(/* get all relevant accounts */);
result.Merge(teams);
result.Merge(accounts);
Debug.WriteLine(result.Dump()); // Output to console / debug window, whatever
Related
I have a DbSet class:
public class Manufacturer
{
public Guid Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
I know I can use Skip() and Take() to get limited manufacturers. But my requirement is to get limited Products of all the manufacturers. I'm using something like this but it's not working
var manufacturers = await _context.Manufacturers.Where(x => x.Products.Take(10))
.ToListAsync();
PS: I'm using Lazy Loading (Not eager loading)
Compile error is:
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable<Domain.Product>' to 'bool'
Cannot convert lambda expression to intended delegate type because
some of the return types in the block are not implicitly convertible
to the delegate return type
How can I achieve to get all the manufacturers but limited products in them?
I believe there is no way to do this directly with a queryable source. You can manage it in memory.
var manufacturers = await _context.Manufacturers.Include(m => m.Products).ToListAsync();
foreach(var m in manufacturers)
{
m.Products = m.Products.Take(10).ToList();
}
This will get all products for each manufacturer from the DB and then keep only the first 10.
You can load the Manufacturer entity without the Product list first (so without an Include() call) and then run a separate query to load only the products you want for a specific Manufacturer entity. EF will automatically update the navigation properties. See the following example (authors can have multiple posts in this example):
using (var context = new MyContext())
{
Author author = context.Author.First();
Console.WriteLine(context.Post.Where(it => it.Author == author).Count());
context.Post.Where(it => it.Author == author).Take(2).ToList();
Console.WriteLine(author.Posts.Count());
}
This will generate the following output:
3
2
Even though there are three entries available in my test database, only two are actually read. See the generated SQL queries:
For the Author author = context.Author.First(); line:
SELECT `a`.`Id`, `a`.`Name`
FROM `Author` AS `a`
LIMIT 1
For the context.Post.Where(it => it.Author == author).Count() line:
SELECT COUNT(*)
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1
For the context.Post.Where(it => it.Author == author).Take(2).ToList(); line:
SELECT `p`.`Id`, `p`.`AuthorId`, `p`.`Content`
FROM `Post` AS `p`
INNER JOIN `Author` AS `a` ON `p`.`AuthorId` = `a`.`Id`
WHERE `a`.`Id` = 1
LIMIT 2
However, you have to do this trick for each individual Manufacturer entity, that it loads only ten associated Product entities. This can result in 1+N SELECT queries.
Try the longer way:
_await _context.Manufacturers.Select(x =>
{
x.Products = x.Products.Take(10).ToList();
return x;
}).ToListAsync();
Here is my code the issue I have is the less than comparison in the On clause ... Since Linq doesn't allow this .... Migrating down into the where clause wont work as I am comparing one of the fields to null.
Here is the sql query (THE a.UserID= is hardcoded for now)
SELECT A.Policy, A.Comments, A.EventDTTM, A.Status, A.Reason, A.FollowUp
FROM PP_PolicyActivity A
LEFT JOIN PP_PolicyActivity B
ON(A.Policy = B.Policy AND A.EventDTTM < B.EventDTTM)
WHERE A.UserID = 'Ixxxxxx'
AND B.EventDTTM IS NULL AND a.status = 'open - Pending'
order by A.EventDTTM DESC
I need the result set from the above query as an IEnumerable list to populate a view
I'm tasked with rebuilding an old VB ASP NET that has a set of standing production databases behind it ... i don't have the option of changing the db design. I connecting to the server and database and this query was going against a table on that database.. the model also reflects the layout of the actual table.
The problem is with A.EventDTTM < B.EventDTTM - I can't move this to the where clause as I also have to deal with B.EventDTTM IS NULL in the where clause.
I need to retool the query someway so that it is 'linq' friendly
public class PolicyActivityModel
{
public string Policy { get; set; }
public int PolicyID { get; set; }
public string Status { get; set; }
public string Reason { get; set; }
public string Comments { get; set; }
public DateTime EventDTTM { get; set; }
public string UserID { get; set; }
public DateTime FollowUp { get; set; }
}
Company policy prohibits me from showing the connection string.
I am extremely new to Linq, Any help greatly appreciated
thank you
You can use the navigation property after you get the policy from the database.
var policy = DbContext.First(x => x.Id == 1000);
var otherPolicies = policy.ConnectedPolicies.Where(p => ...);
It's weird being a self-join but this is the most direct translation to Linq:
var query = from leftPP in PP_PolicyActivity
join rightPP in PP_PolicyActivity
on new { Policy = leftPP.Policy, EventDTTM = leftPP.EventDTTM }
equals new { Policy = rightPP.Policy, EventDTTM = rightPP.EventDTTM }
into pp from joinedRecords.DefaultIfEmpty()
where leftPP.UserId == 1
&& leftPP.EventDTTM < rightPP.DTTM)
&& rightPP.EventDTTM == null
&& leftPP.status = "open - Pending"
select new
{
leftPP,
rightPP
}
I free typed this, without models or Intellisense, thus there might be some smaller errors.
You could add the order by in that clause, but it's also still an IQUeryable, so I'd leave it.
And then, to get a List of models:
var results = query.OrderByDescending(x => x.EventDTTM).ToList();
The actual join is lines 2,3,4 and 5. It's verbose and "backwards" from SQL, and most importantly uses anonymous types. Accessing indidual properties will look something like:
results[0].leftPP.PolicyId
I have two models, Benefit and SchemeName
Benefit -
[Key]
public int BenefitID { get; set; }
public string BenefitName { get; set; }
public string BenefitDescription { get; set; }
public virtual ICollection<SchemeName> SchemeNames { get; set; }
SchemeName
[Key]
public int SchemeNameID { get; set; }
public string Name { get; set; }
public virtual ICollection<Benefit> Benefits { get; set; }
This has created three tables in the database Benefits, SchemeNames and a joining table called SchemeNameBenefits.
I am trying to populate a droplownlist that contains only the SchemeNames associated with a certain Benefit but am not sure how I can do this, can I reference the join table in my code?
I started with the following (which returns all SchemeNames)
private void PopulatePensionSchemeName(object selectedPensionSchemeName = null)
{
var schemeNameQuery = from d in db.SchemeNames
orderby d.SchemeNameID
select d;
ViewBag.PensionSchemeNameID = new SelectList(schemeNameQuery, "SchemeNameID", "Name", selectedPensionSchemeName);
}
But I'm not sure how I can add this clause. Any pointers?
You'll need the key of the Benefit object you want the SchemeNames for. The query you're probably looking for is:-
var benefitId = // However you get your benefit Id
var schemaNameQuery = from b in db.Benefits
from s in b.SchemeNames
where b.BenefitId == benefitId
select s;
Or in the extension method syntax:-
var schemaNameQuery = db.Benefits.Where(b.BenefitId == benefitId)
.SelectMany(b => b.SchemeNames);
Which produces the following SQL:-
SELECT ...
FROM [dbo].[SchemeNameBenefits] AS [Extent1]
INNER JOIN [dbo].[SchemeNames] AS [Extent2]
ON [Extent1].[SchemeName_Id] = [Extent2].[SchemeNameId]
WHERE [Extent1].[Benefit_Id] = #p__linq__0
Alternately you can use:-
var benefitId = // However you get your benefit Id
var schemeNameQuery = from d in db.SchemeNames
where d.Benefits.Any(x => x.Id == benefitId)
orderby d.SchemeNameId
select d;
This produces the following SQL:-
SELECT ...
FROM ( SELECT ... FROM [dbo].[SchemeNames] AS [Extent1]
WHERE EXISTS (SELECT 1 AS [C1]
FROM [dbo].[SchemeNameBenefits] AS [Extent2]
WHERE ([Extent1].[SchemeNameId] = [Extent2].[SchemeName_Id])
AND ([Extent2].[Benefit_Id] = #p__linq__0)))
AS ...
ORDER BY [Project2].[Id] ASC
Note that in both cases the generated SQL references your junction table even though it isn't part of your EF model.
If you already have the Benefit object, of course, you can get its SchemeNames more simply by using:-
var schemeNameQuery = benefit.SchemeNames;
I would do this by using a Junction Table. The Junction Table is your Joining Table. It will consist of two foreign Keys, SchemeNameID & BenefitID.
Check out this website for more on Junction Tables:
http://megocode3.wordpress.com/2008/01/04/understanding-a-sql-junction-table/
It helped me out a lot.
[Disclaimer] Conditional on there existing or adding the SchemeNameBenefits model the following should work.
So it seems like the SchemeNameBenefits table is a many-to-many map, in this case use the benefit ID to get a collection of scheme ID's
var schemeIds = db.SchemeNamesBenefits.Where(map => map.BenefitID == id)
.Select(map => map.SchemeNameID).ToArray();
Then pull back all the scheme name information for these scheme ID's
var result = db.SchemeNames.Where(scheme => schemeIds.Contains(scheme.SchemeNameID))
.OrderBy(scheme => scheme.SchemeNameId)
.Select(scheme => scheme.Name).ToArray();
Or in one query
var result = db.SchemeNamesBenefits.Where(map => map.BenefitID == id)
.SelectMany(map => db.SchemeNames
.Where(scheme => map.SchemeNameID == scheme.SchemeNameID)
.OrderBy(scheme => scheme.SchemeNameId)
.Select(scheme => scheme.Name)
.AsEnumerable())
.ToArray()
I have a one class to one table mapping; unfortunately this table has 110+ columns, and queries take a long time process, especially when most of the time I only want to view <10 columns.
My problem is that the queries are dynamically generated based on what the user wants to look at. I can't really create different mappings with different columns because there would be a very large number of combinations. I'm using the criteria API to generate the queries. Can I also use this to only select the columns the user wants? Or some other method?
Thanks
Easy to do with LINQ (assuming you're using NHibernate 3.0 or later):
var products = from p in Session.Query<Product>()
where // ...some query (snip)
select new
{
Name = p.ProductName,
Description = p.ShortDesc,
Price = p.Price,
Units = p.Quantity
};
Also, if you're using HQL, you can just select the columns you need similar to using T-SQL, but use a Transformer to get a strongly typed object back:
First create a class with your narrowed down columns:
public class ProductReport
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Units { get; set; }
}
Then your query:
string hql = "select p.ProductName as Name, p.ShortDesc as Description ...(snip) " +
"from Product p " +
"where ...some query (snip)";
IQuery query = Session.CreateQuery(hql)
.SetResultTransformer(Transformers.AliasToBean<ProductReport>());
IList<ProductReport> products = query.List<ProductReport>();
Just sure make the aliases in your query (as Name, as Description etc.) match the property names in your class.
In addition to the example Tim gave you can do something like this:
IList selection =
session.QueryOver<Cat>()
.Select(
c => c.Name,
c => c.Age)
.List<object[]>();
Above example was taken from: http://nhforge.org/blogs/nhibernate/archive/2009/12/17/queryover-in-nh-3-0.aspx
Use a ProjectionList to select the columns you want. See here for the examples.
I am executing a SQL Query using Nhibernate, below is the code in which I use for this:
public ArrayList getDocumentsForApproval(string ReleaseId)
{
string query = string.Format("SELECT distinct doc.Id, doc.Name as Doc, doc.url as url, suser.Name as Author, ds.name, CONVERT(VARCHAR(11), doc.DateEntered, 101) as DateEntered FROM dbo.Documents doc INNER JOIN DevelopmentSteps ds ON doc.TypeId = ds.Id INNER JOIN DocumentTrackingItems dti ON doc.Id = dti.DocumentId INNER JOIN TrackingItems ti ON dti.ItemStepId = ti.Id INNER JOIN dbo.Releases rl ON ti.ReleaseId = rl.BugTrackerName left outer join (select * from users) as suser on doc.AuthorUserid = suser.Id WHERE doc.DateEntered IS NOT NULL AND doc.DateApproved IS NULL AND rl.ID = '{0}'", ReleaseId);
ISession session = NHibernateHelper.GetCurrentSession();
ArrayList document =(ArrayList) session.CreateSQLQuery(query).List();
return document;
}
The error information I receive is as follows:
**Exception Details:**
NHibernate.QueryException: Return types of SQL query were not specified [SELECT distinct doc.Id, doc.Name as Doc, doc.url as url, suser.Name as Author, ds.name, CONVERT(VARCHAR(11), doc.DateEntered, 101)
What could be the issue? ---- Thanks
You are fundamentally misunderstanding NHibernate. NHibernate is not like the TypeDataSource classes that return you DataSets/DataTables that aren't real business objects.
NHibernate is meant to work with fully owned objects so you would have something similar to
Public Class Document
{
public virtual decimal Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime DateEntered { get; set; }
... so forth
}
Then you need to create a mapping file either manually or by code generation for raw HBM mappings or use a tool on top of NH to build mappings programmatically with FluentNHibernate or ConfORM.
You need to learn the basics of NHibernate before attempting to query this is a decent introductory post: http://www.fincher.org/tips/Languages/NHibernate.shtml
And then for querying you can use http://www.castleproject.org/ActiveRecord/documentation/v1rc1/usersguide/hql.html for reference.
In most cases you should use entity objects instead of custom queries.
If you really need a custom query, the following example might be useful
public IEnumerable<GeoAreaIdAndCode> ReadAllGssCodes()
{
var query = "select GeoAreaID,Code from GeoAreaAlternativeCode where AlternativeCodeType=" + (int)GeoAreaAlternativeCodeType.GssCode;
var result = Owner.Session.CreateSQLQuery(query)
.AddScalar("GeoAreaID",NHibernateUtil.Int32)
.AddScalar("Code",NHibernateUtil.String)
.SetResultTransformer(Transformers.AliasToBean(typeof (GeoAreaIdAndCode)))
.List<GeoAreaIdAndCode>();
return result;
}
public class GeoAreaIdAndCode
{
public int GeoAreaID { get; set; }
public string Code { get; set; }
}
The secret is to use:
CreateSQLQuery("Your query with alias").AddScalar(...)
In AddScalar you have to define your NH types for output.
See ref here