What are the two ways I can write this statement with LINQ? - c#

I understand that there are two different ways I can write LINQ code. Can someone show me the two ways for this simple code block. Which is the most commonly used or considered most easy to debug
var subTopics = _subTopicService.GetSubTopics(Id);
var subTopicsSelect = (from subTopic in subTopics
select new
{
id = subTopic.SubTopicId,
name = subTopic.Name
});

Since your query consists solely of a from and select clause, all you need to do to convert this to fluent syntax is call .Select.
In fluent syntax, that would be:
var subTopicsSelect = subTopics.Select(x =>
new
{
id = x.SubTopicId,
name = x.Name
});
Further Reading
How to: Write LINQ Queries in C#

You have displayed the first way "SQL-like syntax" the second would be "Lambda syntax":
subTopics.Select(s => new { id = s.SubTopicId, name = s.Name });

This really confuses me as I have a completely different way to select here are the two methods:
var emailsToSend = db.emailQueues.Where(
e => e.sent == false
).Take(5);
var emailsToSend2 = from e2 in db.emailQueues
.Take(5)
.Where(
e => e.sent == false
)
select e2;
They both seem to do exactly the same thing but i prefer the syntax of the first method. Its easier to remember.

Related

The nested query is not supported

I have a query that is similar to the following (my actual query has three sections like this and then Concats them together and applies some additional filters and sorting).
var articles = from p in Repository.Query<Product>()
let article = p.Article
let office = p.TariffCategory.Office
where p.IsDeleted == false
select new
{
OfficeId = office.Id,
Office = office.Name,
Category = p.TariffCategory.Description,
ArticleId = article.Id,
Article = article.Title,
Destinations = p.ProductDestinations.Select(d => new { Id = d.DestinationId, Name = d.Destination.Description }),
GlobalDestinations = p.AllDestinationsInOffice,
article.LastReviewedDate,
article.CreatedDate,
article.CreatedByEmployee
};
Everythings seems right except my assignment to Destinations. That line produces the following error.
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
If I remove that line, everything works as expected. Is there any way to perform a query like this?
I had a bit of a think, and rather than doing a join as I suggested, it may make sense to start the query at ProductDestination. What we're interested in is a row for each product+destination combination, much like you'd see via regular SQL queries. Once we've got that, we can apply grouping to the result so that we're closer to the representation you had
var data = Repository.Query<ProductDestination>()
.Where(pd => !pd.Product.IsDeleted)
.Select(pd =>
new {
Product = pd.Product,
Destination = pd.Destination,
})
.GroupBy(pd => pd.Product)
//I'm not in a position to test if EF will successfully run the below, so .ToList()
//May not be required. However, the bulk of the work is done in the database, anyway.
//.ToList()
.Select(g => new {
OfficeId = g.Key.TariffCategory.Office.Id,
Office = g.Key.TariffCategory.Office.Name,
Category = g.Key.TariffCategory.Description,
ArticleId = g.Key.Article.Id,
Article = g.Key.Article.Title,
Destinations = g.Select(gg => new { Id = gg.Destination.DestinationId, Name = gg.Destination.Description }),
GlobalDestinations = g.Key.AllDestinationsInOffice,
g.Key.Article.LastReviewedDate,
g.Key.Article.CreatedDate,
g.Key.Article.CreatedByEmployee
});
I'm pretty sure the above should work without the ToList(), but I'm not confident to say it 100% will work. However, as noted, the bulk of the work is done in the database, the final projection shouldn't be too intensive, even if it's done in memory. However, should the ToList() be required, we would need to modify the GroupBy to return all fields we select via Key, otherwise we're going to have issues with lazy loading and N+1 queries.

Implement this "not in" where clause in LINQ

