I'm quite new with C#. I have a function which gives me the results in an array like this
[0] => value
[1] => value
[2] => value
[3] => value
[4] => value
But in my case, [0] [1] [2] need to be together, and I show them in a table in PHP.
This is my function:
public List<string> ReadOpenCalls(int relation)
{
IQAssemblyResolver.Initialize(#"C:\Program Files\.....");
IQSDK IQSDK = new IQSDK();
string loginTekst = IQSDK.Login("Administrator", "..", "..").GetResult();
SDKRecordset inboundSet = IQSDK.CreateRecordset("R_ACTIONSCOPE", "CODE, DESCRIPTION, DATECREATED", "FK_RELATION = " + relation, "");
var messages = new List<string>();
if (inboundSet != null && inboundSet.RecordCount > 0)
{
inboundSet.MoveFirst();
do
{
string code = inboundSet.Fields["CODE"].Value.ToString();
string desc = inboundSet.Fields["DESCRIPTION"].Value.ToString();
string date = inboundSet.Fields["DATECREATED"].Value.ToString();
messages.Add( code);
messages.Add( desc);
messages.Add( date);
inboundSet.MoveNext();
}
while (!inboundSet.EOF);
return messages;
}
messages.Add("Error niet gelukt");
return messages;// null;
}
I want the output to be something like this:
[0] => [0] => "desc"
[1] => "code"
[2] => "date"
So that I can output this in a nice way in my table in PHP.
A very quick fix:
var messages = new List<string[]>();
...
messages.Add( new string[] { code, desc, date});
But it also depends on what is easy to work with in PHP.
A better solution, probably, is to write a small class:
class MyMessage
{
public string Code { get; set; }
public string Desc { get; set; }
public string Date { get; set; }
}
And of course you'd have to change the method into one of:
public List<string[]> ReadOpenCalls(int relation) ...
public List<MyClass> ReadOpenCalls(int relation) ...
and that change also needs to be made in the ServiceContract etc.
PHP induces bad practices and a horrible understanding of data structures. Please do not try to pattern anything after whatever you saw or did in PHP. In particular, using array indices as a substitute for proper members.
Have you considered writing a class that has three fields to describe a message?
Ideally you want to change the existing function, however using System.Linq you could alter your results after you've generated them.
var messages = ReadOpenCalls(relation);
var new messages = messages.Select((a,i) => new {index = i / 3, value = a})
.GroupBy (y => y.index, y => y.value)
.Select(g => g.ToList())
.ToList();
However you would be far better altering your function
either add the list directly
public List<List<string>> ReadOpenCalls(int relation)
{
IQAssemblyResolver.Initialize(#"C:\Program Files\.....");
IQSDK IQSDK = new IQSDK();
string loginTekst = IQSDK.Login("Administrator", "..", "..").GetResult();
SDKRecordset inboundSet = IQSDK.CreateRecordset("R_ACTIONSCOPE", "CODE, DESCRIPTION, DATECREATED", "FK_RELATION = " + relation, "");
var messages = new List<List<string>>();
if (inboundSet != null && inboundSet.RecordCount > 0)
{
inboundSet.MoveFirst();
do
{
string code = inboundSet.Fields["CODE"].Value.ToString();
string desc = inboundSet.Fields["DESCRIPTION"].Value.ToString();
string date = inboundSet.Fields["DATECREATED"].Value.ToString();
messages.Add(new List<string> { code, desc, date});
inboundSet.MoveNext();
}
while (!inboundSet.EOF);
return messages;
}
messages.Add("Error niet gelukt");
return messages;// null;
}
or make a class to hold the values and return a list of the class
UPDATE - OP has stated this is in a webservice so I've added the DataContract and DataMember attributes. You will need to make sure the project references System.Runtime.Serialization
using System.Runtime.Serialization;
[DataContract]
public class Class {
public Class(string code, string desc, string date) {
this.code = code;
this.desc = desc;
this.date = date;
}
[DataMember]
public string Code { get;set; }
[DataMember]
public string Desc { get;set; }
[DataMember]
public string Date { get;set; }
}
And then your altered function
public List<Call> ReadOpenCalls(int relation)
{
IQAssemblyResolver.Initialize(#"C:\Program Files\.....");
IQSDK IQSDK = new IQSDK();
string loginTekst = IQSDK.Login("Administrator", "..", "..").GetResult();
SDKRecordset inboundSet = IQSDK.CreateRecordset("R_ACTIONSCOPE", "CODE, DESCRIPTION, DATECREATED", "FK_RELATION = " + relation, "");
var messages = new List<Call>();
if (inboundSet != null && inboundSet.RecordCount > 0)
{
inboundSet.MoveFirst();
do
{
string code = inboundSet.Fields["CODE"].Value.ToString();
string desc = inboundSet.Fields["DESCRIPTION"].Value.ToString();
string date = inboundSet.Fields["DATECREATED"].Value.ToString();
messages.Add( new Call(code,desc, date));
inboundSet.MoveNext();
}
while (!inboundSet.EOF);
return messages;
}
messages.Add("Error niet gelukt");
return messages;// null;
}
Try this,
Change
public List<string> ReadOpenCalls(int relation)
to
public List<List<string>> ReadOpenCalls(int relation)
Change
var messages = new List<string>();
to
var messages = new List<List<string>>();
And change
messages.Add( code);
messages.Add( desc);
messages.Add( date);
to
List<string> list = new List<string>();
list.Add( code);
list.Add(desc);
list.Add(date);
messages.Add(list);
Change function return value to string[][] and return the jagged array however you like.
Related
i would like to sort by postal address but i am unable to i have seen some Linq functions tried them but i can't seem to get all the required parameters needed.
for example i saw this one example
list.Sort((p, q) => p.Category.CompareTo(q.Category)); /*has and error that says cannot convert lamba expressions to type '|Comparer' because it is not a delegate*/
but i dont seem to understand how to use it.
MyCustomList.cs
class MyCustomList
{
private string name;
private string postalAddress;
public MyCustomList(string name, string postalAddress)
{
this.name = name;
this.postalAddress = postalAddress;
}
//getters and setters
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public string PostalAddress
{
get
{
return postalAddress;
}
set
{
postalAddress = value;
}
}
}
Form1.cs
ArrayList list = new ArrayList();
list.Add(new MyCustomList("A somename","A Fake Postal Address");
list.Add(new MyCustomList("B somename","B Fake Postal Address");
list.Sort(); // Sort by Postal adress
Do you really need to use ArrayList?
It's a relic from the pre-generics days of .NET, and you should really be using an implementation of IEnumerable<T> where possible e.g. List<T>.
LINQ operates on IEnumerable<T>, so won't work with your ArrayList, and the method you are looking for is OrderBy or OrderByDescending.
Example:
var list = new List<MyCustomList>();
list.Add(new MyCustomList("A somename","A Fake Postal Address"));
list.Add(new MyCustomList("B somename","B Fake Postal Address"));
list.OrderBy(cl => cl.Postcode); // Sort by Postal address
First stop using ArrayList - its as good as obsolete.
Either using Array like this
var list = MyCustomList[2];
list[0] = new MyCustomList(...
list[1] = new MyCustomList(....
or use something like the List<T> class
var list = new List<MyCustomList>();
list.Add(new MyCustomList(...
list.Add(new MyCustomList(...
If you use array then the Sort function that takes an instance of Comparison<T> is static
see the documentation here https://learn.microsoft.com/en-us/dotnet/api/system.array.sort?view=netframework-4.8#System_Array_Sort__1___0___System_Comparison___0__
you need to call it like so:
Array.Sort(list, (a,b) => a.PostalAddress.CompareTo(b.PostalAddress))
or use linq on your List or Array and use OrderBy
var orderedList = list.OrderBy(a => a.PostalAddress);
Already approved by many https://stackoverflow.com/a/57371579/6923146
For order wise sorting with a specific field in c# using linq
list = list.OrderByDescending(x => x.Name).ToList();
list = list.OrderBy(x => x.Name).ToList();
//list.OrderBy(x => x.YourClassSpecificField).ToList()
Example:
please try to run following code in fiddle :
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<MyCustomList> list = new List<MyCustomList>();
list.Add(new MyCustomList("A somename", "A Fake Postal Address"));
list.Add(new MyCustomList("B somename", "B Fake Postal Address"));
//list.Sort();
Console.WriteLine("descending order");
list = list.OrderByDescending(x => x.Name).ToList();
foreach (MyCustomList o in list)
{
Console.WriteLine(o.Name + " -- " + o.PostalAddress );
}
Console.WriteLine("ascending order");
list = list.OrderBy(x => x.Name).ToList();
foreach (MyCustomList o in list)
{
Console.WriteLine(o.Name + " -- " + o.PostalAddress );
}
}
public class MyCustomList
{
private string name;
private string postalAddress;
public string Name
{
get { return name; }
set { name = value; }
}
public string PostalAddress
{
get { return postalAddress; }
set { postalAddress = value; }
}
public MyCustomList(string name, string postalAddress)
{
this.name = name;
this.postalAddress = postalAddress;
}
}
}
I have a input strings example
str1 = ""Type":"#Microsoft.Azure","Email":"abc#tmail.com","DisplayName":"abc","Dpt":"home"";
str2 = ""Type":"#Microsoft.Azure","Email":"xyz#tmail.com","DisplayName":"xyz","Dpt":"home"";
In compileable form it looks like this:
string str = #"""Type"":""#Microsoft.Azure"",""Email"":""abc#tmail.com"",""DisplayName"":""abc"",""Dpt"":""home""";
Can i split on "," delimiter and assign left to ":" as columns and right to ":" as rows in table.
Example:
"Type" "Email" "DisplayName" "Dpt"
"#Microsoft.Azure" "abc#tmail.com" "abc" "home"
"#Microsoft.Azure" "xyz#tmail.com" "xyz" "home"
i tried something like this
string str = ' "name":"abd","":""m"":"" ';
string[] strS1 = str.split(',');
foreach(string S1 in strS1){
string[] strS2 = str.split(':');
foreach(string S2 in strS2){
console.write(s2)
}
}
You can try something like this : Demo
The Json Way.
You assume it's a Json.
Join the line with },{. Add a Starting [{ and Ending }]. Bim, You are ready to go.
Deserilise to you custom type : I ignored Type property here
JsonConvert.DeserializeObject<List<CsvItem>>(myJSON);
public class CsvItem
{
public string Email { get; set; }
public string DisplayName { get; set; }
public string Dpt { get; set; }
}
Your parsing way: String Split.
After the S1.Split(':'), you end up with a small array the first value is the property name the second the value.
Trim the ", compare and assign.
if (strS2[0].Trim('"') == "Email") temp.Email = strS2[1].Trim('"');
if (strS2[0].Trim('"') == "DisplayName") temp.DisplayName = strS2[1].Trim('"');
if (strS2[0].Trim('"') == "Dpt") temp.Dpt = strS2[1].Trim('"');
Same thing in LinQ:
At this point it's not faster, easier to maintain, not even easier to read. It's just compact
lines
.Select(x => x.Split(','))
.Select(x =>
new CsvItem
{
Email = x[1].Split(':')[1].Trim('"'),
DisplayName = x[2].Split(':')[1].Trim('"'),
Dpt = x[3].Split(':')[1].Trim('"')
})
What's left?
Regex like : "(\w+)":"(.+?)", could easy replace the split. With a more detailed regex you can catch only the value you need.
What solution in the end?
Depending on the quality of the file. If it's human generated and can containt error.
You way to handle the error: Do you reject the whole file in case of error? Do you return only the list of valid data? etc.
I will choose either Solution #1 or #2. #1 for sometime broken file. #2 for meticulous error handleing and debugging.
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string inputCSV = #"""Type"":""#Microsoft.Azure"",""Email"":""abc#tmail.com"",""DisplayName"":""abc"",""Dpt"":""home""
""Type"":""#Microsoft.Azure"",""Email"":""xyz#tmail.com"",""DisplayName"":""xyz"",""Dpt"":""home""";
// ReadAllLines mock
string[] lines = inputCSV.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
{ // The Json Way
var bringJsonBack = "[\n{" + string.Join("},\n{", lines) + "}\n]";
var results = JsonConvert.DeserializeObject<List<CsvItem>>(bringJsonBack);
results.Dump();
}
{ // Your working way
var results = new List<CsvItem>();
foreach (var line in lines)
{
var temp = new CsvItem();
string[] strS1 = line.Split(',');
foreach (string S1 in strS1)
{
string[] strS2 = S1.Split(':');
// You have a part Before the : and one after we just string check to know what property we re on.
if (strS2[0].Trim('"') == "Email")
{
temp.Email = strS2[1].Trim('"');
}
if (strS2[0].Trim('"') == "DisplayName")
{
temp.DisplayName = strS2[1].Trim('"');
}
if (strS2[0].Trim('"') == "Dpt")
{
temp.Dpt = strS2[1].Trim('"');
}
}
results.Add(temp);
}
results.Dump();
}
{ // LinQ Version of your algo.
var results = lines
.Select(x => x.Split(','))
.Select(x =>
new CsvItem
{
Email = x[1].Split(':')[1].Trim('"'),
DisplayName = x[2].Split(':')[1].Trim('"'),
Dpt = x[3].Split(':')[1].Trim('"')
})
.ToList();
results.Dump();
}
}
public class CsvItem
{
public string Email { get; set; }
public string DisplayName { get; set; }
public string Dpt { get; set; }
}
}
Your original string looks suspiciously like it was extracted from a JSON response. You should just deserialize the original JSON response directly into a DataTable with Newtonsoft.Json, ala:
//Install-Package Newtonsoft.Json
using Newtonsoft.Json;
using System.Data;
namespace Split_string_and_assign_as_table
{
class Program
{
static void Main(string[] args)
{
string json = #"[
{
""Type"": ""#Microsoft.Azure"",
""Email"": ""abc#tmail.com"",
""DisplayName"": ""abc"",
""Dpt"": ""home""
},
{
""Type"": ""#Microsoft.Azure"",
""Email"": ""xyz#tmail.com"",
""DisplayName"": ""xyz"",
""Dpt"": ""home""
}
]";
var dataTable = JsonConvert.DeserializeObject<DataTable>(json);
}
}
}
I often want to parse a string into various bits and have a readable way to return them.
I like this approach, but it involves creating a specific class
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
In Utils.cs:
public class TradeIdentData
{
public string AccountIdent;
public long OrderID;
public string SubID;
}
public static TradeIdentData UnTradeIdent(string tradeIdent)
{
TradeIdentData tradeIdentData = new TradeIdentData();
var parts = tradeIdent.Split('!');
tradeIdentData.AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
tradeIdentData.OrderID = long.Parse(bits[1]);
tradeIdentData.SubID = bits[1];
}
else
{
tradeIdentData.OrderID = long.Parse(parts[1]);
tradeIdentData.SubID = "";
}
return tradeIdentData;
}
A separate class with well-named properties (which you are already using) is currently the most readable way of doing this.
In C# 7 you will be able to use tuples for return values, like so:
public static (string AccountIdent, string OrderID, string SubID) UnTradeIdent(string tradeIdent)
{
string accountIdent, orderID, subID ;
... Code to initialise accountIdent, orderID and subID appropriately ...
// Now return the data as a tuple:
return (accountIdent, orderID, subID);
}
You can consume this as follows:
long orderID = Utils.UnTradeIdent(tradeIdent).OrderID;
Or if you want all the values:
var result = Utils.UnTradeIdent(tradeIdent);
// Use result.OrderId, result.SubID or result.AccountIdent
This is not going to be available until some time next year, though.
Also, even though this new tuple support makes it more convenient to WRITE the code, it doesn't let you document it using XML comments as well. Spending the time to write a simple and well-documented class will still often be better than using the new C# 7 tuple support.
See here for more details.
You can also use the out keyword to pass arguments by reference, see MSDN article out (C# Reference):
public static void UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
UPDATED with suggestion from comments:
public static bool UnTradeIdent(string tradeIdent, out string AccountIdent, out long OrderID, out string SubID)
{
bool result = false;
AccountIdent = "";
OrderID = 0;
SubID = "";
try
{
var parts = tradeIdent.Split('!');
AccountIdent = parts[0];
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
OrderID = long.Parse(bits[1]);
SubID = bits[1];
}
else
{
OrderID = long.Parse(parts[1]);
SubID = "";
}
}
catch(ArgumentNullException ane)
{
// Handle parsing exception
}
catch (FormatException fe)
{
// Handle parsing exception
}
catch (OverflowException oe)
{
// Handle parsing exception
}
return result;
}
Its pretty simple to do just by changing the return type to dynamic and using an anonymous class
public static dynamic UnTradeIdent(string tradeIdent)
{
var value1 = //parselogic
var value2 = //parselogic
return new { Identity = value1, Item2 = value2};
}
I would consider making additional static methods. Your current implementation is cleaner when you require all of the returned properties, but something like below might be appropriate when you only need one of them.
public static string TradeIdentToAccountIdent(string tradeIdent)
{
var parts = tradeIdent.Split('!');
return parts[0];
}
public static long TradeIdentToOrderID(string tradeIdent)
{
var parts = tradeIdent.Split('!');
if (parts[1].Contains("."))
{
var bits = parts[1].Split('.');
return long.Parse(bits[1]); // Taken from your example, should probably be bits[0]?
}
else
return long.Parse(parts[1]);
}
// My own take on it this time, you could obviously use your logic as well.
public static string TradeIdentToSubID(string tradeIdent)
{
var order = tradeIdent.Split('!')[1];
if (order.Contains("."))
return order.Split('.')[1];
else
return String.Empty;
}
I received some help here with the following LINQ query, but am still struggling with it. The result I'm trying to obtain is to display some attributes and their values from an xml file in a DataGridView control. I'm calling my method from a button click and am trying to pass back the list for display in the grid. Here is an example of the row:
<z:row CenterCode="JAX" CenterName="Jacksonville" Version="1.0" NextExport="66742" NextImport="29756" LastImportTime="2015-06-10T14:48:33" FtpProxyServer="" FtpUserName="" FtpPassword="" ResetImportID="False"/>
Here is the method:
public static List<string[]> MonitorCounts(string upperLimit)
{
// Load xml
XDocument xmldoc = XDocument.Load(#"c:\XML\Configuration.xml");
XNamespace z = "#RowsetSchema";
Int32 limit = Convert.ToInt32(upperLimit);
var elementQuery = xmldoc.Descendants(z + "row").Where(e => (long?)e.Attribute("NextExport") > limit | (long?)e.Attribute("NextImport") > limit);
var attributes = elementQuery.Select(e => e.Attributes().Select(a => new KeyValuePair<string, string>(a.Name.LocalName, (string)a)).ToList()).ToList();
return attributes;
}
My questions are how to select only specific attributes and values in attributes. If I do something like this:
var attributes = elementQuery.Select(e => e.Attributes("CenterName").Select(a => new KeyValuePair<string, string>(a.Name.LocalName, (string)a)).ToList()).ToList();
then this is returned:
[0] = {[CenterName, Jacksonville]}
I need to select this and 4 others. I'm also getting a convrsion error - Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>>>' to 'System.Collections.Generic.List<string[]>. Appreciate any pointers to help me along.
You can use an anonymous type:
var attributes =
elementQuery.Select(e => new
{
CenterName = (string)e.Attribute["CenterName"],
Version = (string)e.Attribute["Version"],
// more attributes
}).ToList();
You can't however return this from the method in a useful way. So if you really need both the attribute name and the attribute value as strings, try this approach instead:
var attributes =
elementQuery.Select(e => new []
{
Tuple.Create("CenterName", (string)e.Attribute["CenterName"]),
Tuple.Create("Version", (string)e.Attribute["Version"]),
// more attributes
}).SelectMany(x => x).ToList();
The return type of your method now has to be List<Tuple<string, string>>.
And finally, if you actually need a List<string[]> as the return type, use this code:
var attributes =
elementQuery.Select(e => new []
{
new [] { "CenterName", (string)e.Attribute["CenterName"] },
new [] { "Version", (string)e.Attribute["Version"] },
// more attributes
}).SelectMany(x => x).ToList();
I solved my own problem. Here is what I did:
Created a class for the attributes needed:
public class dataRow
{
public string CenterName { get; set; }
public string CenterCode { get; set; }
public string NextImport { get; set; }
public string NextExport { get; set; }
public string LastImportTime { get; set; }
}
Selected the results into it:
List<dataRow> dataRows = elementQuery.Select( e => new dataRow
{ CenterName = (string)e.Attribute("CenterName"),
CenterCode = (string)e.Attribute("CenterCode"),
NextImport = (string)e.Attribute("NextImport"),
NextExport = (string)e.Attribute("NextExport"),
LastImportTime = (string)e.Attribute("LastImportTime") }).ToList();
Changed my method to return the correct object:
public static List<dataRow> MonitorCounts(string upperLimit)
Set my grids datasource to the method return:
dataGridView1.DataSource = xmlProcessing.MonitorCounts(tbxUpperLimit.Text.ToString());
return dataRows;
I have this class:
public class allFields
{
public string EAN { get; set; }
public string title { get; set; }
public string qty { get; set; }
public string price { get; set; }
public DateTime date { get; set; }
}
And a function that return an anonymous type:
public IEnumerable<object> stockEtatQty()
{
List<allFields> afList = new List<allFields>();
var query = from x in ctx.book
where x.qty > 0
select x;
foreach (var item in query)
{
allFields af = new allFields();
af.EAN = item.EAN;
af.title = item.Titre;
af.qty = ""+item.Quantite;
afList.Add(af);
}
var q = from x in afList
select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}
In my WinForm a use this function as below:
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty().ToList();// q is a list<object>
string str = "";
foreach (var item in q)
{
str += item + Environment.NewLine;
}
MessageBox.Show(str);
}
The result is:
{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...
What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.
If there is no solution for my problem I would like to know an alternative,
Thanks for help.
The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).
Then you can easily format the output.
public class BookInfo
{
public string EAN {get;set;}
public string Title {get;set;}
public int Quantity {get;set;}
}
public IEnumerable<BookInfo> stockEtatQty()
{
...
var q = from x in afList
select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q;
}
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty();
var message = string.Join(Environment.NewLine,
q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
MessageBox.Show(message);
}
Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:
str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item =>
string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));
The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.
Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.
stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)
Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:
var q = afList.Select(x => {
dynamic res = new ExpandoObject();
res.EAN=x.EAN;
res.Title=x.title;
res.Quantity=x.qty;
return res;
});
Create a new class that represents the new object structure and return that.
var q = from x in afList
select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };
WinForm Function
foreach (SmallerType item in q)
{
//
}
You can use collection of dynamic objects instead of simple objects as return type of your method:
public IEnumerable<dynamic> stockEtatQty()
Then you will not have IntelliSense but at runtime properties will be found:
foreach (var item in sstock.stockEtatQty())
str += String.Format("{0}", item.EAN) + Environment.NewLine;
But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.
Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:
var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);
MessageBox.Show(builder.ToString());