SQL subquery result in LINQ and Entity Framework Code First - sql

I want to make a query that'll return me entities and additionally a number of one of their associated entities. For example:
select *, (select COUNT(*) from Forms where Contact_Id = Contacts.Id)
as FormsCount from Contacts;
My Contact entity has a property named FormsCount, but it isn't mapped since there's no column named like that in the table. Is it possible to write one LINQ query that'll return me Contact entities with the additional FormsCount property filled in?
Alternatively, I'd be happy if I could get the FormsCount values in a separate field and I can copy them to the entities manually. The result from the query could be in this form for example:
{
Contact Contact;
int FormsCount;
}
Then I can iterate over the results and copy FormsCount to Contact. Maybe this can be achieved by using projections?
I know how to do that using 2 queries:
a) fetch contact entities first
b) fetch pairs or contact ID and FormsCount for contacts returned in the first query.
But I'd like to do that using one query. Also, I don't want the FormsCount property to be always filled in my Contact entity, I want to have a control over that. Any ideas?
Thanks,
Michal

You are right about the projection.
If Contact has a navigation property Forms you can project:
from c in context.Contacts
select new { Contact = c, FormsCount = c.Forms.Count() }
If not, you'll have to use a subquery:
from c in context.Contacts
select new
{
Contact = c,
FormsCount = context.Forms.Count(f => f.Contact_Id == c.Id)
}
EF will handle both situations in one SQL query.

Related

SQL - Visual Studio - fill table if related data is empty

In my data source "Properties" is linked to "tenants" and I want to fill a table using a SQL query where tenant does not exist for that property.
In other words, "where that property is vacant."
What is the SQL statement for something like this?
SELECT tblProperties.Type, tblProperties.PropertyID, tblProperties.Street, tblProperties.Unit, tblProperties.City, tblProperties.State, tblProperties.Zip, tblProperties.Description, tblTenant.TenantID
FROM dbo.tblProperties
JOIN tblTenant
ON tblProperties.PropertyID = tblTenant.PropertyID
WHERE tblTenant.TenantID = ''
Properties and Tenants are both tables in your database?
Are they joined by a cross-reference table or...? It would be helpful to see the table structures.
Assuming that's the case, you just want something like:
SELECT * FROM Properties
WHERE PropertyId NOT IN
(SELECT PropertyId FROM PropertyTenants)
Try something like this:
SELECT Property.PropertyID, Property.TenantID
FROM Property LEFT JOIN Tenant ON Property.[TenantID] = Tenant.[TenantID]
WHERE (((Tenant.TenantID) Is Null));
This should show what properties don't have Tenants, Suggest you first try it with JUST the id fields, then add in the other fields later to keep the query simple and help with troubleshooting. If you have the potential to have multiple tenants who have offices in more than one property, this is a many-to-many relationship that is best documented in an index table.

How can i join 2 sharepoint 2010 list

When using SP 2007 and needed to do a join i just write the tables to a sql table and then use sql to join the tables.
What i really need to do is quite simple.
I have a master list and another list that users insert records too lets say a child list.
When a user opens up the master list and clicks on an item i insert a record including their usernames to the child list
All i want to show the users(based on login names) is the items they haven't read and what items.
In sql i could have done something like e.g
Select * from master where not in(select from child where username ='blalal')
Any ideas.Not sure if to do it on the client or in the object model.
Sure CAMl doesn't have joins
Thanks in advance
You can do joins in CAML queries as long as the two lists are related by a lookup field.
http://msdn.microsoft.com/en-us/library/ie/ee539975.aspx
Or you can use the Camelot .NET Connector from Bendsoft to JOIN any fields. It supports typical CRUD commands, including LEFT and INNER joins and UNION.
Check this approach very easy to join as many list as you want: Link
cawl_QueryBuilder cawl = new cawl_QueryBuilder();
cawl.Select("Users_Title");
cawl.Select("Users_Age");
cawl.Select("Users_Sex");
cawl.Select("CarBrand");
cawl.Join("UsersList";"OwnerColumn");
cawl.Get('UserCarsList');
StringBuilder Result = new StringBuilder();
foreach (SPListItem item in cawl.ListItemCollection())
{
Result.Append(item["Users_Title"].ToString() +
item["Users_Age"].ToString() +
item["Users_Sex"].ToString() +
item["CarBrand"].ToString());
}
Label1.Text = Result .ToString();

Efficiently getting a count of child records when returning parent record(s)

I am using Entity Framework 4.1 using the Code First approach. I have two entities that exhibit a parent-child relationship. To provide a concrete example, imagine I have a Category entity that has zero-to-many Product entities associated with it. I have set up navigation properties on both sides (in my example, the Category entity would have an ICollection<Product> property while the Product entity has a Category property).
Now, when I get Category entities I want to also get back a count of the number of children records for each category. I am aware I can do:
Category category = dbContext.Categories.Single(...);
int productCount = category.Products.Count();
But I am concerned because the resulting SQL that gets sent to the database depends on whether I use lazy or eager loading.
In the first case (lazy loading), the call to the Products collection prompts EF to run a SQL query like:
SELECT ... all columns ...
FROM Products
WHERE CategoryID = #CategoryID
In the second case (eager loading), the products are loaded when the category information is retrieved so there is no second query to the database, but the downside is that if I'm not at all interested in products (other than their count) then I'm bringing back a lot of unneeded data.
What I'd like it to have the best of both worlds: namely, the ability to have just one database query and one that uses SELECT COUNT(*) rather than one that gets all of the columns from the table. In short, I'd like SQL like the following to be sent to the database:
SELECT ... all category columns ...,
(SELECT COUNT(*) FROM Products p WHERE p.CategoryID = c.CategoryID)
FROM Categories c
WHERE c.CategoryID = ...
Is that at all possible with EF or is what I want a pipe dream?
Not sure, but maybe try this:
var result = db.Categories.Where(x => x.CategoryId == categoryId)
.Select(y => new
{
Count = y.Products.Count(),
Category = y
})
.Single();
//result.Count
//result.Category
Yes, this is possible with EF. You can also create a view model to show the information with the child counts as properties. This article cover how to do that.
http://www.ozkary.com/2015/04/entity-framework-associated-table.html

