LINQ select depending on inner collection value - c#

I have this piece of code:
public string Label { get; set; }
public bool IsSpoon(out Spoon sp)
{
sp = null;
foreach (Tool t in Tools.GetAllItems())
if ((sp = t.AllSpoons.FirstOrDefault(x => x.Label == this.Label)) != null)
break;
return sp != null;
}
How can this be optimised via LINQ?
I thought of something like this but this isn't allowed:
public string Label { get; set; }
public bool IsSpoon(out Spoon sp)
{
return Tools.GetAllItems().FirstOrDefault(x => (sp = x.AllSpoons.FirstOrDefault(y => y.Label == this.Label)) != null) != null;
}

EDIT : I did not notice the sp parameter, here is an update:
sp = Tools
.GetAllItems()
.SelectMany(x => x.AllSpoons)
.FirstOrDefault(y => y.Label == this.Label);
return sp != null;

You can flatten the list with SelectMany. Then you don't have to do anything tricky like assigning a value in the middle of a LINQ statement.
public bool IsSpoon(out Spoon sp)
{
sp = Tools.GetAllItems().SelectMany(t => t.AllSpoons)
.FirstOrDefault(x => x.Label == this.Label);
return sp != null;
}
Here is the essentially equivalent query syntax:
sp = (from tool in Tools.GetAllItems()
from spoon in tool.AllSpoons
where spoon.Label == this.Label
select spoon).FirstOrDefault();

public bool IsSpoon(out Spoon sp)
{
return Tools.GetAllItems()
.SelectMany(t => t.AllSpoons)
.Any(x => x.Label == this.Label);
}

Related

Linq where clause doesn't work in Entity Framework

This code doesn't work (returns null):
var result = context.Data
.Select(x => x)
.Where(x => x.ID == 1)
.FirstOrDefault();
But this:
var result = context.Data.Take(1);
works.
My question is why while I am using EF and the context.Data returns an IEnumerable<Data> the first code doesn't work? (And yes, the data contains element with ID equals 1)
This is a question that has really nothing to do with Entity Framework but the nature of how LINQ works with collections with it's extension methods. Let do a simple example in a console application in C#:
class Program
{
public class Faker
{
public int Id { get; set; }
public string Name { get; set; }
public Faker(int id, string name)
{
Id = id;
Name = name;
}
}
static void Main(string[] args)
{
var ls = new List<Faker>
{
new Faker(1, "A"),
new Faker(2, "B")
};
Faker singleItem = ls.FirstOrDefault(x => x.Id == 1);
IEnumerable<Faker> collectionWithSingleItem = ls.Where(x => x.Id == 1);
Console.ReadLine();
}
}
When I pause under 'Locals' I see the variables populated as such:
The simple answer is : it should work. Although your line could be optimized to :
var v = context.Data.FirstOrDefault(x => x.ID == 1);
So, basically there is no ID == 1 in your database, or you misspelled something.
If you wanted a IEnumerable<T> type then :
var v = context.Data.Where(x => x.ID == 1);
But I'd rather use list :
var v = context.Data.Where(x => x.ID == 1).ToList();

C# and Linq launching combined Linq query

Is it possible to execute a combined Linq query as a some sort of query in c#?
For example I've got 3 methods:
private queueCollection;
private void testA(parameterA)
{
if(parameterA != null)
{
queueCollection.Add(someCollection.Where(a => a.Name = parameterA))
}
else
{
queueCollection.Add(someCollection);
}
}
private void testB(parameterB)
{
if(parameterB != null)
{
queueCollection.Add(someCollection.Where(b => b.Name = parameterB))
}
else
{
queueCollection.Add(someCollection);
}
}
private void testC(parameterC)
{
if(parameterC != null)
{
queueCollection.Add(someCollection.Where(c => c.Name = parameterC))
}
else
{
queueCollection.Add(someCollection);
}
}
public void linqQueue()
{
start queueCollection;
}
So can I query different combination at once and don't need to code every combination separately?
I want to create a sorting filter on the collection so if I'll get parameter A, I will get A results from collection; if I add B, I want A and B, and if I get A null I want B only etc.
As I understand your question correctly, the answer is Yes, you can. You can use Predicates in Linq queries.
private void test(parameterA,parameterB,parameterC)
{
queueCollection.Add(someCollection.Where(a => (parameterA != null && a.Name = parameterA) && (parameterB != null && a.Name = parameterB) && (parameterC != null && a.Name = parameterC)));
}
or if you need OR operator
private void test(parameterA,parameterB,parameterC)
{
queueCollection.Add(someCollection.Where(a => (parameterA != null && a.Name = parameterA) || (parameterB != null && a.Name = parameterB) || (parameterC != null && a.Name = parameterC)));
}

NHibernate extension for querying non mapped property

