LINQ query for WHERE IN clasue - linq

I've 4 tables Transactions, Attachment, SubReportType & SubRepRole. I'm having user roles on my Cache. I want to have those attachments which are related with User roles[a user may have multiple role]
List<int> userrole = new List<int>();
UserContext user_details = (UserContext)HttpContext.Current.Cache["UserContext"];
IList<UserRole> userrole_id = user_details.UserRoleModuleWise;
var query = (from trans in objContext.Transactions
join attch in objContext.Attachment on trans.TransId equals attch.TransId
join subrept in objContext.SubReportType on trans.SubRepId equals subrept.SubRepId
join subreprl in objContext.SubRepRole on trans.SubRepId equals subreprl.SubRepId
join selectedrole in userrole_id on subreprl.RoleId equals selectedrole.RoleId
/*where obj.Contains(subreprl.RoleId) */orderby trans.TransDate
select new AttachmentModel
{
Createdate = attch.CreatedDateTime,
FileType = attch.FileType,
FileName = attch.FileName,
Attachid = attch.AttachedId,
FileTag = attch.FileTag,
Transid = trans.TransId,
SubReportName = subrept.SubRepName,
RandomPinNo = attch.FileRandomPin
}).ToList();
Now getting this error:
Unable to create a constant value of type
'User.Common.DataContract.UserRole'. Only primitive types or
enumeration types are supported in this context.
Please help on this. Tried "Contains" too but type casting error is coming. Just wanted to have those records where roles are in user_details.UserRoleModuleWise. user_details.UserRoleModuleWise is an array with RoleId and RoleName

You need to use Contains with an array of the correct values:
where user_details.UserRoleModuleWise.Select(urmw => urmw.RoleId).ToArray().Contains(aRoleId => aRoleId == subreprl.RoleId)

Created a common class --
object[] common_data = new object[4];
UserContext user_details = (UserContext)HttpContext.Current.Cache["UserContext"];
List<UserRole> userrole_id = user_details.UserRoleModuleWise.ToList();
int[] roleid = { -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -19, -20 };
string[] rolename = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" };
for (int i = 0; i < userrole_id.Count; i++)
{
roleid[i] = Convert.ToInt32(userrole_id[i].RoleId);
rolename[i] = Convert.ToString(userrole_id[i].RoleName);
r_id.Add(roleid[i], rolename[i]);
}
common_data[0] = roleid;
common_data[1] = rolename;
//common_data[2] = username;
common_data[3] = userrole_id.Count;
then in my repository i used -
User_common_data ucd = new User_common_data();
object[] ucd_data = ucd.user_common_details();
int[] roles = (int[]) ucd_data[0];
var query = (from obj in objContext.ReportType
join obj1 in objContext.SubReportType on obj.RepId equals obj1.RepId
join obj2 in objContext.SubRepRole on obj1.SubRepId equals obj2.SubRepId
where roles.Contains(obj2.RoleId)
select new ReportTypeModel
{
RepId = obj.RepId,
RepName = obj.RepName,
RepCode = obj.RepCode
}).ToList();
now its working fine. #NetMage's answer also worked... thanks

Related

GroupBy using LINQ but base distinct on another column, and still be able to count other records

How should I run a GroupBy based on Id using LINQ when there is an object similar to following:
public class foo
{
public int id { get; set; }
public string name { get; set; }
public string lang { get; set; }
public int displayOrder { get; set; }
public int count { get; set; }
}
The list could be:
id = 1, name="test1", lang = "en", displayOrder = 1, count = 1
id = 1, name="test2", lang = "fr", displayOrder = 2, count = 2
id = 1, name="test3", lang = "de", displayOrder = 3, count = 1
id = 2, name="test4", lang = "en", displayOrder = 2, count = 1
id = 2, name="test5", lang = "fr", displayOrder = 3, count = 1
id = 3, name="test6", lang = "en", displayOrder = 6, count = 1
id = 3, name="test7", lang = "fr", displayOrder = 4, count = 1
id = 4, name="test8", lang = "en", displayOrder = 5, count = 1
id = 5, name="test9", lang = "de", displayOrder = 6, count = 1
I want to run LINQ so that it Groups By Id values, but the distinct id values should be filtered based on lang e.g. "fr", if nothing is available in "fr" it should output only default language record for "en"
but should also Count the total number of records based on Id, it should retrieve following results for above:
id = 1, name="test2", lang = "fr", displayOrder = 2, count = 4
id = 2, name="test5", lang = "fr", displayOrder = 3, count = 2
id = 3, name="test7", lang = "fr", displayOrder = 4, count = 2
id = 4, name="test8", lang = "en", displayOrder = 5, count = 1
id = 5, name="test9", lang = "de", displayOrder = 6, count = 1
Please, is there a way to do something like this using LINQ ?
All of you LINQ experts, I'm ideally looking for query using lambda, this would be a great help. Thanks in advance.
You can sort the target language to the front, and select the first item in a group:
var query = from f in foos
group f by f.id into g
let lang = (from f in g
orderby
f.lang == "fr" ? 0 : 1,
f.lang == "en" ? 0 : 1,
f.lang
select f).First()
select new
{
id = g.Key,
lang.name,
lang.lang,
lang.displayOrder,
count = g.Sum(f => f.count)
};
This assumes that pairs (id, lang) are unique.
Demo.
This may run slightly faster as it does not need to go through all items in the group for ordering.
var res = data
.GroupBy(d => d.id)
.Select(g => new {
id = g.Key,
count = g.Sum(d => d.count),
Lang = g.FirstOrDefault(d => d.lang == "fr") ?? g.First()
})
.Select(g => new {
g.id,
g.Lang.lang,
g.Lang.name,
g.Lang.displayOrder,
g.count
})
.ToList();

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

