.OrderBy() / .OrderByDescending() with .FirstOrDefault()/.First() - c#

I have list of results from table named "product".
Id User ProductName ArrivalDate
---- ----- ------------ ------------
1 James Shoes 05/07/2016
2 Jenny TShirt 05/01/2018
3 James Pants 13/05/2017
i would like to sort the result by descending order based on ArrivalDate where User is "James", which mean it should return to me the third row result.
However, if i do as below:
return List(spec).OrderByDescending(x=>x.ArrivalDate).FirstOrDefault();
the result i got is still the first one, appereciate if anyone could guide on this.
Below is the sample code:
public class EfRepository<T> : IRepository<T>, IAsyncRepository<T> where T : BaseEntity
{
public T GetSingleBySpec(ISpecification<T> spec)
{
return List(spec).FirstOrDefault();
}
}
public class ProductSpecification : BaseSpecification<NewProducts>
{
public ProductSpecification(string User)
: base(b => b.User == User) //User = James
{
}
}
public class ProductService : IProductService
{
public void getOrderProductDetails
{
var data = _productRepository.GetSingleBySpec(new ProductSpecification(user));
}
}

I don't see a filter for the user and you are ordering by user. Try this.
return List(spec).Where(x => x.User == "James")
.OrderByDescending(y => y.ArrivalDate)
.FirstOrDefault();

If you want to get just a specific user last arrival time you should add extra condition to where clause
FirstOrDefault generates a query like this:
Select top 1 ....

It seems to me that you are trying to encapsulate some basic Linq functionality using your own methods, but you are only complicating things, at the same time you are making it limited and hard to use, instead of flexible and easy to use.
This is what I would do instead:
public void getOrderProductDetails
{
var data = _productRepository
.Where(x => x.User == user)
.OrderByDescending(x => x.ArrivalDate)
.FirstOrDefault();
// process data ...
}

You can also put the "filter" ~inside the FirstOrDefault method.
I show a generic example (both ways) below.
Also note the string-compare for case insensitivity.
ICollection<Employee> employees = (blah blah blah);
Employee firstEmployeeWhereClause = employees.Where(z => z.LastName.Equals("Smith", StringComparison.OrdinalIgnoreCase)).OrderByDescending(x => x.EmployeeSurrogateKey).FirstOrDefault();
Employee secondEmployeeNoWhereClause = employees.OrderByDescending(x => x.EmployeeSurrogateKey).FirstOrDefault(z => z.LastName.Equals("Smith", StringComparison.OrdinalIgnoreCase));

Related

Read Data from table with no relationship

I have some global information (Company name, street, phone, …) stored in a table "CompInfo".
This table has only one entry and has no relationship to other tables.
Now I need this information in a view to generate, i.e. offers.
I tried to add this data in the controller, but I don't know how I can send this info to my view.
Here is my controller:
public async Task<IActionResult> Offer(int qry_offer)
{
ViewData["Message"] = "Offer";
var x412Context = _context.Offer
.Where(m => m.Id == qry_offer);
x412Context = x412Context
.Include(o => o.Kunde)
.Include(o => o.Mandant)
.Include(m => m.OfferPos)
.ThenInclude(m => m.Ust)
;
return View(await x412Context.ToListAsync());
}
First I add the following code
var Comp_Info = _context.CompInfo
.Where(m => m.Id == 1);
With breakpoints and debugging I see that Comp_Info has the information I require.
But I don´t know how to get this info to my view.
I tried
ViewData["street"] = Comp_Info.street;
But this doesn't work.
Does anyone have an idea how I can transmit the data to the view?
You can return a viewmodel in your Offer method that looks like this :
public class FooVM
{
public List<Offer> Offers {get; set;}
public CompInfo CompInfo{get; set;}
}
and in your controller initilize the FooVm like this:
var vm = new FooVM
{
Offers = await x412Context.ToListAsync(),
CompInfo = Comp_Info
};
and then in your Offer Method you return View(vm);
then your Offer.cshtml will look something like :
#model FooVm;
// some html code
#foreach (var offer in Model.Offers)
{
// Do something
}
<span>#Model.CompInfo.Street</span>
you can implement another class which should have members as the list (of the same type x412Context.ToListAsync()) and another member which will have the same datatype as Comp_Info.street.
then you can return the same class object to the view like return View(newClassObj);, you can access it as we do to access model class members in view.

Is there a way I can cache a LINQ parameter and reuse it later?

Is there a way I can cache a LINQ parameter and reuse it later for optimization?
Here is my situation.
public interface IEmployee
{
ProjectCollection GetProjects();
// ...
}
public class ProjectCollection
{
List<Project> EmployeeProjects { get; private set; }
// ...
}
public class Project
{
public Guid ProjectId { get; private set; }
// ...
}
Now given a list of employees List, and a given ProjectId (guid), I need to retrieve the Project object.
I've tried a combination of two LINQ statements, one to find the right employee, and one to find the right project. But is there a way to do it in one statement, or at least optimize it by caching the employee somewhere?
public Project GetProject(List<IEmployee> employees, Guid id)
{
Project rtn = null;
// Step 1: Retrieve the employee who has the right project.
var employeeWithProject = employees.Where (e => e.GetProjects().EmployeeProjects.Any(p => p.ProjectId.Equals(guid))).FirstOrDefault(); // Note: This retrieves the employee. But I need to cache the part [e.GetProjects().EmployeeProjects] to query it later.
if employeeWithProject != null)
{
// Step 2: Retrieve the project itself.
rtn = employeeWithProject.GetProjects().EmployeeProjects.Where(p => p.ProjectId.Equals(guid)).FirstOrDefault(); // Note: This retrieves the actual project from the previously set employeeWithProject
}
return rtn; // nothing found
}
I really don't like this solution, and was wondering if anyone could help me optimize it. It basically iterates through the Projectcollection twice. So if anyone can think of a way to do the whole thing with a single LINQ statement I'd appreciate it.
Thanks.
you could try something like this:
var employeeWithProject = employees
.Select(e => e.GetProjects().EmployeeProjects.FirstOrDefault(p => p.ProjectId.Equals(guid)))
.FirstOrDefault(x=> x != null);
here you're selecting the wanted projects from the employees, and then getting the first one that isn't null
SelectMany could work here too.
static public Project GetProject(List<Employee> employees, Guid id)
=> employees
.SelectMany( e => e.GetProjects()?.EmployeeProjects)
.FirstOrDefault( p => p.ProjectId == id);

