I have the following table in sql
I am trying to select all records with a status of onboard, but however you can see thatfor user '43d658bc-15a7-4056-809a-5c0aad6a1d86' i have two onboard entries. How do i select the firstordefault entry if the user has more than one record?
so my expected outcome should be
this is what i have that gets all the records
public async Task<List<Model>> HandleAsync(Query query)
{
return (await _repository.GetProjectedListAsync(q=> q.Where(x => x.Status== "Onboard").Select( Project.Log_Model))).ToList();
}
internal static partial class Project
{
public static readonly Expression<Func<Log,Model>> Log_Model =
x => x == null ? null : new Model
{
Id = x.Id,
UserId = x.UserId,
Status = x.Status,
created=x.Created,
DepartmentId=x.DepartmentId
};
}
i tried the following
var test = (await _repository.GetProjectedListAsync(q=> q.Where(x => x.Status== "Onboard").Select( Project.Log_Model))).ToList();
var t= test.FirstOrDefault();
return t;
but i get an error "can not implicitly convert type model to system.collections.generic.list"
The following code example demonstrates how to use FirstOrDefault() by passing in a predicate. In the second call to the method, there is no element in the array that satisfies the condition. You can filter out an entry you are looking for directly in the FirstOrDefault().
If you don't want to have duplicates, you could also use Distinct.
string[] names = { "Hartono, Tommy", "Adams, Terry",
"Andersen, Henriette Thaulow",
"Hedlund, Magnus", "Ito, Shu" };
string firstLongName = names.FirstOrDefault(name => name.Length > 20);
Console.WriteLine("The first long name is '{0}'.", firstLongName);
string firstVeryLongName = names.FirstOrDefault(name => name.Length > 30);
Console.WriteLine(
"There is {0} name longer than 30 characters.",
string.IsNullOrEmpty(firstVeryLongName) ? "not a" : "a");
/*
This code produces the following output:
The first long name is 'Andersen, Henriette Thaulow'.
There is not a name longer than 30 characters.
*/
Related
In my project I have pagination.
My code looks like:
var queryable = _context.Projects.Where(x => x.IsDeleted == false).AsQueryable();
queryable = ProjectListExtension.Sort(request, queryable);
queryable = ProjectListExtension.Search(request, queryable);
var projects = await queryable
.Skip(request.Offset ?? 0).Take(request.Limit ?? 50)
.ProjectToListAsync<ProjectListForUiDto>(_mapper.ConfigurationProvider);
return new ProjectsList
(
projects,
await queryable.CountAsync(cancellationToken: cancellationToken)
);
My extension method for search looks like:
public static class ProjectListExtension
{
public static IQueryable<Project> Search(ProjectListQuery query, IQueryable<Project> queryable) =>
query switch
{
{Search:not null} => queryable.Where(x => x.Name.ToLower().Contains(query.Search.ToLower()) || x.Organization.Name.ToLower().Contains(query.Search.ToLower())),
_ => queryable
};
}
I try offset 1 and limit 3, simply I'd like search from 2nd page element which exists in 1st page.
In result it returns empty result, but in total Count I can see 1 (which means found this data). I don't quite understand I suppose paging should be last part of operation
P.S. We have 5 result
1st page
element1
element2
element3
2nd page
element4
element5
I am on 2nd page, and when I type element1 I'd like to see result.
Here is my code:
var value = query.Select(
a => new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = (a.TOPLAM_FIYAT != null).ToString().Count(),
COUNT2 = (a.TOPLAM_FIYAT == null) ? ToString().Count() : 0
}
).ToList();
Here is the error:
System.NotSupportedException: 'LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression.'
and CHART_MODEL
public class CHART_MODEL
{
public int COUNT1 { get; set; }
public int COUNT2 { get; set; }
public string TEXT { get; set; }
}
My question is: I have to calculate null and non null values with Count(). but it doesn't allow me to write. I don't know how to rearrange linq query code in correct way with Count().
This is an error you get because EF doesn't know how to execute the .ToString() method in sql.
Methods like that which EF doesn't have correlated methods in plain sql, will throw an error like the one you received.
What you can do is use an Immediate Execution.methods like: ToList or ToArray will force the execution of the query, hence loading the data into memory and when the data is loaded the rest of the operators are performed using Linq to objects on that the data that was brought in memory.
So, you can load the data using toList() and after that use the rest of the code and methods like toString() won't throw an error.
query.toList().Select(..)
You can read more about Deferred Execution vs Immediate Execution here:
https://samirbehara.com/2016/01/04/deferred-execution-vs-immediate-execution-in-linq/
The simplest way would be something like
var value = new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = query.Where(a=>a.TOPLAM_FIYAT != null).Count(),
COUNT2 = query.Where(a=>a.TOPLAM_FIYAT == null).Count()
};
Note, this will issue two select statements.
If you really want to avoid the two select statements, you have to introduce a dummy grouping such as
var value = (from r in query
group r by 1 into results
select new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = results.Where(a => a.TOPLAM_FIYAT != null).Count(),
COUNT2 = results.Where(a => a.TOPLAM_FIYAT == null).Count()
}).Single();
Just complementing on #sgmoore's answer, you can place you filter condition inside the .Count method itself:
var value = new CHART_MODEL
{
TEXT = "xyz",
COUNT1 = query.Count(a => a.TOPLAM_FIYAT != null),
COUNT2 = query.Count(a => a.TOPLAM_FIYAT == null)
};
There is the new is null and is not null syntax introduced in C#9. It reads really nice, but it is still not recommended by Microsoft to use in Linq-to-SQL.
I'm working on small app for fetching products/articles, and I wrote a method that's getting articles by type. (types are contained in request arg).
What I'm trying to achieve is: append all results (from all if conditions if they are satisfied) to one main list which should be returned to customer..
When I'm debugging and checking query it says its returning type is IQueryable<Article> so basically my question is how can I append multiple IQueryables into one which should be returned to user..
This code below is not working because result is always empty..
I've tried also with var result = new List<Article>(); and later result.AddRange(query); and I've changed also return type to
return await result.AsQueryable().ToListAsync(); but obviously something breaks somewhere and I get an empty array at the end.
public async Task<IEnumerable<Article>> GetArticlesByType(ArticleObject request)
{
var result = new Article[] { }.AsQueryable();
IQueryable<ArticleDTO> query = null;
if (request.Food.HasValue && (bool)request.Food)
{
// Return type of query is IQueryable<Article>
query = _context.Articles.Where(x => x.Active == true && x.ArticleType == ArticleType.Food).Select(x => new Article
{
Id = x.Id,
ArticleName = x.ArticleName
});
// Here I just wanted if this condition is satisfied to add values to my result
result.AsQueryable().Union(query);
}
if (request.Drink.HasValue && (bool)request.Drink)
{
query = _context.Articles.Where(x => x.Active == true && x.ArticleType == ArticleType.Drink).Select(x => new Article
{
Id = x.Id,
ArticleName = x.ArticleName
});
// Again if there are any values in query add them to existing result values
result.AsQueryable().Union(query);
}
if (request.Candy.HasValue && (bool)request.Candy)
{
// When its candy I want also articles from food category
query = _context.Articles.Where(x => x.Active == true && x.ArticleType == ArticleType.Food || x.ArticleType == ArticleType.Candy).Select(x => new Article
{
Id = x.Id,
ArticleName = x.ArticleName
});
// Again if there are values in query add them to existing result
result.AsQueryable().Union(query);
}
//At the end return result and all the values in case all conditions were satisfied
return await result.ToListAsync();
}
Try with result.AsQueryable().Union(query.ToList());. This will fetch the object from database. So far query contains references to objects in database and not in your memory
I have a simple linq lambda statement
Interactions = new BindableCollection<InteractionDTO>(ctx.Interactions.Where(x => x.ActivityDate > DateTime.Today)
.Select(x => new InteractionDTO
{
Id = x.Id,
ActivityDate = x.ActivityDate,
subject = x.Subject,
ClientNames = x.Attendees.Count == 1 ? x.Attendees.FirstOrDefault().Person.CorrespondenceName :
x.Attendees.FirstOrDefault().Person.CorrespondenceName : "Multiple attendees"
}));
This will give me the first Client Name, I'm trying to have it appear First 2 attendees followed by dots. I tried this
ClientNames = x.Attendees.Count == 1 ?
x.Attendees.FirstOrDefault().Person.CorrespondenceName :
x.Attendees.FirstOrDefault().Person.CorrespondenceName +
x.Attendees.Skip(1).FirstOrDefault().Person.CorrespondenceName + " ..."
But I get this error:
The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
You could try ordering first, as the message suggests.
I'm not sure what your Attendees class looks like, but assuming it has an ID field:
x.Attendees.OrderBy(a => a.ID)
.Skip(1)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault() + " ..."
A couple other notes:
I swapped your Select and FirstOrDefault statements. The way you've currently got it, if FirstOrDefault() returns null, then Person.CorrespondenceName will throw an exception.
But now if no record is found, you'll end up with just "...". You might want to adjust your first Where clause to filter out records that have no correspondance name, and then change FirstOrDefault() to First().
EDIT:
That should steer you in the right direction (I hope). This may be more what you're looking for, assuming it can actually be translated into a valid SQL statement:
ClientNames = x.Attendees.Any()
? x.Attendees.Count == 1
? x.Attendees.Select(a => a.Person.CorrespondenceName).FirstOrDefault()
: x.Attendees.Count == 2
? string.Join(", ", x.Attendees.OrderBy(a => a.ID).Take(2)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault()
: string.Join(", ", x.Attendees.OrderBy(a => a.ID).Take(2)
.Select(a => a.Person.CorrespondenceName).FirstOrDefault() + " ..."
: "No client name available";
// put this in your namespace...
public static class EnumerableExtension
{
public static TSource SecondOrDefault<TSource>(this IEnumerable<TSource> source)
{
var iterator = source.GetEnumerator();
if (iterator.MoveNext() && iterator.MoveNext() )
return iterator.Current;
else
return default( TSource );
}
}
// Usage... var thing = list.SecondOrDefault();
I have the following Linq query:
result.Partials.Where(o => o.IsPositive).Min(o => o.Result)
I get an exception when result.Partials.Where(o => o.IsPositive) does not contains elements. Is there an elegant way to handle this other than splitting the operation in two and checking for null? I have a class full of operations like this one.
EDIT: The question is related with LINQ to Objects.
This is the Exception I'm getting (translated it says: The sequence is empty):
A short summary of the calculation of a Min
- No mediation (Exception!)
var min = result.Partials.Where(o => o.IsPositive).Min(o => o.Result);
This is your case: if there are no matching elements, then the Min call will raise an exception (InvalidOperationException).
- With DefaultIfEmpty() -- still troublesome
var min = result.Partials.Where(o => o.IsPositive)
.Select(o => o.Result)
.DefaultIfEmpty()
.Min();
DefaultIfEmpty will create an enumeration over the 0 element, when there are no elements in the list. How do you know that 0 is the Min or if 0 stands for a list with no elements?
- Nullable values; A better solution
var min = result.Partials.Where(o => o.IsPositive)
.Min(o => (decimal?)o.Result);
Here Min is either null (because that's equal to default(decimal?)) or the actual Min found.
So a consumer of this result will know that:
When result is null then the list had no elements
When the result is a decimal value then the list had some elements and the Min of those elements is that returned value.
However, when this doesn't matter, then min.GetValueOrDefault(0) can be called.
You can use the DefaultIfEmpty method to ensure the collection has at least 1 item:
result.Partials.Where(o => o.IsPositive).Select(o => o.Result).DefaultIfEmpty().Min();
You can't use Min (or Max) if the sequence is empty. If that shouldn't be happening, you have a different issue with how you define result. Otherwise, you should check if the sequence is empty and handle appropriately, eg:
var query = result.Partials.Where(o => o.IsPositve);
min = query.Any() ? query.Min(o => o.Result) : 0; // insert a different "default" value of your choice...
Yet another way to express it in LINQ is to use Aggregate:
var min = result.Partials
.Where(o => o.IsPositive)
.Select(o => o.Result)
.Aggregate(0, Math.Min); // Or any other value which should be returned for empty list
Since LINQ lacks methods like MinOrDefault() and MaxOrDefault(), you can create them by yourself:
public static class LinqExtensions
{
public static TProp MinOrDefault<TItem, TProp>(this IEnumerable<TItem> This, Func<TItem, TProp> selector)
{
if (This.Count() > 0)
{
return This.Min(selector);
}
else
{
return default(TProp);
}
}
}
Therefore, if the collection has values, the Min() is calculated, otherwise you get the property type's default value.
An example of use:
public class Model
{
public int Result { get; set; }
}
// ...
public void SomeMethod()
{
Console.WriteLine("Hello World");
var filledList = new List<Model>
{
new Model { Result = 10 },
new Model { Result = 9 },
};
var emptyList = new List<Model>();
var minFromFilledList = filledList.MinOrDefault(o => o.Result)); // 9
var minFromEmptyList = emptyList.MinOrDefault(o => o.Result)); // 0
}
NOTE 1: you don't need to check if the This parameter is null: the invoked Count() already checks that, and it throws the same Exception that you would throw.
NOTE 2: This solution is good only in situations where the Count() method is cheap to call. All .NET collections are fine since they are all very efficient; it could be a problem for particular customized/non-standard collections.