PIVOT with LINQ from Datatable [duplicate]

I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}

Transpose Data in C# Business Objects

I have the below scenario
List<Class> Classes = new List<Class>();
Class c1 = new Class() { ClassID = 1, Name = "Class1", Abbreviation = "CLS1" }; Classes.Add(c1);
Class c2 = new Class() { ClassID = 2, Name = "Class2", Abbreviation = "CLS2" }; Classes.Add(c2);
Class c3 = new Class() { ClassID = 3, Name = "Class3", Abbreviation = "CLS3" }; Classes.Add(c3);
List<ClassCode> ClassCodes = new List<ClassCode>();
ClassCode cc1 = new ClassCode() { ClassID = 1, ClassCodeID = 1, Code = "CC1", Description = "CCD1", Class = c1 }; ClassCodes.Add(cc1);
ClassCode cc2 = new ClassCode() { ClassID = 1, ClassCodeID = 2, Code = "CC2", Description = "CCD2", Class = c1 }; ClassCodes.Add(cc2);
ClassCode cc3 = new ClassCode() { ClassID = 2, ClassCodeID = 3, Code = "CC3", Description = "CCD3", Class = c2 }; ClassCodes.Add(cc3);
ClassCode cc4 = new ClassCode() { ClassID = 2, ClassCodeID = 4, Code = "CC4", Description = "CCD4", Class = c2 }; ClassCodes.Add(cc4);
ClassCode cc5 = new ClassCode() { ClassID = 3, ClassCodeID = 5, Code = "CC5", Description = "CCD5", Class = c3 }; ClassCodes.Add(cc5);
ClassCode cc6 = new ClassCode() { ClassID = 3, ClassCodeID = 6, Code = "CC6", Description = "CCD6", Class = c3 }; ClassCodes.Add(cc6);
I am trying to use Linq to transpose the above data in the below format
Class1 | CLS1 | Class2 | CLS2 | Class3 | CLS3 - Columns
---------------------------------------------------------------------------
CCD1CCD2 | CC1CC2 | CCD3CCD4 | CC3CC4 | CCD5CCD6 | CC4CC5 - Row
Columns headers are values of Name (Class) and Abbreviation (Class) on the basis of group by on ClassID (ClassCode)
Value is concatination of Code (ClassCode) and Description (ClassCode) - Map is Code goes to Abbreviation Column and Description goes to Name Column
DataTable is a fit when you want to create properties at runtime - any other option will be appreciated.
Please help!!
You are not explaining why you are doing this.
There might be other ways of achieving what you want.
Anyway, the following code should work.
It produces a DataTable, which I think is a good fit for this:
var dt = new System.Data.DataTable("Transpose");
foreach (var c in Classes)
{
var dc1 = new System.Data.DataColumn(c.Name, typeof(string));
var dc2 = new System.Data.DataColumn(c.Abbreviation, typeof(string));
dc1.ExtendedProperties.Add("ID", c.ClassID);
dc2.ExtendedProperties.Add("ID", c.ClassID);
dt.Columns.AddRange(new System.Data.DataColumn[] { dc1, dc2 } );
}
var dr = dt.NewRow();
for (int i = 0; i < dt.Columns.Count; i++)
{
var col = dt.Columns[i];
dr[i++] = ClassCodes.Where(cc => cc.ClassID == (int)col.ExtendedProperties["ID"])
.Select(cc => cc.Description)
.Aggregate((first, next) => first + next);
col = dt.Columns[i];
dr[i] = ClassCodes.Where(cc => cc.ClassID == (int)col.ExtendedProperties["ID"])
.Select(cc => cc.Code)
.Aggregate((first, next) => first + next);
}
dt.Rows.Add(dr);

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

Resources