How to convert early bound query to IQueryable<TEntity>

Assuming that AccountSet returns an IQueryable<Account>, how can I convert this early binding:
XrmServiceContext _xrmServiceContext;
var result = _xrmServiceContext.AccountSet.FirstOrDefault(x => x.Id == locationGuid);
Into something that is more reusable like:
_xrmServiceContext.Where(Set == "Account").FirstOrDefault(x => x.Id == locationGuid);
AccountSet is defined as:
public System.Linq.IQueryable<Xrm.Account> AccountSet
{
get
{
return this.CreateQuery<Xrm.Account>();
}
}
How do I generalize this to be reusable for any IQueryable member of XrmServiceContext ?
As long as the Id is the primary key you can use that in conjunction with find to make a generic call
public T Get<T>(object[] Id)
where T : class
{
return _xrmServiceContext.Set<T>().Find(Id )
}
if your looking for an IQueryable that may be a bit harder but you can find out how to do that off of a previous question I asked:
Entity Framework Filter By PrimaryKey
Courtesy of this post, I've found the following solution:
private static T EntityGet<T>(Guid id)
where T : Entity
{
T item =
(
from query in Context.CreateQuery<T>()
where query.Id == id
select query
).Single();
return item;
}

LINQ Order List By SubObject with possible Null Reference

After some hours of trying and researching I'm still stuck with the following problem.
I have a list of customers, each customer has a list of orders. I now want to sort the list of customers by their highest order number.
Another aspect is that not every customer has an order, so for sorting i want to replace the missing order number with an “-“ sign.
My first approach won’t work because it can’t compare the object, but it explains the problem quite well.
customers.OrderBy(m => m.Orders == null ? "-" : m.Orders.LastOrDefault().OrderNumber.ToString());
After reading the link below I ended up with this code, but that is not working either.
how do I sort a collection with child property?
customers.OrderBy(c => c.Orders.OrderBy(o => o == null ? "-" : o.OrderNumber.ToString()).Select(o => o == null ? "-" : o.OrderNumber.ToString()).ToList();
Can someone tell me how I can do this best?
Best Regards
Edit:
Here are the entities (not full entity) I'm trying to sort.
I'm working with entity framework and I've already read out the customers as an IQueryable and now I need to sort this.
Customer
public class Customer
{
public int ID { get; set; }
public virtual List<Order> Orders { get; set; }
}
Order
public class Order {
public int ID { get; set; }
public int OrderNumber { get; set; }
public virtual Customer Customer { get; set; }
}
Now the customer does not necessarily have a list of orders associated.
Meaning a customer can have 0,1 or n orders assigned to him, that's what I meant with null.
I now want to sort the list of customers by their highest order number.
Then the following should do the trick:
var result = customers.OrderBy(c => c.Orders.Max(o => (int?)o.OrderNumber));
The int? cast is needed to let Max function return null for customers w/o orders. This normally would put them at the beginning of the sort order.
In case you want to put customers w/o orders at the end, the simplest would be to use int.MaxValue (assuming you have no OrderNumber == int.MaxValue):
var result = customers.OrderBy(c => c.Orders.Max(o => (int?)o.OrderNumber) ?? int.MaxValue);
You could first remove the customers with no Orders and then add them back with Concat()
customers
.Where(c => c.Orders.Count() != 0)
.OrderBy(c => c.Orders.OrderBy(o => o.OrderNumber).Select(o => o.OrderNumber).FirstOrDefault())
.Concat(customers.Where(c => c.Orders.Count() == 0))
.ToList();
Let me know if that works for your use-case.
So Long

asp.net is there a way to get id in list?

I have a many-to-many between 'Edital' and 'Graduando' and I created a entity called 'EditalGraduando'. Now I want to know every 'edital' that 'graduando' subscribe using the graduando id. So I did this:
public IQueryable<int> GetGraduandosIds(int editalId)
{
var graduandosId = db.EditalGraduandoe.Where(e => e.EditalId == editalId).Select(i => i.graduandoID);
return graduandosId;
}
There's a way to do a select in the entety 'Graduando' using this result? Like this: SQL WHERE ID IN (id1, id2, ..., idn)
Yes, you can do this:
public IQueryable<Graduandoe> GetGraduandos(IEnumerable<int> graduandosIds)
{
return db.Graduandoe.Where(g => graduandosIds.Contains(g.graduandoID));
}
Alternatively, you could have navigation properties, and you can write the following instead of the two functions:
public IQueryable<Graduandoe> GetGraduandos(int editalId)
{
return db.EditalGraduandoe.Where(e => e.EditalId == editalId).Select(i => i.Graduandoe);
}

Categories

Resources