How to group Multivalued Value with LINQ? - c#

Im my model I have decleared value from wich I have to group, like this:
Note! Sender value is multivalued type
private ICollection<string> _send;
[SolrField("sender")]
public ICollection<string> Sender
{
get
{
if (_send == null)
{
return new Collection<string> { "" };
}
else
{
return _send;
}
}
set { _send = value; }
}
In my controller where I call Linq group code is:
var groups = matchingSpremenljivke.GroupBy(s => s.Sender);
foreach (IGrouping<string, SolrVariables> groups in gruph)
{
...
}
In foreach sentence I get this error:
"Unable to cast type 'Grouping
[System.Collections.Generic.ICollection'1
[Sytem.String],MySoulution.Models.SolrVariables]'to type
'System.Linq.IGrouping'2[System.String,MySolution.Models.SolrVariables]'
This error shows only for variables that are multivalued, for non-multivalued type of variables this error is not shown.
my all code look like this now:
var grupe = matchingSpremenljivke.GroupBy(s => s.Sender);
foreach (IGrouping<ICollection<string>, SpremenljivkeSolr> grupa in grupe)
{
List<SpremenljivkeSolr> tmpList = new List<SpremenljivkeSolr>();
foreach (SpremenljivkeSolr tmpSolr in grupa)
{
tmpList.Add(tmpSolr);
}
SpremenljivkeSolr spremenljivkeSolr = new SpremenljivkeSolr();
spremenljivkeSolr.Internal = tmpList[tmpList.Count - 1].Internal;
spremenljivkeSolr.Exchangetimestamp = tmpList[tmpList.Count - 1].Exchangetimestamp;
spremenljivkeSolr.Order = tmpList[0].Order;
spremenljivkeSolr.Exchangetimestamp2 = tmpList[0].Exchangetimestamp;
spremenljivkeSolr.MsgT = tmpList[0].MsgT;
spremenljivkeSolr.Sender = tmpList[tmpList.Count - 1].Sender;
seznam.Add(spremenljivkeSolr);
}
Thanks for help!

Since the type of Sender property is ICollection<string>, this query:
var groups = matchingSpremenljivke.GroupBy(s => s.Sender);
returns an IGrouping<ICollection<string>,SolrVariables> not IGrouping<string, SpremenljivkeSolr>. Change string to ICollection<string> in foreach or use var.
Also are you sure you want to group your values based on an ICollection<T> without providing an IEqualityComparer<T> implementation ?

Related

C# - String to list used in Linq Where Any statement

I would like to use this string as a filter to remove some Ids in a linq query
public class ProductKitMakerDto
{
public int Id { get; set; }
public string TitleShort { get; set; }
public string Media { get; set; }
}
[HttpPost]
public ActionResult KitItemSelect(string culture)
{
string productMakerIds = "4174,2196,2201,2460,2508,2204";
//create a list
var productMakerList = new List<ProductKitMakerDto>();
foreach (int i in productMakerIds)
{
productMakerList.Add(new ProductKitMakerDto { Id = i });
}
var itemselects = (from p in _context.Products
where p.Matrix == 2400
select new ProductKitMakerDto()
{
Id = p.Id,
TitleShort = culture == "de" ? p.TitleShortDe :
culture == "fr" ? p.TitleShortFr :
p.TitleShortEn,
Media = "/img/" + p.Photo,
}).ToList();
//From this query I get 40 results.
//Then I want to remove the ones from the list:
//itemselects = itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
//1st (above) I get an Error CS0266 asking for explicit cast. So aplly the modification
itemselects = (List<ProductKitMakerDto>)itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
return Json(itemselects, JsonRequestBehavior.AllowGet);
}
I get 500 (Internal Server Error) - xhr.send( options.hasContent && options.data || null );
I guess the list is empty.
Any idea? Thanks
this does not work
string productMakerIds = "4174,2196,2201,2460,2508,2204";
var productMakerList = new List<ProductKitMakerDto>();
foreach (int i in productMakerIds)
{
productMakerList.Add(new ProductKitMakerDto { Id = i });
}
because you need to split on comma first and parse the string to int:
foreach (string i in productMakerIds.Split(',')) // and parse i to int with int.Parse
but since it's a string literal, initialize it correctly in the first place. Don't use a List<ProductKitMakerDto> because you just need a List<int>, then you can use Contains:
var productMakerList = new List<int>
{
4174, 2196, 2201, 2460, 2508 , 2204
};
you can not cast to a list if it's not a list and Enumerable.Where does not return one:
itemselects = (List<ProductKitMakerDto>)itemselects.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id));
you need to append ToList after the Where
itemselects = itemselects
.Where(i => !productMakerList.Any(pml =>pml.Id == i.Id))
.ToList();
but as mentioned, you could also use this Where before you create that list the first time, so include the condition witha Contains which should be supported:
var itemselects = (from p in _context.Products
where p.Matrix == 2400
&& !productMakerList.Contains(p.Id)
select new ProductKitMakerDto()
{
Id = p.Id,
TitleShort = culture == "de"
? p.TitleShortDe
: culture == "fr" ? p.TitleShortFr : p.TitleShortEn,
Media = "/img/" + p.Photo,
}).ToList();
foreach (string i in productMakerIds.Split(','))
{
productMakerList.Add(new ProductKitMakerDto { Id = int.Parse(i) });
}