I'm looking for a way to get total price count from the Costs list in my object. I can't get Projections.Sum to work in my QueryOver so I tried another way but I'm having problems with it. I want to use a unmapped property in my QueryOver. I found this example but it's giving an error.
Object:
public class Participant
{
public int Id { get; set; }
public double TotalPersonalCosts { get { return Costs.Where(x => x.Code.Equals("Persoonlijk") && x.CostApprovalStatus == CostApprovalStatus.AdministratorApproved).Sum(x => x.Price.Amount); } }
public IList<Cost> Costs { get; set; }
}
The property TotalPersonalCosts is not mapped and contains the total price count.
Extension Class:
public static class ParticipantExtensions
{
private static string BuildPropertyName(string alias, string property)
{
if (!string.IsNullOrEmpty(alias))
{
return string.Format("{0}.{1}", alias, property);
}
return property;
}
public static IProjection ProcessTotalPersonalCosts(System.Linq.Expressions.Expression expr)
{
Expression<Func<Participant, double>> w = r => r.TotalPersonalCosts;
string aliasName = ExpressionProcessor.FindMemberExpression(expr);
string totalPersonalCostName = ExpressionProcessor.FindMemberExpression(w.Body);
PropertyProjection totalPersonalCostProjection =
Projections.Property(BuildPropertyName(aliasName, totalPersonalCostName));
return totalPersonalCostProjection;
}
}
My QueryOver:
public override PagedList<AccountantViewInfo> Execute()
{
ExpressionProcessor.RegisterCustomProjection(
() => default(Participant).TotalPersonalCosts,
expr => ParticipantExtensions.ProcessTotalPersonalCosts(expr.Expression));
AccountantViewInfo infoLine = null;
Trip tr = null;
Participant pa = null;
Cost c = null;
Price p = null;
var infoLines = Session.QueryOver(() => tr)
.JoinAlias(() => tr.Participants, () => pa);
if (_status == 0)
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPrinted || pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPaid);
else if (_status == 1)
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPrinted);
else
infoLines.Where(() => pa.TotalCostApprovalStatus == TotalCostApprovalStatus.CostPaid);
infoLines.WhereRestrictionOn(() => pa.Employee.Id).IsIn(_employeeIds)
.Select(
Projections.Property("pa.Id").WithAlias(() => infoLine.Id),
Projections.Property("pa.Employee").WithAlias(() => infoLine.Employee),
Projections.Property("pa.ProjectCode").WithAlias(() => infoLine.ProjectCode),
Projections.Property("tr.Id").WithAlias(() => infoLine.TripId),
Projections.Property("tr.Destination").WithAlias(() => infoLine.Destination),
Projections.Property("tr.Period").WithAlias(() => infoLine.Period),
Projections.Property("pa.TotalPersonalCosts").WithAlias(() => infoLine.Period)
);
infoLines.TransformUsing(Transformers.AliasToBean<AccountantViewInfo>());
var count = infoLines.List<AccountantViewInfo>().Count();
var items = infoLines.List<AccountantViewInfo>().ToList().Skip((_myPage - 1) * _itemsPerPage).Take(_itemsPerPage).Distinct();
return new PagedList<AccountantViewInfo>
{
Items = items.ToList(),
Page = _myPage,
ResultsPerPage = _itemsPerPage,
TotalResults = count,
};
}
Here the .Expression property is not found from expr.
I don't know what I'm doing wrong. Any help or alternatives would be much appreciated!
Solution with Projection.Sum() thx to xanatos
.Select(
Projections.Group(() => pa.Id).WithAlias(() => infoLine.Id),
Projections.Group(() => pa.Employee).WithAlias(() => infoLine.Employee),
Projections.Group(() => pa.ProjectCode).WithAlias(() => infoLine.ProjectCode),
Projections.Group(() => tr.Id).WithAlias(() => infoLine.TripId),
Projections.Group(() => tr.Destination).WithAlias(() => infoLine.Destination),
Projections.Group(() => tr.Period).WithAlias(() => infoLine.Period),
Projections.Sum(() => c.Price.Amount).WithAlias(() => infoLine.TotalPersonalCost)
);
You can't use unmapped columns as projection columns of a NHibernate query.
And the way you are trying to do it is conceptually wrong: the ParticipantExtensions methods will be called BEFORE executing the query to the server, and their purpose is to modify the SQL query that will be executed. An IProjection (the "thing" that is returned by ProcessTotalPersonaCosts) is a something that will be put between the SELECT and the FROM in the query. The TotalCosts can't be returned by the SQL server because the SQL doesn't know about TotalCosts

How to add a non-property rule in FluentValidation?

I have this validator class:
internal class CustomerTypeValidator : AbstractValidator<CustomerType>
{
public CustomerTypeValidator()
{
RuleFor(x => x.Number).Must(BeANumber).WithState(x => CustomerTypeError.NoNumber);
}
private bool BeANumber(string number)
{
int temp;
bool ok = int.TryParse(number, out temp);
return ok && temp > 0;
}
}
And I have the service class:
public class CustomerTypeService
{
public CustomerType Save(CustomerType customerType)
{
ValidationResult results = Validate(customerType);
if (results != null && !results.IsValid)
{
throw new ValidationException<CustomerTypeError>(results.Errors);
}
//Save to DB here....
return customerType;
}
public bool IsNumberUnique(CustomerType customerType)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}
}
However I get the following exception:
Property name could not be automatically determined for expression x => x. Please specify either a custom property name by calling 'WithName'.
Is the above not the correct way to add an extra rule?
With the current version of FluentValidation, it is possible to solve the above problem by doing the following:
public bool IsNumberUnique(CustomerType customerType, int id)
{
var result = customerTypeRepository.SearchFor(x => x.Number == customerType.Number).Where(x => x.Id != customerType.Id).FirstOrDefault();
return result == null;
}
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Id).Must(IsNumberUnique).WithState(x => CustomerTypeError.NumberNotUnique);
return validator.Validate(customerType);
}

LINQ query with boolean check

I need a method which should return a list of stores from stores table, where customerid = id and Isactive = "true".
I am able to get the customerid match like so, how can I also include the Boolean check... need help with the query syntax..the "AND" operator
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id).ToList();
return (stlist);
}
Assuming that Isactive is a property of records in db.Stores, like CustomerId is
You can just add the additional check inside the Where extension method:
Assuming that Isactive is a property of type bool
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive).ToList();
return (stlist);
}
But if Isactive is a property of type string as OP seems to indicate
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive == "true").ToList();
return (stlist);
}
In C# and many other languages, && is the Boolean AND operator.
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m=>m.CustomerId == id && m.IsActive == true).ToList();
return stlist;
}
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive == "true").ToList();

Categories