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();
Related
Is the following scenario possible using LINQ. I have a class like this:
public class MyClass
{
public int Id { get; set; }
public List<MyClass2> MyData { get; set; }
public List<MyClass2> OtherData { get; set; }
}
I have a list of these and I want to filter MyData based on a criteria; for example (doesn't work):
List<MyClass> myData = GetData();
var myDataFiltered = myData
.Where(d => d.Id == 3)
.SelectMany(d => d.Where(m => m.MyData.SomeProperty == somevalue), d);
I want the result to be a List<MyClass>, but to only contain the original MyData elements where SomeProperty == somevalue.
Try this:
var myDataFiltered = myData
.Where(d => d.Id == 3)
.Select(x => new MyClass()
{
Id = x.Id,
MyData = x?.MyData.Where(y => y.SomeProperty == somevalue).ToList(),
OtherData = x.OtherData
})
.Where(x => x.MyData != null && x.MyData.Count > 0)
.ToList();
It creates new instances of MyClass that only contains the queried MyClass2 objects. You probably don't want to modify the original instances in a query.
I think you want to reassign property in LINQ select function.
Something like this:
var myDataFiltered = myData
.Where(d => d.Id == 3)
.Select(d => {
d.MyData = d.MyData.Where(m => m.SomeProperty == somevalue).ToList();
return d;
});
var myDataFiltered = myData
.Where(d => d.Id == 3)
.SelectMany(d => d.MyData).Where(t=>t.SomeProperty == somevalue);
Remember LINQ query uses lambda expressions to query for data. So what that means is that, you should use from to get the data. Now there are two key things that you should note. When you don't use FROM as shown below:
// LINQ Query Syntax
var result = from s in stringList
where s.Contains("Tutorials")
select s;
The operators FROM, WHERE and SELECT ensure that the query returns the data, into variable result. See screenshot below
However in your situation you are using LINQ method syntax which means that we use extension methods to return results as shown below:
Therefore you query will look like as follows:
List<MyClass> myData = GetData();
var myDataFiltered = myData.Where(d => d.Id == 3)
.SelectMany(d => d.Where(m => m.MyData.SomeProperty == somevalue), d)
.ToList();
I hope the two methods make sense. That you are querying list object or an IEnumerable list using LINQ query syntax or using LINQ operations method.
I create two kinds of nearly identical mapping functions in my View Models to map from POCOs, one for Queryables and one for Collections. Is it possible to create one method that does both so I can remove the duplicate code? I'd like to only keep the Expression<Func<>> then use it on local Collections and Entity Framework.
public class MyViewModel
{
public static readonly Expression<Func<MyPOCO, MyViewModel>> AsMap =
(e) => new ImportPattern
{ id = e.id,
name = e.name
}
public static readonly Func<MyPOCO, MyViewModel>> ToMap =
(e) => new ImportPattern
{ id = e.id,
name = e.name
}
public int id { get; set; }
public string name { get; set; }
}
Examples: AsMap succeeds with Entity Framework to project only the fields listed in the mapping function.
var a = Product
.Where(p => p.id > 0 && p.id < 10)
.OrderBy(q => q.id)
.Select( ImportPattern.AsMap );
ToMap successfully works on local Collections.
var products = Products.Where( p => p.id > 0 && p.id < 10).ToList();
var a = products
.OrderBy(q => q.id)
.Select( ImportPattern.ToMap );
Using AsMap on local collections fails with a Non-invocable member error. Here's a few more that fail:
.Select( ImportPattern.ToMap.Compile().Invoke() )
.Select( o => ImportPattern.ToMap.Compile().Invoke(o) )
FYI, not looking for an AutoMapper() answer please.
What about
public static readonly Expression<Func<MyPOCO, MyViewModel>> AsMap =
(e) => new ImportPattern
{ id = e.id,
name = e.name
}
public static readonly Func<MyPOCO, MyViewModel>> ToMap = AsMap.Compile();
It does not really hurt since you do that only once, but you immediately get rid of the duplicate code.
You need to pass the delegate directly:
Select( ImportPattern.ToMap.Compile() )
Note that Compile() is a slow call; you should cache its result.
Consider the the following query method:
internal List<Product> productInStockQuery(InStock inStock1)
{
var inStock =
from p in products
where p.INStock == inStock1
select p;
return inStock.ToList<Product>();
}
I would like the method to do the following:
If inStock1 == InStock.needToOrder I would like it to search for both (something like):
(instock1==InStock.needToOrder && instock1==InStock.False)
How can I do that?
I would also like to know if it is possible to create a method that allows me to search all of my Product fields in one Method.
EDITED SECTION: (second question i had)
trying to explain myself better: my class has several fields and right now for each field i have a method like shown above i wanted to know if it is possible to create a special variable that will allow me to acces each field in Product without actually typing the fields name or getters like instead of p.price / p.productName i would just type p.VARIABLE and depending on this variable i will acces the wanted field / getter
if such option exists i would appreciate it if you could tell me what to search the web for.
p.s
thanks alot for all the quick responses i am checking them all out.
Do you mean using something like an Enum?
internal enum InStock
{
NeedToOrder,
NotInStock,
InStock
}
internal class Product
{
public string Name { get; set; }
public InStock Stock { get; set; }
}
Then pass a collection to the method...
internal static List<Product> ProductInStockQuery(List<Product> products)
{
var inStock =
from p in products
where p.Stock != InStock.NeedToOrder && p.Stock != InStock.NotInStock
select p;
return inStock.ToList();
}
Create a list and pass it in...
var prods = new List<Product>
{
new Product
{
Name = "Im in stock",
Stock = InStock.InStock
},
new Product
{
Name = "Im in stock too",
Stock = InStock.InStock
},
new Product
{
Name = "Im not in stock",
Stock = InStock.NotInStock
},
new Product
{
Name = "need to order me",
Stock = InStock.NotInStock
},
};
var products = ProductInStockQuery(prods);
You can compose queries based upon an input variable like so:
var inStock =
from p in products
select p;
if(inStock1 == InStock.needToOrder)
{
inStock = (from p in inStock where p.INStock == InStock.needToOrder || instock1 == InStock.False select p);
}
else
{
inStock = (from p in inStock where p.INStock == inStock1 select p);
}
return inStock.ToList<Product>();
I believe you can use WhereIf LINQ extension method to make it more clear and reusable. Here is an example of extension
public static class MyLinqExtensions
{
public static IQueryable<T> WhereIf<T>(this IQueryable<T> source, Boolean condition, System.Linq.Expressions.Expression<Func<T, Boolean>> predicate)
{
return condition ? source.Where(predicate) : source;
}
public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Boolean condition, Func<T, Boolean> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
Below you can see an example how to use this approach
class Program
{
static void Main(string[] args)
{
var array = new List<Int32>() { 1, 2, 3, 4 };
var condition = false; // change it to see different outputs
array
.WhereIf<Int32>(condition, x => x % 2 == 0) // predicate will be applied only if condition TRUE
.WhereIf<Int32>(!condition, x => x % 3 == 0) // predicate will be applied only if condition FALSE
.ToList() // don't call ToList() multiple times in the real world scenario
.ForEach(x => Console.WriteLine(x));
}
}
q = (from p in products);
if (inStock1 == InStock.NeedToOrder)
q = q.Where(p => p.INStock == InStock.False || p.InStock == InStock.needToOrder);
else
q = q.Where(p => p.INStock == inStock1);
return q.ToList();
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
I have some tables as follows:
ImageSettingOverrides
TechniqueSettings
SettingKeyValues
From TechniqueSettings table:
BAZ-FOO setting (SettingKeyId: 7) is an override to the BAZ-Default (SettingKeyId: 4) setting.
Example of expected return from query grouped by Override value:
I want to compile a list of SettingKeyValues given technique BAZ and override FOO that excludes the overridden BAZ-Default settings and includes non-overridden BAZ-Default settings.
I currently have a LINQ query that groups setting-key values based on Default/Override values:
var techniqueSettings = _dataRepository.All<TechniqueSetting>()
.Where(s => s.Technique.Equals(TechniqueName, StringComparison.InvariantCultureIgnoreCase))
// group settings by: e.g. Default | FOO
.GroupBy(s => s.Override);
From there I determine if the user is querying for just the defaults or the defaults with overrides:
var techniqueGroups = techniqueSettings.ToArray();
if (OverridesName.Equals("Default", StringComparison.InvariantCultureIgnoreCase)) {
// get the default group and return as queryable
techniqueSettings = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.AsQueryable();
} else {
// get the overrides group - IGrouping<string, TechniqueSetting>
var overridesGroup = techniqueGroups
.Where(grp => !grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.First();
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
// we know what is in the overrides, so exlude them from being selected here
// how to exlude overridden defaults???
.First();
}
In addition, I can't help but think there must be an easier - less clumsy - LINQ query using JOIN (maybe ???).
NOTE: Using EntityFramework 6.x
__
UPDATE:
I found Aggregate seems to simplify somewhat but still required an anonymous method.
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.Aggregate(overridesGroup,
(overrides, defaults) => {
var settings = new List<TechniqueSetting>();
foreach (var setting in defaults) {
if (overrides.Any(o => o.SettingKey.Key == setting.SettingKey.Key)) {
continue;
}
settings.Add(setting);
}
return settings.GroupBy(s => s.Override).First();
},
setting => setting);
I haven't tried the Join yet per comment by #MarkoDevcic.
Can Except be used in this query?
Revised Answer
With values
int myImageId = 1;
string myOverride = "FOO";
string myTechnique = "BAZ";
results =
ImageId Override Value
1 FOO 1000
With Values
int myImageId = 1;
string myOverride = "Default";
string myTechnique = "BAZ";
results =
ImageId Override Value
1 Default 10000
void Main()
{
// Create Tables and Initialize values
// ***********************************
var techniqueSettings = new List<TechniqueSettings>();
techniqueSettings.Add(new TechniqueSettings { Id = 1, Override = "Default", SettingKeyId = 3, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 2, Override = "Default", SettingKeyId = 4, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 3, Override = "FOO", SettingKeyId = 7, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 4, Override = "FOO", SettingKeyId = 8, Technique="BAZ"});
var imageSettingOverrides = new List<ImageSettingOverrides>();
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 1, ImageId=1, Override=null } );
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 2, ImageId=1, Override="FOO" } );
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 3, ImageId=1, Override="FOO" } );
var settingKeyValues = new List<SettingKeyValues>();
settingKeyValues.Add(new SettingKeyValues {Id = 4, Setting="Wait", Value=1000 } );
settingKeyValues.Add(new SettingKeyValues {Id = 7, Setting="Wait", Value=10000 } );
int myImageId = 1;
string myOverride = "FOO";
string myTechnique = "BAZ";
var results = from iso in imageSettingOverrides
join ts in techniqueSettings on iso.SettingId equals ts.Id
join skv in settingKeyValues on ts.SettingKeyId equals skv.Id
where iso.ImageId == myImageId &&
//iso.Override.Equals(myOverride,StringComparison.InvariantCultureIgnoreCase) &&
ts.Override.Equals(myOverride,StringComparison.InvariantCultureIgnoreCase) &&
ts.Technique.Equals(myTechnique, StringComparison.InvariantCultureIgnoreCase)
select new {
ImageId = iso.ImageId,
Override = ts.Override,
Value = skv.Value
};
results.Dump();
}
// Define other methods and classes here
public class ImageSettingOverrides
{
public int SettingId {get; set;}
public int ImageId {get; set;}
public string Override {get; set;}
}
public class TechniqueSettings
{
public int Id {get; set;}
public string Override {get; set;}
public int SettingKeyId {get; set;}
public string Technique { get; set;}
}
public class SettingKeyValues
{
public int Id {get; set;}
public String Setting {get; set;}
public int Value {get; set;}
}
I assume you expect each of SettingKeyValues in the result will have unique Setting value (it doesn't make sense to have two 'Wait' records with different numbers against them).
Here is query:
var result =
(
from ts in techniqueSettings
// For only selected technique
where ts.Technique.Equals("BAZ", StringComparison.InvariantCultureIgnoreCase)
// Join with SettingsKeyValues
join skv in settingKeyValues on ts.SettingKeyId equals skv.Id
// intermediate object
let item = new { ts, skv }
// Group by SettingKeyValues.Setting to have only one 'Wait' in output
group item by item.skv.Setting into itemGroup
// Order items inside each group accordingly to Override - non-Default take precedence
let firstSetting = itemGroup.OrderBy(i => i.ts.Override.Equals("Default") ? 1 : 0).First()
// Return only SettingKeyValue
select firstSetting.skv
)
.ToList();
I'm going to make some assumptions.
That if there is an ImageSettingOverrides the override also has to match the override passed in AKA FOO (that's the where iSettingsOverrides => iSettingsOverrides.Override == OverridesName in the join clause
You only want a distinct list of SettingKeyValues
TechniqueSetting.Id is the key and ImageSettingOverride.TechniqueSettingsId is the foreign key and that's how they are related
SettingKeyValue.Id is the key and TechniqueSetting.SettingKeyId is the foreign key and that's how they are related.
You don't have navigation properties and I have to do the join.
If I understand your classes and how they are related this should give you a list of SettingKeyValues. Since everything stays IQueryable it should execute on the server.
//I'm assuming these are your variables for each IQueryable
IQueryable<TechniqueSetting> techniqueSettings;
IQueryable<ImageSettingOverride> imageSettingOverrides;
IQueryable<SettingKeyValue> settingKeyValues;
var OverridesName = "FOO";
var TechniqueName = "BAZ";
IQueryable<TechniqueSetting> tSettings;
if (OverridesName.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
{
// Get a list of TechniqueSettings that have this name and are default
tSettings = techniqueSettings.Where(t => t.Override == OverridesName && t.Technique == TechniqueName);
}
else
{
// Get a list of TechniqueSettings Id that are overridden
// The ImageSettingOverrides have the same override
var overriddenIDs = techniqueSettings.Where(t => t.Technique == TechniqueName && t.Override == "Default")
.Join(
imageSettingOverrides.Where(
iSettingsOverrides =>
iSettingsOverrides.Override == OverridesName),
tSetting => tSetting.SettingKeyId,
iSettings => iSettings.TechniqueSettingsId,
(tSetting, iSettingsOverrides) => tSetting.Id);
// Get a list of techniqueSettings that match the override and TechniqueName but are not part of the overriden IDs
tSettings =
techniqueSettings.Where(
t =>
t.Technique == TechniqueName && !overriddenIDs.Contains(t.Id) &&
(t.Override == OverridesName || t.Override == "Default"));
}
// From expected results seems you just want techniqueSettings and that's what would be in techniqueSettings right now.
// If you want a list of SettingKeyValues (which is what is stated in the questions we just need to join them in now)
var settings = tSettings.Join(settingKeyValues, tSetting => tSetting.SettingKeyId,
sKeyValues => sKeyValues.Id, (tSetting, sKeyValues) => sKeyValues)
.Distinct();
I found Aggregate seems to simplify somewhat but still required an anonymous method.
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.Aggregate(overridesGroup,
(overrides, defaults) => {
var settings = new List<TechniqueSetting>();
foreach (var setting in defaults) {
if (overrides.Any(o => o.SettingKey.Key == setting.SettingKey.Key)) {
continue;
}
settings.Add(setting);
}
return settings.GroupBy(s => s.Override).First();
},
setting => setting);
Update:
I came up with a couple of extension methods that allows for exclusion of items and comparisons and replacements:
internal static IEnumerable<TSource> Exclude<TSource>(this IEnumerable<TSource> Source, Func<TSource, bool> Selector) {
foreach (var item in Source) {
if (!Selector(item)) {
yield return item;
}
}
}
internal static IEnumerable<TResult> ReplaceWith<TSource1, TSource2, TResult>(this IEnumerable<TSource1> Source1,
Func<TSource1, TResult> Source1Result,
IEnumerable<TSource2> Source2,
Func<TSource1, IEnumerable<TSource2>, TResult> Selector) {
foreach (var item in Source1) {
var replaceWith = Selector(item, Source2);
if (replaceWith == null) {
yield return Source1Result(item);
continue;
}
yield return replaceWith;
}
}
Exclude is fairly straightforward. For ReplaceWith usage:
var settings = _repository.Settings
.ReplaceWith(s => s.SettingKeyValue,
_repository.SettingOverrides.Where(o => o.OverrideName == overrideName),
(s, overrides) => overrides.Where(o => o.Setting == s)
.Select(o => o.SettingKeyValueOverride)
.FirstOrDefault())
.ToList();