How to pass a list of strings through webapi and get the results without those strings?

My code already gets the table without containing a string. How can I get a list without containing a list of strings? I want to get the result of SELECT * FROM table WHERE column NOT IN ('x' ,'y');
public IEnumerable<keyart1> Get(string keyword)
{
List<keyart1> keylist;
using (dbEntities5 entities = new dbEntities5())
{
keylist = entities.keyart1.Where(e => e.keyword != keyword).ToList();
var result = keylist.Distinct(new ItemEqualityComparer());
return result;
}
}
I think i found the answer if anybody interested
public IEnumerable<keyart1> Get([FromUri] string[] keyword1)
{
List<keyart1> keylist;
List<IEnumerable<keyart1>> ll;
using (dbEntities5 entities = new dbEntities5())
{
ll = new List<IEnumerable<keyart1>>();
foreach (var item in keyword1)
{
keylist = entities.keyart1.Where(e => e.keyword != item).ToList();
var result = keylist.Distinct(new ItemEqualityComparer());
ll.Add(result);
}
var intersection = ll.Aggregate((p, n) => p.Intersect(n).ToList());
return intersection;
}
}

C# Calculate field inside LINQ Query

I need some help to calculate a property inside my Linq query.
I know I need to use "let" somewhere, but I can't figure it out!
So, first I have this method to get my list from Database:
public BindingList<Builders> GetListBuilders()
{
BindingList<Builders> builderList = new BindingList<Builders>();
var ctx = new IWMJEntities();
var query = (from l in ctx.tblBuilders
select new Builders
{
ID = l.BuilderID,
Projeto = l.NomeProjeto,
Status = l.Status,
DataPedido = l.DataPedido,
DataPendente = l.DataPendente,
DataEntregue = l.DataEntregue,
DataAnulado = l.DataAnulado
});
foreach (var list in query)
builderList.Add(list);
return builderList;
}
Then, I have a function to calculate the Days between Dates accordingly to Status:
public int GetDays()
{
int Dias = 0;
foreach (var record in GetListBuilders)
{
if (record.Status == "Recebido")
{
Dias = GetBusinessDays(record.DataPedido, DateTime.Now);
}
else if (record.Status == "Pendente")
{
Dias = GetBusinessDays(record.DataPedido, (DateTime)record.DataPendente);
}
else if (record.Status == "Entregue")
{
Dias = GetBusinessDays(record.DataPedido, (DateTime)record.DataEntregue);
}
else if (record.Status == "Anulado")
{
Dias = GetBusinessDays(record.DataPedido, (DateTime)record.DataAnulado);
}
}
return Dias;
}
I need to call the GetDays in a DataGridView to give the days for each record.
My big problem is, How do I get this? include it in Linq Query? Calling GetDays() (need to pass the ID from each record to GetDays() function)!?
Any help?
Thanks
I think it would be easier to create an extension method:
public static int GetBusinessDays(this Builders builder) // or type of ctx.tblBuilders if not the same
{
if (builder == null) return 0;
switch(builder.status)
{
case "Recebido": return GetBusinessDays(builder.DataPedido, DateTime.Now);
case "Pendente": return GetBusinessDays(builder.DataPedido, (DateTime)builder.DataPendente);
case "Entregue": return GetBusinessDays(builder.DataPedido, (DateTime)builder.DataEntregue);
case "Anulado": GetBusinessDays(builder.DataPedido, (DateTime)builder.DataAnulado);
default: return 0;
}
}
Then, call it like that:
public BindingList<Builders> GetListBuilders()
{
BindingList<Builders> builderList = new BindingList<Builders>();
var ctx = new IWMJEntities();
var query = (from l in ctx.tblBuilders
select new Builders
{
ID = l.BuilderID,
Projeto = l.NomeProjeto,
Status = l.Status,
DataPedido = l.DataPedido,
DataPendente = l.DataPendente,
DataEntregue = l.DataEntregue,
DataAnulado = l.DataAnulado,
Dias = l.GetBusinessDays()
});
foreach (var list in query)
builderList.Add(list);
return builderList;
}
To do better, to convert a object to a new one, you should create a mapper.
Why does it need to be a part of the query? You can't execute C# code on the database. If you want the calculation to be done at the DB you could create a view.
You're query is executed as soon as the IQueryable is enumerated at the foreach loop. Why not just perform the calculation on each item as they are enumerated and set the property when you are adding each item to the list?