How to build custom PLINQO query by multiple id's?

Here's my table structure
Places
PlaceId PK
Name
...
PlaceCategories
CatId PK
Name
...
PlaceCats
PlaceId PK
CatId PK
Here's my query that pulls Places based on category id (table join)
public static IQueryable<Places> ByPlaceCat(this Table<Places> table, Expression<Func<PlaceCats, bool>> predicate) {
var db = (DataContext)table.Context;
var innerBizBase = db.PlaceCats.Where(predicate);
return db.Places.Join(innerBizBase, a => a.PlaceId, ab => ab.PlaceId, (a, ab) => a);
}
I use it like this:
places = Db.Places.ByPlaceCat(a => a.CatId == 5);
But I want to be able to pull based on a List<int> of category id's. Looking through the generated PLINQO code, a query that pulls by multiple PlaceId's (but not using a joined table) looks like this:
public static IQueryable<Places> ByPlaceId(this IQueryable<Places> queryable, IEnumerable<long> values)
{
return queryable.Where(p => values.Contains(p.PlaceId));
}
How could I essentially merge those two queries, to let me pass in a List<int> of CatId's to query by? This LINQ/PLINQO query is melting my brain. Thanks in advance!
You would need to write a extension method like this:
public static IQueryable<Places> ByPlaceCats(this Table<Places> table, IEnumerable<int> catIds)
{
var db = (TestDataContext)table.Context;
var places = (from placeCat in db.PlaceCats
join place in db.Places on placeCat.PlaceId equals place.PlaceId
where catIds.Contains(placeCat.CatId)
select place);
return places;
}
Please note that the PlaceCats table could be made into a ManyToMany relationship by adding two foreign keys to the proper tables. Once this change has been made than PLINQO will automatically generate the correct code and will create a link between the two tables skipping the intermediary table. So you could get a collection of PlaceCategories associated to the current Places entity by accessing a property on the Places entity.
Please remember to contact us if you have any questions and be sure to check out the community forums located here and PLINQO forums here.
Thanks
-Blake Niemyjski (CodeSmith Support)

NHibernate - how to get an item that is not referenced by an item in another table

Let's say I have a class Voucher:
public class Voucher
{
public Guid Id {get;set;}
public DateTime DateAvailable {get;set;}
}
and a class Entry
public class Entry
{
public Guid Id {get;set;}
public Voucher Voucher {get;set;}
// ... other unrelated properties
}
How can I create an NHibernate Criteria query that finds the first available voucher that is NOT currently assigned to an Entry?
The equivalent SQL would be
select
v.Id, v.DateAvailable
from
Voucher v
left join Entries e on e.VoucherId = v.Id
where
v.DateAvailable <= getutcdate() and
e.Id is null
Edit: I'm still unable to figure this one out. The Voucher table has no reference to the Entries table, but I need to find the first voucher (by date order) that has not been assigned to an entry. This seems like such a simple task, but everything I keep reading about using NHibernate criteria left joins requires the Voucher object to have a property that references the entry. Surely there's a way to invert the query or add a reference property to the Voucher object without modifying the database to have each table reference the other.
Edit 2: For what it's worth, I don't think it's possible to do what I was trying to do without some modifications. I eventually got a query to work using the Entry as the primary criteria with the Voucher as a sub-criteria, but then UniqueResult returned null, even if the data was there. I guess it just couldn't make the association.
In case anyone runs into this, I ended up making a foreign key in each table that references the other and using the References<> mapping to associate the two. It's not idea, because I have to manually set each entity's sub property to the other to make the association bidirectional, but it at least works without a ton of changes.
Translating your SQL literally:
var voucher = NHibernateSessionManager.Session.CreateCriteria<Voucher>("v")
.Add(Restrictions.Le("v.DateAvailable", DateTime.UtcNow))
.CreateCriteria("Entries", "e")
.Add(Restrictions.IsNull("e.Id"))
.SetMaxResults(1)
.UniqueResult<Voucher>();
Now if I understand this correctly there may be an alterantive.
If the statement: "..finds the first available voucher that is NOT currently assigned to an Entry..." is the same with the statment "finds the first available voucher that has no entries..." (since the voucher is the entity that has many entries... according to your classes) then you could do:
var voucher = NHibernateSessionManager.Session.CreateCriteria<Voucher>()
.Add(Restrictions.IsEmpty("Entries"))
.Add(Restrictions.Le("DateAvailable", DateTime.UtcNow))
.SetMaxResults(1)
.UniqueResult<Voucher>();
...assuming that you have mapped the Entries property in the Voucher entity.
But maybe I got it wrong...
The following should work using HQL:
IQuery query = session.CreateQuery(#"
FROM
Voucher v LEFT JOIN
Entry e
WHERE
e.ID IS NULL AND
v.DateAvailable <= :now
");
query.SetParameter("now", DateTime.Now);
// If you want to restrict to only the first result
query.SetMaxResults(1);
Voucher v = query.UniqueResult<Voucher>();
// Otherwise, get a list of results...
// List<Entry> results = query.List<Entry>();
From memory, might not work:
session.CreateQuery<Entry>().CreateAlias("Voucher","v").Add(Restrictions.And(
Restrictions.Lt("DateAvailable",DateTime.Now),
Restrictions.Eq("v.Id",null")
).List<Voucher>();