Related
I have some problem with my unit test which work with fake Context(means without database connection). On productive environment it work with connection to sql and everything is clear. But with fake context happened something strange - not find array "item.OfferKey" in the array of arrays "validCartItems"
//Array of arrays validCartItems values for example
byte[] offerKey1 = { 30, 163, 252, 225, 36, 208, 128, 47, 64, 244, 34, 199, 28, 57, 110, 215 };
byte[] offerKey2 = { 31, 163, 254, 225, 35, 203, 119, 47, 65, 244, 24, 199, 28, 56, 110, 215 };
byte[][] validCartItems = new byte[4][];
validCartItems[0] = offerKey1;
validCartItems[1] = offerKey1;
validCartItems[2] = offerKey1;
validCartItems[3] = offerKey2;
//Example of ItemPrice in _dataContext.ItemPrices
var itemPriceInFakeContext = new ItemPrice()
{
OfferKey = offerKey1,
//some other properties
};
var itemPrices = _dataContext
.ItemPrices
.Where(item =>
item.UserID == user.UniqueID
&& itemsPartID.Contains(item.PartID)
&& validCartItems.Contains(item.OfferKey)
&& item.CurrencyID == defaultCurrencyCode
&& item.Inventory > 0)
.ToList();
In this case no elements found. But in case we work with database everithing is all right.
To solve the problem, I wrote the following code:
var itemPricesUncheckOfferKey = _dataContext
.ItemPrices
.Where(item =>
item.UserID == user.UniqueID
&& itemsPartID.Contains(item.PartID)
//&& validCartItems.Contains(item.OfferKey)
&& item.CurrencyID == defaultCurrencyCode
&& item.Inventory > 0)
.ToList();
List<ItemPrice> itemPrices = new List<ItemPrice>();
foreach (var itemPrice in itemPricesUncheckOfferKey)
{
foreach (var validCartItem in validCartItems.Distinct())
{
if (validCartItem.SequenceEqual(itemPrice.OfferKey))
itemPrices.Add(itemPrice);
}
}
But it does not look like a good solution. Could you tell me the solution within LINQ?
UPD
UnitTest code:
[TestMethod]
public void AddCartItems_Test()
{
User user;
InitUser(out user);
List<AddCartItem> addCartItems;
addCartItems = InitAddCartItem();
ICartService cartService;
InitCartService(out cartService);
List<AddCartItemRezult> addCartItemRezults = cartService.AddCartItems(user, addCartItems);
Assert.AreEqual(4, addCartItemRezults.Count);
int countAllGood = 0;
foreach (var addCartItemRezult in addCartItemRezults)
{
if (addCartItemRezult.IsSuccess) countAllGood++;
}
Assert.AreEqual(1, countAllGood);
}
private void InitCartService(out ICartService cartService )
{
DataFakeContext dataFakeContext = new DataFakeContext();
DataContext_InitUsers(ref dataFakeContext);
DataContext_ItemPrices(ref dataFakeContext);
DataContext_CartItems(ref dataFakeContext);
IDeliveryService deliveryService = new DeliveryFakeService(dataFakeContext);
cartService = new CartService(dataFakeContext, deliveryService);
}
private void DataContext_ItemPrices(ref DataFakeContext dataFakeContext)
{
dataFakeContext.ItemPrices = new ItemPriceDbSet();
byte[] OfferKeyPriv = { 30, 163, 252, 225, 36, 208, 128, 47, 64, 244, 34, 199, 28, 57, 110, 215 };
var itemPrice1 = new DataAccess.Sql.NavisionModel.ItemPrice()
{
Inventory = 2075,
ItemID = "475931",
LineAmount = (decimal)389.9300,
LineAmountWithMargin = (decimal)522.5062,
Multiplicity = 1,
OfferKey = OfferKeyPriv,
//some other properties
};
dataFakeContext.ItemPrices.Add(itemPrice1);
}
I use Repository.Pattern.Ef6;
In your code you're creating byte[] offerKey1 = ... and saving it into itemPriceInFakeContext. And you should use the same variable to adding this into your _dataContext. I mean exactly the same - not the save value but the reference to the same object.
Like this:
//Array of arrays validCartItems values for example
byte[] offerKey1 = { 30, 163, 252, 225, 36, 208, 128, 47, 64, 244, 34, 199, 28, 57, 110, 215 };
byte[] offerKey2 = { 31, 163, 254, 225, 35, 203, 119, 47, 65, 244, 24, 199, 28, 56, 110, 215 };
byte[][] validCartItems = new byte[4][];
validCartItems[0] = offerKey1;
validCartItems[1] = offerKey1;
validCartItems[2] = offerKey1;
validCartItems[3] = offerKey2;
//Example of ItemPrice in _dataContext.ItemPrices
var itemPriceInFakeContext = new ItemPrice()
{
OfferKey = offerKey1, // use the same object
//some other properties
};
// add fake item price to data context
_dataContext.ItemPrices.Add(itemPriceInFakeContext );
var itemPrices = _dataContext
.ItemPrices
.Where(item =>
item.UserID == user.UniqueID
&& itemsPartID.Contains(item.PartID)
&& validCartItems.Contains(item.OfferKey)
&& item.CurrencyID == defaultCurrencyCode
&& item.Inventory > 0)
.ToList();
That should help.
p.s. you faced this problem because of differences between how runtime compares byte[] and how it does EF and SQL.
In runtime it compares by references. But your LINQ query (on executing) converts this byte[] array (I believe) into the string. And on SQL side it compares the string which will be compared by value.
var itemPrices = _dataContext
.ItemPrices
.Where(item =>
item.UserID == user.UniqueID
&& itemsPartID.Contains(item.PartID)
&& validCartItems.Contains(item.OfferKey)
&& item.CurrencyID == defaultCurrencyCode
&& item.Inventory > 0)
.ToList();
Your LINQ query seems to have no error in it. Please check the dataContext if it is initialized and whether the connection to the database is ok.
The datatable has 5 columns
Name Class Course Month Score
Alex C1 Math 12 90
Bob C1 Chem 11 91
Alex C2 Math 11 91
Alex C1 Math 11 89
Bob C1 Chem 12 97
Alex C1 Math 10 94
Alex C2 Chem 12 92
Bob C2 Math 12 94
And I wanna group (name, class) and fetch the max math score in just Nov and Dec, and the max chem score. Heres my query code
DataRow[] dr1 = dt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
And I could output these 2 query results as this:
Name Class Math_Max_Month Math_Max
Alex C1 12 90
Alex C2 11 91
Bob C2 12 94
Name Class Chem_Max_Month Chem_Max
Bob C1 12 97
Alex C2 12 92
But how can I merge these 2 results into 1 output such as this:
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null
I've tried to use result_one.Concat(result_two) and result_one.Union(result_two), but both are incorrect.
Alright, seems a bit complicated in your example. So i'll give you an answer on a int[] instead of DataRow[]
int[] first = new int[] { 3, 5, 6, 9, 12, 14, 18, 20, 25, 28 };
int[] second = new int[] { 30, 32, 34, 36, 38, 40, 42, 44, 46, 48 };
int[] result = first
.Concat(second)
.OrderBy(x => x)
.ToArray();
Output will be
// 3, 5, 6, 9, 12, 14, 18, 20, 25, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48
Console.Write(String.Join(", ", result));
theoretically this should work in your case, sense we're only dealing with arrays.
This works perfectly well for your code.,
DataRow[] dr1 = dtt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dtt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Chem_Max = g.Max(r => r.Field<int>("Score")),
Chem_Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
Left Join...
var lstLeftJoin = (from a in result_one
join b in result_two
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = a.Max_Month, Math_Max = a.Max, Chem_Max_Month = (subpet == null ? 0 : subpet.Chem_Max_Month), Chem_Max = (subpet == null ? 0 : subpet.Chem_Max) }).ToList();
Right Join...
var lstRightJoin = (from a in result_two
join b in result_one
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = (subpet == null ? 0 : subpet.Max_Month), Math_Max = (subpet == null ? 0 : subpet.Max), a.Chem_Max_Month, a.Chem_Max }).ToList();
Finaly the Union...
var lstUnion = lstLeftJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max }).Union(lstRightJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max })).OrderBy(o => o.Name).ThenBy(c => c.Class).ToList();
RESULT
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null
I'd like to know the best way to approach this. I have an integer array (say of 3, 4, 8, 10, 15, 24, 29, 30) and I want to sort it into 3 groups: 4 times table, 5 times table, and neither).
As the groups would suggest, it would sort the array into the 4 and 5 times table with another for items that aren't present in either.
What's the best way to approach this in C#? I'm currently using this:
int[] iArray = new int[]{3, 4, 8, 10, 15, 24, 29, 30};
var iE = iArray.GroupBy ((e) => {
if (e % 4 == 0) {
return "four";
} else if (e % 5 == 0) {
return "five";
} else {
return "other";
}
}).OrderBy (e => e.Count ());
Produces:
4
4
8
24
5
10
15
30
Other
3
29
int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
If you want to get all multiples of 4 and all multiples of 5 (and have some overlap between the two) you can do this:
List<int> multiplesOf4 = (from i in arr where i % 4 == 0 select i).ToList();
List<int> multiplesOf5 = (from i in arr where i % 5 == 0 select i).ToList();
List<int> others = (from i in arr where i % 5 != 0 && i % 4 != 0 select i).ToList();
If you want no overlap, however, you need to pick which one will be dominant. I chose 4 here:
List<int> multiplesOf4 = new List<int>(),
multiplesOf5 = new List<int>(),
others = new List<int>();
foreach (int i in arr)
{
if (i % 4 == 0)
multiplesOf4.Add(i);
else if (i % 5 == 0)
multiplesOf5.Add(i);
else
others.Add(i);
}
Try this:
var numberGroupsTimes5 =
from n in numbers
group n by n % 5 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
var numberGroupsTimes4 =
from n in numbers
group n by n % 4 into g
where g.Key == 0
select new { Remainder = g.Key, Numbers = g };
foreach (var g in numberGroupsTimes5)
{
string st = string.Format("Numbers with a remainder of {0} when divided by 5:" , g.Remainder);
MessageBox.Show("" + st);
foreach (var n in g.Numbers)
{
MessageBox.Show(""+n);
}
}
foreach (var g in numberGroupsTimes4)
{
string st = string.Format("Numbers with a remainder of {0} when divided by 4:", g.Remainder);
MessageBox.Show("" + st);
foreach (var n in g.Numbers)
{
MessageBox.Show("" + n);
}
}
You approach is correct. But you can do some small improvements to make it more readable and standard:
var iArray = new[] { 3, 4, 8, 10, 15, 24, 29, 30 };//Don't need to give type(int) explicitly
var iE = iArray.GroupBy(e => e % 4 == 0 ? "four" : e % 5 == 0 ? "five" : "other").OrderBy(e => e.Count());
It'll give the same results.
Suppose we have a simplified Orders entity, with this properties: Id (int, PK), orderDate (datetime, not null) and productCategory (string, not null).
What would be the LINQ to Entities query that returns the count of orders for each category, grouped by month, for the past 12 months, ordered by year, month?
The output should look like this, in order to pass it as series for a Highcharts lines chart, using JQuery.getJSON():
[{
name: 'Dairy',
data: [23, 27, 32, 44, 21, 30, 11, 0, 9, 24, 3, 19]
},
{
name: 'Frozen',
data: [11, 4, 0, 6, 8, 10, 17, 24, 18, 8, 23, 10]
}]
That is, 12 values for each category, including zeros.
This seems to work:
var orders = from o in orderList.ToList().Where(x => x.orderDate >= DateTime.Now.AddYears(-1)).GroupBy(x => x.productCategory)
select new {
name = o.Key,
data = ((Func<int[]>)(() => {
var months = new int[12];
for (int i = 0; i < 12; i++) {
months[i] = o.Where(x => x.productCategory == o.Key && x.orderDate.Month == i).Count();
}
return months;
}))()
};
First we filter the orders for anything with a date in the last year. Then we group the results by the productCategory field. From this grouping, we create an anonymous projection class. Here we set the name field equal to the grouping key (the category). In order to fill the data field, we use an anonymous function to enumerate each month and count up each record that matches the month number and product category which we have grouped by.
This allows us to put 0 counts for months that we don't have data for.
var months = Enumerable.Range(1, 12);
var max = DateTime.Now.AddYears(-1);
var result = data.Where(d => d.OrderDate >= max)
.GroupBy(d => d.ProductCategory)
.Select(g =>
new
{
Name = g.Key,
Data =(
from m in months
join d in
g.OrderBy(gg => gg.OrderDate.Year)
.ThenBy(gg => gg.OrderDate.Month)
.GroupBy(gg => gg.OrderDate.Month)
on m equals d.Key into gj
from j in gj.DefaultIfEmpty()
select j.Key != null ? j.Count() : 0)
}).ToArray();
I have following Table with records for ActivityLog:
ID, UserId, Category, Created
1 11 DeAssigned 05/10/2012
2 11 LogIn 05/11/2012
3 20 Assigned 06/15/2012
4 11 Assigned 06/10/2012
5 20 DeAssigned 06/13/2012
6 20 Assigned 07/12/2012
7 11 DeAssigned 07/16/2012
8 20 Assigned 08/15/2012
...
now i want to query the Table to create same struct the same Result such as this:
var data = new[] {
new { Month = "05", Assigned = 14, DeAssigned = 5, LogIn=1 },
new { Month = "06", Assigned = 5, DeAssigned = 2, LogIn=0 },
new { Month = "07", Assigned = 50, DeAssigned = 8, LogIn=0 },
new { Month = "08", Assigned = 15, DeAssigned = 1, LogIn=0 }
};
what i have achieved:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by new { l.Created.Value.Month, l.Category }
into groups
orderby groups.Key.Month
select new
{
Month = groups.Key.Month,
Category = groups.Key.Category,
Count = groups.Count()
});
there is not optimal result but count all Activity's grouped by Month:
[0] {Month = 6, Category = Assigned, Count = 2}
[0] {Month = 6, Category = Designed, Count = 1}
[0] {Month = 6, Category = LogIn, Count = 1}
[0] {Month = 7, Category = Assigned, Count = 3}
How can i query my Table to Format my Result in "Horizontal Counted" format?
Or simplier:
var result = (from l in repoA.GetAll()
where l.Created >= System.DateTime.Now.AddYears(-1)
group l by l.Created.Month into groups
orderby groups.Key ascending
select new
{
Month = groups.Key,
Assigned = groups.Where(g => g.Category == "Assigned").Count(),
Deassigned = groups.Where(g => g.Category == "DeAssigned").Count(),
LogIn = groups.Where(g => g.Category == "LogIn").Count()
});