Generic Method of Searching all Fields within an IEnumerable for any match in c#

I code below that works well with an API for the DataTables plugin; for each column that the DataTables searches in, regardless of type, the filter works as expected based on what is supplied.
DataTables also has a "Global" search feature where you can search in one field and if there is a match in ANY of the rows for said data then a match is returned.
What I am Hoping For:
A way to perform a search on an IEnumerable such that if any of the fields match the search the result is returned.
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch))
{
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties)
{
Type propType = currentProperty.PropertyType;
set = set.Where(StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType));
/* ^^^^^ */
/*
Instead of the "Where" here I am looking for something like "where or" which can be added to the IEnumerable.
*/
}
}
Original Code:
protected virtual IQueryable<T> FilterEntitiesBySearchParameters(IQueryable<T> set)
{
try
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var name = prop.Name;
var val = nvc[name];
Type propType = prop.PropertyType;
if (val != null)
{
set = set.Where(StaticUtility.PropertyEquals<T>(prop, val, propType));
}
if (nvc.GetPairs().Where(p => p.Value == name).Where(p => p.Key.StartsWith("mDataProp")).Any())
{
var key = nvc.GetPairs().Where(p => p.Value == name).Where(p => p.Key.StartsWith("mDataProp")).FirstOrDefault().Key;
key = key.Replace("mDataProp", "sSearch");
val = nvc[key];
if (!String.IsNullOrEmpty(val))
set = set.Where(StaticUtility.PropertyEquals<T>(prop, val, propType));
}
}
return set;
} catch (Exception exc)
{
return set;
}
}
If I understand your request correctly, you basically want to search through your data and match any field for equality? If that's true, then simply add your matching data to a new set, and filter it by .Distinct() after the fact, to make sure you get one record of each. Something like so...
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var results = new IEnumerable<T>();
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch))
{
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties)
{
Type propType = currentProperty.PropertyType;
results.AddRange(set.Where(StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType)));
}
}
return results.Distinct();
You could try to first create a BinaryExpression consisting of all the different options, then pass that expression to the Where() method of the query.
Assuming your StaticUtility class is used to create expressions, you might try something like the following:
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
// Container for filter expression
BinaryExpression filter = null;
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch)) {
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties) {
Type propType = currentProperty.PropertyType;
if (filter == null) {
// Start with first filter expression
filter = StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType);
} else {
// Add another filter using OR
BinaryExpression other = StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType);
filter = BinaryExpression.OrElse(filter, other);
}
}
}
// Add actual filter to query
set = set.Where(filter);

Accessing C# Dictionary and return value

I have a dictionary:
Dictionary<ICD_Map2, string> maps = new Dictionary<ICD_Map2, string>();
I add to the dictionary via button click:
private void button2_Click(object sender, EventArgs e)
{
maps.Clear();
// Load mapping file.
var reader = new StreamReader(File.OpenRead(#"Call_Details_Map.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
maps.Add(new ICD_Map2(values[0].Replace("\"",""), values[1].Replace("\"","")), values[2].Replace("\"",""));
}
}
I want to use LINQ and map my keys to the "string" in maps.
How do I do it?
var File001 = from line in File.ReadLines(ICD_process)
let l = line.Split(',')
where l[0] != "\"Statement Date\""
select new
{
CallType = maps.ToLookup(p => l[5], p => l[3]),
Calls = l[11] == "\"\"" ? "0" : (maps.ToLookup(p => l[5], p => l[3]) == "Mobile Data" || maps.ToLookup(p => l[5], p => l[3]) == "Mobile SMS") ? "0" : l[11].Replace("\"","").ToString())
};
I am getting error in Calls variable in File001 Linq method
It's not clear what you are trying to achieve, but here is my advice. Now you are working with spitted lines array like this l[0] != "\"Statement Date\"". I think only you know what data should be at index 9. It's not very readable, error-prone (typo in Statemnet Date, wrong index), and it's very hard to maintain. Instead this create an object, which will parse line for you and provide data via strongly typed properties with nice names.
public class ICDEntry
{
public static ICDEntry CreateFrom(string line)
{
string[] values = line.Split(',');
var entry = new ICDEntry();
// assign values to properties:
// if (values[0] == "\"Statement Date\"")
// entry.StatementDate = DateTime.Parse(values[1]);
// entry.IsSomething = values[11] == "\"\""
return entry;
}
public DateTime? StatementDate { get; private set; }
public string MobileSMS { get; private set; }
public bool IsSomething { get; private set; }
}
Now you can parse each line, and then work in strongly typed world making queries to your ICD entries:
var entries = File.ReadLines(ICD_process).Select(l => ICDEntry.CreateFrom(l));
var File001 = from e in entries
where e.StatementDate.HasValue
select new {
Calls = e.IsSomething ? "0" : e.MobileSMS; // use ICDEntry here
};

Categories

Resources