Dynamic Linq Any syntax - linq

I am trying to figure out how to use Any operator in Linq.Dynamic, here is my test code:
var bar = new[] {
new {Bar = "A", Id = 1},
new {Bar = "B", Id = 1},
new {Bar = "C", Id = 2},
new {Bar = "A", Id = 2},
new {Bar = "B", Id = 3},
new {Bar = "C", Id = 3}
};
var foo = new[] {
new {Foo = 1, Id = 1},
new {Foo = 1, Id = 1},
new {Foo = 2, Id = 2},
new {Foo = 2, Id = 2},
new {Foo = 2, Id = 3},
new {Foo = 3, Id = 3}
};
var res = foo.GroupJoin(bar, x => x.Id, y => y.Id, (x, l) => new { F = x.Foo, Bs = l.Select(b => b.Bar).ToArray() }).ToArray();
var normalLinq = res.Where(x => x.Bs.Any(y => y == "A")).ToArray();
var dynamicLinq = res.AsQueryable().Where("Bs.Any(x => x==\"A\")").ToArray(); //won't work
The syntax "Bs.Any(x => x==\"A\")" is wrong as dynamic linq doesn't seem to know the lambda expression I put in, and exception is thrown. How do I make it work?
My goal is to parse dynamic search input with this code. If you Linq experts have other better ways to do dynamic search, please suggest?

If you are using this library: http://www.nuget.org/packages/System.Linq.Dynamic/ as far as I know it does not allow lambda expressions. However, there is somebody who extended this library. I have never tried it but it looks promising. http://dynamiclinq.codeplex.com/ Good luck :)

Related

Group by with maximum

I want to group by category, show it's name, then show the highest id that is related to it. Here's some data and the result that I want further down. Any ideas? I've been playing around with GroupJoin but can't seem to get it to work.
My Data
var stuff = new[] {
new {id = 5, catId = 2},
new {id = 56, catId = 2},
new {id = 56, catId = 2},
new {id = 8, catId = 1},
new {id = 9, catId = 3}};
var categories = new[] {
new {catId = 1, Name = "Water"},
new {catId = 4, Name = "Wind"},
new {catId = 2, Name = "Fire"}};
What I want my results to look like
Water - 8
Wind - null
Fire - 56
categories
.GroupJoin
(
stuff,
c=>c.catId,
s=>s.catId,
(c,s)=>new
{
c.Name,
Max = s.Any() ? (int?)s.Max (m => m.id) : null
}
);
It seems that you want a "LEFT OUTER JOIN" with LINQ:
var query = from cat in categories
join s in stuff
on cat.catId equals s.catId into gj
from stuffJoin in gj.DefaultIfEmpty()
group stuffJoin by new { cat.catId, cat.Name } into catGroup
select new {
Category = catGroup.Key.Name,
MaxID = catGroup.Max(s => s == null ? 0 : s.id) // stuff is null for Wind
};
foreach (var x in query)
Console.WriteLine("Category: {0} Max-ID: {1}", x.Category, x.MaxID);
Outputs:
Category: Water Max-ID: 8
Category: Wind Max-ID: 0
Category: Fire Max-ID: 56

using mock data instead of EF for webapi unit test

