SQL query:
SELECT M.MailItemId, m.Subject, ISNULL(m.SendCC, ''), ISNULL(attachments.counter, 0) Counters, M.CreationDate
FROM MailItem AS M
LEFT OUTER JOIN (
SELECT MailItemId, COUNT(MailItemId) counter
FROM Attachment Group By MailItemId
) AS attachments ON M.MailItemId = attachments.MailItemId
There are two models MailItem and Attachment.
MailItemId is primary key in MailItem and foreign key in Attachment model.
Want to convert above query to a lambda expression.
So you have a table of MailItems and a table of Attachments. There is a straightforward one-to-many relation between MailItems and Attachments: every MailItem has zero or more Attachments, and every Attachment belongs to exactly one MailItem, namely the MailItem that the foreign key refers to.
Quite often, people want "MailItems with their Attachments", they don't want a left outer join:
MailItem 1 with Attachments A, C
MailItem 2 with Attachments B, D, F
MailItem 3 without Attachments
MailItem 4 with Attachment E
You on the other hand, seem to prefer the following result:
MailItem Attachment
1 A
2 B
1 C
3 <null>
4 E
2 D
2 F
This solution is seldom wanted. That's why there is no standard LINQ method for it.
To get MailItems with their Attachment, use GroupJoin. If you want the left outer join, use SelectMany after the GroupJoin.
MailItems with their Attachments
var mailItemsWithTheirAttacments = dbContext.MailItems.GroupJoin(
dbContext.Attachments, // GroupJoin MailItems with Attachments
mailitem => mailItem.MailItemId, // from every MailItem take the primary key
attachment => attachment.MailItemId, // from every Attachment take the foreign key
// parameter resultSelector: for every MainItem and all its Attachments
// make one new object
(mailItem, attachmentsOfThisMailItem) => new
{
// Select the MailItem properties that you plan to use
Id = mailItem.Id,
Sender = mailItem.Sender,
...
Attachments = attachmentsOfThisMailItem.Select(attachment => new
{
// Select only the Attachments properties that you plan to use:
Id = attachment.Id,
Format = attachment.Format,
...
// not needed, you already know the value:
// MailItemId = attachment.MailItemId,
})
.ToList(),
})
Left Outer Join
var result = dbContext.MailItems.GroupJoin(dbContext.Attachments,
mailitem => mailItem.MailItemId,
attachment => attachment.MailItemId,
(mailItem, attachmentsOfThisMailItem) => new
{
MailItem = mailItem,
Attachments = attachmentsOfThisMailItem,
})
// make it a left outer join, using SelectMany
.SelectMany(groupJoinResult => groupJoinResult.Attachments,
(groupJoinResult, attachment) => new
{
MailId = groupJoinResult.MailItem.Id,
Sender = groupJoinResult.MailItem.Sender,
...
AttachmentId = attachment.Id,
Format = attachment.Format,
...
});
Use the virtual ICollections
If you have followed the entity framework conventions, you will have classes similar to the following:
class MailItem
{
public int Id {get; set; }
...
// every MailItem has zero or more Attachments (one-to-many)
public virtual ICollection<Attachment> Attachments {get; set;}
}
class Attachment
{
public int Id {get; set; }
...
// every Attachment belongs to exactly one MailItem, using foreign key:
public int MailItemId {get; set;}
public virtual MailItem MailItem {get; set;}
}
You might use other identifiers, but the important part are the virtual properties.
In entity framework the columns of the tables are represented by the non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
The foreign key is a column in table Attachments. Hence property Attachment.MailItemId is not virtual. Property Attachment.MailItem represent the relation, hence it is virtual.
If you have defined the relations as properties, you can use them to do the GroupJoin or the Left Outer Join for you:
var mailItemsWithTheirAttachments = dbContext.MailItems.Select(mailItem => new
{
Id = mailItem.Id,
Sender = mailItem.Sender,
...
Attachments = mailItem.Attachments
.Where(attachment => ....) // only if you don't want all Attachments
.Select(attachment => new
{
Id = attachment.Id,
...
})
.ToList(),
});
Entity framework knows the relation, and will do the correct (group-)join for you.
IMHO this looks way more natural than a GroupJoin, whether or not followed by a selectMany
Related
I try to use the Criteria API to create a dynamic JPA-Query. I need to find a key-value pair inside a map of the object.
The Object looks similar to the following one.
public class item {
private UUID id;
#Column(name = "properties", columnDefinition = "nvarchar")
private Map<String, Object> properties;
}
I thought I could use the MapJoin join or joinMap:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Item> criteriaQuery = cb.createQuery(Item.class);
Root<Item> itemRoot = criteriaQuery.from(Item.class);
criteriaQuery.select(itemRoot);
Join<String, Object> properties = itemRoot.join("properties");
// or
//MapJoin<Item, String, Object> properties = itemRoot.joinMap("properties");
Predicate pre1 = cb.equal(properties.get(ITEM_PROPERTY_1), "123");
Predicate pre2 = cb.equal(properties.get(ITEM_PROPERTY_2), "456");
Predicate propertiesPredicate = cb.and(pre1, pre2);
criteriaQuery.where(propertiesPredicate);
Item item = em.createQuery(criteriaQuery).getSingleResult();
But I've read that this is only for associations.
On the join i get an:
IllegalArgumentException: Requested attribute was not a map.
So could sb explain to me, how I will be able to find a key-value pair in a map with the Criteria API?
Edit: I am not able to change anything in the DB.
So I need to guess a little bit because you didn't show us your DB Table, that's why I answer a little bit more freely.
And as a disclaimer: it might be easier and it would be more efficient to query a real table instead of an serialized object/json.
But I would do it this way:
The Table in MSSQL:
create table ${schema}.myTable
(
id bigint identity
constraint PK_myStuff
primary key,
properties nvarchar(max) not null
) go
The Java entity (draft):
public class Item extends AbstractPersistable<...> {
#Column(name = "properties", columnDefinition = "nvarchar")
private String properties;
}
The Java Specification:
protected Specification<Item> customFilter(String filterArg) {
return (root, query, cb) ->
cb.like(root.get(Item_.properties), filterArg);
}
This way your query searches the properties for a string pattern.
Info:
https://vladmihalcea.com/sql-server-json-hibernate/
I've got an ASP.NET MVC application using EF in C#. I can write and run my query correctly in SQL Server. In EF, I'm not sure how to accomplish the same thing.
Tables A, B and C. C references B which References A.
The query looks like this:
Select *
From C
Where C.bID in (Select B.bID
From B
Where B.aID = '<unique Key In A>')
The sub-query returns multiple B keys. Which then pass it through and look up the ID's in C. In short I'm looking up all data in C related to a key in A.
I just don't know how to put that into EF language. Or if it's even possible. The IN operator is what's throwing me on the conversion.
Example:
var exampleList = _context.C
.Where(l => l.bId in (_context.B
.Where(p => p.aId = keyInA)));
"in" doesn't work here. Obviously. After I wrote this post I made sure of it.
Note: A:B has a 1:Many relation. B:C has a 1 to many relation. All IDs and keys are GUIDs
Set up the navigation property between C & B, and between A & B. This is pretty much the whole point of using Entity Framework rather than just an alternative to ADO and SQL queries.
C contains a BId, so set up a B navigation property:
public class C
{
public int CId { get; set; }
[ForeignKey("B")]
public int BId { get; set; }
public virtual B B { get; set; }
}
B contains an AId, so similar:
public class B
{
public int BId { get; set; }
[ForeignKey("A")]
public int AId { get; set; }
public virtual A A { get; set; }
}
Now, to write your query:
var cs = context.Cs.Where(x => x.B.AId == id);
or
var cs = context.Cs.Where(x => x.B.A.AId == id); // can filter on other "A" fields this way.
I mean, to avoid the use of navigation properties, you may as well just write Sprocs and use ADO. It is possible in cases where you have unrelated tables or absolutely need to avoid a navigation property. Use a Join between context.Cs and context.Bs:
var cs = context.Cs.Join(context.Bs, c => c.BId, b => b.BId, (c, b) => new { C = c, AId = b.AId })
.Where(x => x.AId == aid)
.Select(x => x.C);
IMO seriously ugly working with joins in EF, but sometimes necessary. If you can use a navigation property I'd highly recommend it over using something like that regularly.
I've got the problem with searching database through duplicate list values.
First I search all occurrences by given string.
var parentIdList = await _context.ECATEGORIES
.Where(a => a.NAME.ToLower().Contains(partOfName.ToLower()))
.ToListAsync(ct);
then
I retrieve all names when given PARENTID of parentIdList equals database ID
var mainName = await _context.ECATEGORIES
.Where(a=> parentIdList.Any(p=>p.PARENTID==a.ID) )
.Select(s => s.NAME)
.ToListAsync(ct);
My problem is that, sometimes property PARENTID is duplicated.
For example PARENTID = {1,1,2,2,4,5,6}
then result is mainName = {"a","b","c","d","e"}
But I want mainName = {"a", "a","b","b","c","d","e"}
So you have a sequence of Categories, where every Category has an Id, a ParentId and a Name. You also have a string partOfName
You want the Names of Categories. All Categories? No. only those Categories that have a Parent which has a Name that looks like partOfName (looks like is represented by the use of the function contains in your code)
I'm not sure if you use entity framework. Your use of _context seems a hint to this. In that case, It would be easier to use the virtual parent properties. See later.
If you do not use entity framework, I'd advise to fetch your data in one query: join the elements with their parents, and keep only the join results where the parent meets your contains predicate:
string lowerCasePartOfName = partOfName.ToLower(); // for efficiency: do this only once
IQueryable<Category> categories = myDbContext.Categories;
IQueryable<Category> validParents = myDbContext.Categories
.Where(category => category.Name.ToLower().Contains(lowerCasePartOfName))
Now join every Category with the validParentIds on Category.ParentId = Parent.Id
var query = Categories.Join(validParents, // join categories with validParents
category => category.ParentId, // from each category take the ParentId
parent => parent.Id, // from each valid parent take the Id
(category, parent) => new // when they match make one new object
{
CategoryName = category.Name,
});
Note, until now the query has been made, it has not been executed yet. If you want you can concatenate them into one big LINQ query. I'm not sure if that would increase performance very much; however, it would decrease readability.
var result = query.ToListAsync();
Entity Framework Solution
If you use entity framework, your Category class would be like:
class Category
{
public int Id {get; set;}
public string Name {get; set;}
// every Category has zero or one Parent category:
public int? ParentId {get; set;}
public Category Parent {get; set;}
// every Category has zero or more Children, each of them has this Category as Parent
public virtual ICollection<Category> Children {get; set;}
}
You query will be much simpler:
Give me all names of Categories whose Parents have a name that looks like partOfName
string lowerCasePartOfName = partOfName.ToLower();
var result = myDbContext.Categories
// keep only the Categories whose name looks like:
.Where(category => category.Parent.Name.ToLower().Contains(lowerCasePartOfName))
.Select(category => category.Name);
I have a autocompletebox that is used to select a destination for a car booking program. For the itemssource of the autocomplete box I am trying to set it to all the previous destinations entered. The problem is that I can't work out how to return a single column 'Destination' of distinct destination values from my Booking class, e.g.
var query = from bk in ObjectContext.Bookings select new DestinationDTO { Destination = bk.Destination };
return query.Distinct();
. I have tried creating a shared DestinationDTO class to return just the single column but can't work out how to get this to inherit from Entity!!
Any ideas?
You need to have a property with a [Key] attribute in your DestinationDTO class. Then RIA services will be able to generate a corresponding class on the client side.
public class DestinationDTO
{
[Key]
public Guid Id { get; set; }
public string Destination { get; set; }
}
Then just do this:
var query = from bk in ObjectContext.Bookings
select new DestinationDTO { Destination = bk.Destination, Id = Guid.NewGuid() };
return query.Distinct();
I am using CastleProject ActiveRecord.
I have the following property in my class:
[HasMany(typeof(Order), Table = "Orders", ColumnKey = "OrderId")]
internal IList<Order> Orders
{
get;
set;
}
In case Orders table does not contain any orders, Orders property is null. Can I somehow point ActiveRecord that it should create empty list instead of returning null, without giving up autoproperty?
Not exactly what you want, but couldn't you instantiate an empty list in the constructor:
public MyClass()
{
Orders = new List<Order>();
}