I have a table here where it gets popuplated with ActiveDirectory users every night. This list included generic AD accounts used for a variety of purposes.
Examples of lastnames of generic accounts:
vendor testing
IT support
Dept1 Printer
Visitor1
Visitor2
Guest1
Guest2 and etc
I want to retrieve all records ignoring these records. Something like
select * from table where lastname not like '%visitor%'
and lastname not like "%support%"
and so on I made this query but it does not do substring comparison.
List<String> _ignoreList = new List<String> { "visitor", "test" };
IQueryable<String> _records =
from _adUserDatas in _adUserDataDBDataContext.ADUserDatas
where
_adUserDatas.accountActive.ToLower().Contains("yes")
&& _adUserDatas.staffStudentType.ToLower().Contains("neither")
&& !_ignoreList.Contains(_adUserDatas.lastName)
orderby _adUserDatas.username
select _adUserDatas.username;
Here's the resulting SQL being sent to SQL Server.
{
SELECT[t0].[username]
FROM[dbo].[ADUserData] AS[t0]
WHERE
(LOWER([t0].[accountActive]) LIKE# p0)
AND
(LOWER([t0].[staffStudentType]) LIKE# p1)
AND
(NOT([t0].[lastName] IN(#p2, #p3)))
ORDER BY[t0].[username]
}
in LINQ query above, it did not ignore a record with the lastname "only for testing acct".
Any ideas on how to implement it using LINQ?
I've search the net but nothing came up.
Thanks a lot
That is because your are checking whether ignoreList contains the LastName, try doing it the other way.. i.e Whether LastName conatins anything from the ignoreList..
&& !_ignoreList.Any( il => _adUserDatas.lastName.Contains( il ) )
This way it will check whether "only for testing acct" contains anything from { "visitor", "test" }
Hm.. it could be hard to get to work like predicate with in clausule.. My solution would be other:
var queryable = from _adUserDatas in _adUserDataDBDataContext.ADUserDatas
where
_adUserDatas.accountActive.ToLower().Contains("yes")
&& _adUserDatas.staffStudentType.ToLower().Contains("neither")
orderby _adUserDatas.username
select _adUserDatas.username;
foreach (var ignore in _ignoreList)
{
var localIgnore = ignore;
queryable = queryable.Where(userName => !userName.Contains(localIgnore))
}
var result = queryable.ToList();
The answer from pwas lead me to one that works for my situation. PredicateBuilder which is mentioned in lots of topics here in SOF.com. http://www.albahari.com/nutshell/predicatebuilder.aspx
Here's the final code:
ADUserDataDBDataContext _adUserDataDBDataContext = new ADUserDataDBDataContext();
IQueryable<String> _records = null;
Expression<Func<ADUserData,Boolean>> _whereClause = PredicateBuilder.True<ADUserData>();
_whereClause = _whereClause.And(ADUserData => ADUserData.accountActive.ToLower().Contains("yes"));
foreach (var _item in _ignoreList)
{
_whereClause = _whereClause.And(ADUserData => !ADUserData.lastName.ToLower().Contains(_item));
}
_records = _adUserDataDBDataContext.ADUserDatas
.Where(_whereClause)
.Select(ADUserData => ADUserData.fan);
return _records.ToList();

How to get last category given a following route alias in a self referencing table

My problem solving like this such a code;
string permalink = "computers/hp/computers"; //example for similarity
List<string> aliases = permalink.Split('/').ToList();
Category cat = db.Categories.SingleOrDefault(c => c.Alias == aliases.First());
aliases.Remove(aliases.First());
foreach (string alias in aliases)
{
cat = cat.Categories.SingleOrDefault(c => c.Alias == alias);
}
return cat;
But this is sent many query..
How do I make one time?
If I understand what you want, you can use the Enumerable.Aggregate method. You will have to start with a 'root' category, that encompasses all of db.Categories. That's pretty easy to mock up though. Try this:
var aliases = permalink.Split('/');
var category = new Category // start with a 'root' category
{
Id = 0,
Categories = db.Categories
};
var cat = aliases.Aggregate(category, (c, a) => c.Categories.SingleOrDefault(x => x.Alias == a));
Firstly, if the category table is small it is sometimes better to just grab the whole table and do the selection in memory (perhaps using p.w.s.g's answer).
If the table is large, then a Stored procedure would probably be better than Linq.
But, if you really want to do it in Linq, then I think the only way is to repeatedly add a join to same table.
The following is assuming that your relationship is between fields called ParentID and Id. I have also changed your string permalink to better illustrate the order.
You first need a little helper class
public class Info
{
public Category category;
public int? recordID;
}
then your main code
string permalink ="computers1/hp/computers2";
var aliases = permalink.Split('/');
var query = dc.Categories.Where(r=>r.Alias == aliases[aliases.Length-1])
.Select(r=> new Info { category = r, recordID = r.ParentID});
for(int i = aliases.Length -2 ; i >= 0; i--)
{
string alias = aliases[i];
query = query.Join(dc.Categories ,
a => a.recordID , b => b.Id , (a,b) => new { a , b} )
.Where(r=>r.b.Alias == alias)
.Select(r=> new Info { category = r.a.category, recordID = r.b.ParentID});
}
return query.SingleOrDefault().category;
As you can see the lambda syntax of join is (IMHO) horrendous and I usually try to avoid it, but I can't think of anyway of avoiding it here.
Since I can't test it, it could be totally wrong (maybe I've mixed up the ID, ParentID or my a's and b's ), so it is important to test this and to test how it performs.
I think the sql produced should be something like
SELECT * from Categories AS t0
INNER JOIN Categories AS t1 ON t0.ParentID = t1.id
INNER JOIN Categories AS t2 ON t1.ParentID = t2.id
WHERE t2.Alias = 'computers1'
AND t1.Alias = 'hp'
AND t0.Alias = 'computers2'
The more sections or aliases, then the more joins there are.
Now that you've see all that, you probably want to avoid using this method -)
I'll probably just add to your confusion :), but let me just throw an idea...
Let me just say it that this doesn't work (exactly per your specs) - and it's not the solution but might help you simplify things a bit.
var query =
(from cat in db.Categories
where cat.Alias == "mainAalias"
from subcat in cat.Categories
where aliases.Contains(subcat.Alias)
orderby subcat.Alias descending
select subcat);
query.FirstOrDefault(); // or something
This should produce one relatively simple query
(e.g. SELECT...FROM...JOIN...WHERE... AND...IN...ORDERBY...).
e.g. if you give it 'cat1', 'cat2'...'cat6' - out of cat1 - to cat100 - it gives 'cat6'...'cat1' (I mean the aliases)
However it has a major 'flaw' with the 'sorting' - your specs require a sort that is the order of 'aliases' as they come - which is a bit unfortunate for queries. If you could somehow enforce, or define an order, that could be translated to SQL this (or similar) might work.
I'm assuming - that your 'aliases' are pre-sorted in an ascending
order - for this query to work. Which they are not, and I'm aware of
that.
But I think that your idea is not clearly defined here (and why all of us are having problems) - think through, and optimize - simplify your requirements - and let your C# tier help e.g. by pre-sorting.
You could also try some form of 'grouping' per cat.Alias etc. - but I think the same 'sorting problem' persists.

Linq-to-SQL ToDictionary

I have the following LINQ query:
var allocations =
from ta in dc.TransactionAllocations
where ta.Allocated == false
group ta by new { ta.ReceiptReference, ta.Customer } into tag
select new
{
Customer = tag.Key.Customer,
ReceiptReference = tag.Key.ReceiptReference,
Invoices = tag.ToDictionary(a => new AllocationDictionaryKey()
{
ID = a.ID,
InvoiceReference = a.InvoiceReference
},
a => a.Amount)
}
But when I try to execute this, the ToDictionary call fails as it's not a supported LINQ-to-SQL operator. The only way around this I have seen is to call ToDictionary at the end of the query, but I only want one property of my anonymous type to be a dictionary!
Any ideas on how to go about doing this?
Have a look at using AsEnumerable. This is designed to get round operators that are not supported by a specific platform. It means that the data will be processed where the code is rather than where the data is though.
Invoices = tag.AsEnumerable().ToDictionary(a => new AllocationDictionaryKey() { ID = a.ID, InvoiceReference = a.InvoiceReference }, a => a.Amount)
Quite old, but here goes.
I solved my problem with
from ta in dc.TransactionAllocations.AsEnumerable()
i.e. directly making the datatable as Enumerable.

Linq to SQL: DataTable.Rows[0]["ColumnName"] equivalent

Consider this:
var query = from r in this._db.Recipes
where r.RecipesID == recipeID
select new { r.RecipesID, r.RecipesName };
How would i get individual columns in my query object without using a for-loop?
Basicly: how do I translate DataTable.Rows[0]["ColumnName"] into Linq syntax?
It's really unclear what you are looking for, as your two samples are compatible.
As close as I can figure, what you want is:
var rows = query.ToList();
string name = rows[0].RecipesName;
string name = this._db.Recipes.Single(r => r.RecipesID == recipeID).RecipesName;
This is the way to go about it:
DataContext dc = new DataContext();
var recipe = (from r in dc.Recipes
where r.RecipesID == 1
select r).FirstOrDefault();
if (recipe != null)
{
id = recipe.RecipesID;
name = recipe.RecipesName;
}
Sorry, misunderstood your question. As others are saying, you can use ToList() to get a List back. An alternative if all you need is the first one, just use:
query.First().ColumnName
or if you want to avoid an exception on empty list:
var obj = query.FirstOrDefault();
if (obj != null)
obj.ColumnName;
Original Answer (so the comment makes sense):
Use Linq to Datasets. Basically would be something like:
var query = from r in yourTable.AsEnumerable()
select r.Field<string>("ColumnName");

Categories

Resources