I am trying to unit test a webapi project with zero joy.
The api is using EF6.1 to access data. My test project is using moq and xunit.
I have setup a basic test like
[Fact]
public void CheckGroupedDataCountEquals2()
{
var mockData = new List<Skill>()
{
new Skill() {Id = 1, Name = ".net"},
new Skill() {Id = 2, Name = "asp.net", ParentId = 1},
new Skill() {Id = 3, Name = "c#", ParentId = 1},
new Skill() {Id = 4, Name = "php"},
new Skill() {Id = 5, Name = "zend", ParentId = 4},
new Skill() {Id = 6, Name = "zend3", ParentId = 5}
};
// Arrange
_skillMock.Setup(x => x.GroupedSkills())
.Returns((new List<GroupedSkill>()));
var controller = new SkillsController(_skillMock.Object);
// Act
IHttpActionResult actionResult = controller.Get();
var contentResult = actionResult as OkNegotiatedContentResult<List<GroupedSkill>>;
// Assert
Assert.NotNull(contentResult);
var result = contentResult.Content as List<GroupedSkill>;
Assert.Equal(2,result.Count);
}
My apicontroller action looks like
[HttpGet]
[Route("")]
// GET api/<controller>
public IHttpActionResult Get()
{
var data = _skillRepository.GroupedSkills();
if (data!=null)
{
return Ok(data);
}
return NotFound();
}
And finally....my repo
public List<GroupedSkill> GroupedSkills()
{
using (var context = new DataContext())
{
var data = context.Skills.ToList();
var newList = data.Where(x => x.ParentId != null).ToList().GroupBy(x => x.ParentId,
(key, elements) =>
new
{
data.First(i => i.Id == key).Name,
GroupId = key,
Skills = elements.ToList()
})
.ToList();
var filtered = new List<GroupedSkill>();
foreach (var item in newList)
{
filtered.Add(new GroupedSkill()
{
GroupId = item.GroupId.Value,
Name = item.Name,
Skills = item.Skills
});
}
return filtered;
}
}
My test is always failing with the group count is always 0. I think its because I am not adding my mock list of mockData and my repository knows nothing about it.....am I right? how do I pass my mock list of data to the repo from my test
You are correct, you're passing it an empty list.
You first create the mockedData list, and populate it with real Skills, but it doesn't look like you ever use it. This should also be considered part of the Arrange section, for what that's worth.
var mockData = new List<Skill>()
{
new Skill() {Id = 1, Name = ".net"},
new Skill() {Id = 2, Name = "asp.net", ParentId = 1},
new Skill() {Id = 3, Name = "c#", ParentId = 1},
new Skill() {Id = 4, Name = "php"},
new Skill() {Id = 5, Name = "zend", ParentId = 4},
new Skill() {Id = 6, Name = "zend3", ParentId = 5}
};
Then when you begin your Arrange section by creating the _skillMock, but you creating a new, empty list.
// Arrange
_skillMock.Setup(x => x.GroupedSkills())
.Returns((new List<GroupedSkill>()));
And I can't tell where _skillMock is initialized; you shouldn't need it (and really don't want it) to be a class instance variable, because it could change across other tests. Instead, you should be setting this list up similar to how you built your mockData list.
You are properly passing the mock object in this line:
var controller = new SkillsController(_skillMock.Object);
However because you haven't added anything to that list, the list will always be empty.
Ideally, what I think you want is a real list filled with mocks of your GroupedSkill objects, something like this:
var mockData = new List<GroupedSkill>()
{
new Mock<GroupedSkill>(1, ".net").Object,
new Mock<GroupedSkill>(2, "asp.net", 1).Object,
// ... keep going
}
And then pass that list to your controller instead.

Anonymous types object creation and passed into MVC# razor view?

Q1: What is better shorthand version of the following?
Q2: How can I pass anonymous types to my view in mvc3?
public ViewResult Index3()
{
List<T1> ls = new List<T1>();
ls.Add(new T1 { id = 1, title = "t1", val1 = 1, val2 = 2});
ls.Add(new T1 {id=2, title="t2", val1=3, val2=4});
ls.Add(new T1 { id = 3, title = "t3", val1 = 5, val2 = 6});
return View(ls);
}
(Q1) Something similar to?:
List<T1> ls = new List<T1>(
List<T1>(new { id = 1, title = "t1", val1 = 1, val2 = 2}
new { id = 2, title = "t2", val1 = 3, val2 = 4})
);
(Q2) Something similar to?:
public ViewResult Index3()
{
return View(List(new { id = 1, title = "t1", val1 = 1, val2 = 2 }
new { id = 2, title = "t2", val2 = 3, val2 = 4 }
);
}
Then reference the above in the razor view:
#model IEnumerable<Some Anonymous or Dynamic Model>
#item.id
#item.title
#item.val1
...
Q1 is a matter of preference. There is no performance difference as the compiler internally creates similar code.
Q2 is impossible, you must create a non-anonymous type to be able to access it.
Could use ViewBag to pass your list to the view.
Collection initializers are written like this:
List<T1> ls = new List<T1> {
new T1 { id = 1, title = "t1", val1 = 1, val2 = 2 },
new T1 { id = 2, title = "t2", val1 = 3, val2 = 4 },
new T1 { id = 3, title = "t3", val1 = 5, val2 = 6 }
};
Create an implicitly-typed array:
return View(new [] { new { id = 1, ... }, ... });
Neither option will work as anonymous types are internal and razor views are compiled into a separate assembly.
See:
Dynamic view of anonymous type missing member issue - MVC3

Simple LINQ query

I have a List of X items. I want to have LINQ query that will convert it into batches (a List of Lists), where each batch has 4 items, except for the last one which can have 1-4 (whatever the remainder is). Also, the number 4 should be configurable so it could 5, 17, etc.
Can anyone tell me how to write that?
List<Item> myItems = ...;
List<List<Item>> myBatches = myItems.????
Thank you in advance!
If you're happy with the results being typed as IEnumerable<IEnumerable<T>> then you can do this:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val);
If you want an actual List<List<T>> then you'll need to add a couple of extra ToList calls:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val,
(k, g) => g.ToList())
.ToList();
Here is a good article about using Take and Skip to do paging, which is identical functionality to what you are requesting. It doesn't get you all of the way to a single line of LINQ, but hopefully helps.
This made me think of how we did this before LINQ.
var vessels = new List<Vessel>()
{ new Vessel() { id = 8, name = "Millennium Falcon" },
new Vessel() { id = 4, name = "Ebon Hawk" },
new Vessel() { id = 34, name = "Virago"},
new Vessel() { id = 12, name = "Naboo royal starship"},
new Vessel() { id = 17, name = "Radiant VII"},
new Vessel() { id = 7, name = "Lambda-class shuttle"},
new Vessel() { id = 23, name = "Rogue Shadow"}};
var chunksize=2;
// With LINQ
var vesselGroups = vessels.Select((v, i) => new { Vessel = v, Index = i })
.GroupBy(c => c.Index / chunksize, c => c.Vessel, (t,e)=>e.ToList())
.ToList();
// Before LINQ (most probably not optimal)
var groupedVessels = new List<List<Vessel>>();
var g = new List<Vessel>();
var chunk = chunksize;
foreach(var vessel in vessels)
{
g.Add(vessel);
chunk--;
if (chunk == 0)
{
groupedVessels.Add(g);
g = new List<Vessel>();
chunk = chunksize;
}
}
groupedVessels.Add(g);

How to use Linq and the IN clause

Here is my code
if (catid != 0)
posts = posts.Where(x => x.catid IN '1,8,2,109,23');
The in in this code shows as a syntax error. Is there a way to fix this
You must use another list to compare too.
List<int> cadIdFoundList = new List<int>();
cadIdFoundList.Add(1);
cadIdFoundList.Add(8);
// etc. . .
posts.Where(x => cadIdFoundList.Contains(x.catId));
int[] ids = new int[] { 1, 8, 2, 109, 23 };
var query = posts.Where(x => ids.Contains(x.catid));
Rob Conery has discussed this topic before.
Or even more simple:
var query = posts.Where(x => new[] { 1, 8, 2, 109, 23 }.Contains(x.catid));
Maybe something more like:
HashSet<int> categories = new HashSet<int>() { 1, 2, 8, 23, 109};
posts = posts.Where(post => categories.Contains(post.catid));

Resources