New to LINQ but this should be fairly simple.
I'm pulling a recordset of products from the DB:
ReportData= topProductsController.GetWowDetails(CurrentUser.UserId, _companyGroupCode, sector, year, string.Empty, string.Empty, string.Empty);
and from that recordset I'm trying to group the results by the product ID and count:
var productCounts = (from record in wowReportData
group record by record.ProductID into grouping
select new topProduct { Name = grouping.Key, quantity = grouping.Count() });
Here's the class I'm trying to return:
public class topProduct
{
public int quantity { get; set; }
public string Name { get; set; }
public topProduct(string productDesc, int downloadCount)
{
this.Name = productDesc;
this.quantity = downloadCount;
}
}
I'm trying to return a list of these from the function. The current error is that:
topProduct does not contain a constructor that takes 0 parameters
The reason it is failing is because you are using the property initializer way to set values to your properties, and at least in the way you called it (new topProduct {...) it will first initialize the object using the default constructor. But you don't have one.
Change to this:
var productCounts = (from record in wowReportData
group record by record.ProductID into grouping
select new topProduct(grouping.Key, grouping.Count()));
Or add a default constructor (which that is what I'd do) and then you can use it as you did
public class topProduct
{
public int quantity { get; set; }
public string Name { get; set; }
//default constructor
public topProduct() {}
public topProduct(string productDesc, int downloadCount)
{
this.Name = productDesc;
this.quantity = downloadCount;
}
}
The use of () if for when you are initializing an object and you call a constructor - () being the default constructor (with no parameters). This one is created automatically in the case you have not created any other constructor. See here about constructors.
Now in C# 3.5 if I'm not mistaking they introduced the ability to initialize properties inline with the initialization of the object and thus to save you the pain of creating a big array of constructors for all the different options. But that is just a nice syntactic sugar for:
var obj = new Class() { Prop1 = "a", Prop2 = 2 };
||
var obj = new Class();
obj.Prop1 = "a";
obj.Prop2 = 2;
Then they even enabled you to remove the empty () (in the case that the constructor you are calling is the default constructor) and you turn out to have: var obj = new Class { Prop1 = "a", Prop2 = 2 }; but you can't do this if you don't have a default constructor like in your original case.
Related
I have a series of C# static lists in an API project that are very similar to the simple example defined here.
using System.Collections.Generic;
namespace myproject.api.PropModels
{
public class CommonSelectOptionsYesNoItem
{
public int Id { get; set; }
public string Title { get; set; }
}
public static class CommonSelectOptionsYesNo
{
public static readonly List<CommonSelectOptionsYesNoItem> Table = new List<CommonSelectOptionsYesNoItem>
{
new CommonSelectOptionsYesNoItem { Id = 0, Title = "No",},
new CommonSelectOptionsYesNoItem { Id = 1, Title = "Yes",},
};
}
}
These models establish a common information reference between a Javascript web application and the API that services the application.
User's are uploading spreadsheet data to the API that includes the list class name and the Title of an item in the list. I need to be able to determine what Id is associated with the Title - if any.
For example I know that this the information is in the list CommonSelectOptionsYesNo.Table and the Title property is "Yes". I can therefore determine that the the Id is 1.
In principle I can set up a switch / case method that picks the list identified as CommonSelectOptionsYesNo.Table and then gets the Id value. There are however than 60 of these reference lists and they keep growing.
Can I use reflection to invoke an instance of the readonly static list based on the the static class object name - in this example CommonSelectOptionsYesNo.Table?
After further research have worked out the following method to call up the static readonly list and return the Id for any given "Title" value.
The propModelKey is stored with the static list class in a dictionary of all the lists.
The list can be extracted as an object - knowing that the list is always declared with the property name "Table".
The properties of the list objects can vary depending on the purpose of the list but they always have the "Id" and "Title" properties. Serializing and deserializing the object with the simple class object "SelectOptions" generates a list that can be queried to extract the Id corresponding to the Title string submitted.
// This will return an Id of 1 from the simple YesNo list
var id = GetSelectListIndex("QuestionOneId", "Yes");
// Method to extract the Id of a value in a list given the list model key
private static int? GetSelectListIndex(string propModelKey, string title)
{
if (SelectListModelMap.ContainsKey(propModelKey))
{
var model = SelectListModelMap[propModelKey];
var typeInfo = Type.GetType("myproject.api.PropModels." + model).GetTypeInfo();
var fieldInfo = typeInfo.DeclaredFields.FirstOrDefault(x => x.Name == "Table");
var json = JsonConvert.SerializeObject(fieldInfo.GetValue(new object()));
var dictionary = JsonConvert.DeserializeObject<List<SelectOptions>>(json);
var index = dictionary.FirstOrDefault(x => x.Title == title)?.Id;
return index;
}
return null;
}
// Dictionary of lists with model key and class name
public static Dictionary<string, string> SelectListModelMap => new Dictionary<string, string>
{
{ "QuestionOneId", "CommonSelectOptionsYesNo" },
{ "CountryId", "CommonSelectOptionsCountries" },
// ... other lists
};
// generic class to extract the Id / Title pairs
public class SelectOptions
{
public int Id { get; set; }
public string Title { get; set; }
}
I have this class
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
}
I have a normal ListBox. Im selecting Data from MySql.
private void ListUsers(string server)
{
List<UserData> ls = new List<UserData>();
foreach(dynamic obj in _data)
{
if(obj.servername == server)
{
ls.Add(new UserData() { Name = obj.username, Val = obj.password });
}
}
UserList.Sorted = true;
UserList.DisplayMember = "Name";
UserList.ValueMember = "Val";
UserList.DataSource = ls;
}
When debugging the ls it contains
[0]
Name => "Test",
Val => "12345"
[1]
Name => "Test2",
Val => "54321"
Now sometimes there ist only 1 postition in that list. If this happens, I want to select that entry or at least the Name and paste this into a textbox.
But for some reason I cant achive this. And Google didnt brought any results. At least non that suites to my problem.
I tried
rdpUserList.Items[0].ToString();
but this brings me ProjectName.UserData and not Test.
What is the right way to select the first Item in a list that was generated by a datasource ?
You're calling ToString() on an instance of the type ProjectName.UserData, which gives you its type name.
You want to access that instance's Name property instead.
If rdpUserList is a List<UserData>, you want this:
rdpUserList.Items[0].Name
If instead it's a datasource, you need to cast the item in order to access its properties:
((ProjectName.UserData)rdpUserList.Items[0]).Name
There are two approaches to this issue. The first is a logical problem; you need to call the Name property and not the ToString() method. Note that using this way, you need to cast to your object type.
((UserData)rdpUserList.Items[0]).Name
The second option is to override the ToString() method so you can call the name the way you tried.
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
public override string ToString()
{ return this.Name; }
}
and then call with
rdpUserList.Items[0].ToString()
Sorry for previous post, I did not test before I answered,
try the following if you want, I just like linq :)
var item = rdpUserList.Items.OfType<ListItem>().First();
Class1 t = new Class1(){Id=Convert.ToInt32(item.Value), description = item.Text};
OR for just the name
string name = rdpUserList.Items.OfType<ListItem>().First().Text;
For example, I have an immutable type
class Contact
{
// Read-only properties.
public string Name { get; }
public string Address { get; }
}
And I hope I can use object initializer syntax to create a Contact
Contact a = new Contact { Name = "John", Address = "23 Tennis RD" };
But I cannot. Any possible way to make use of the powerful object initializer syntax in this case?
The closest thing would be a constructor with optional parameters:
class Contact
{
public string Name { get; }
public string Address { get; }
public Contact(string name = null, string address = null) {
Name = name;
Address = address;
}
}
Then you can call it with parameter names:
new Contact(
name: "John",
address: "23 Tennis RD"
)
The syntax is slightly different from an object initializer, but it's just as readable; and IMO, the difference is a good thing, because constructor parameters tend to suggest immutable properties. And you can specify the parameters in any order, or leave some out, so it's just as powerful as object initializer syntax.
This does require some extra code (defining the constructor, assigning all the properties), so it's more work than object initializer syntax. But not too terrible, and the value of immutable objects is worth it.
(For what it's worth, C# 7 may get immutable "record types" that have much simpler syntax. These may or may not make it into the final release, but they sound pretty cool.)
This is dated now, but with the release of C# 9 you can use init to achieve the desired functionality.
So your example would become:
class Contract
{
// Read-only properties.
public string Name { get; init; }
public string Address { get; init; }
}
And then you could initialize with:
// success!
Contract a = new Contract { Name = "John", Address = "23 Tennis RD" };
But you would still be unable to modify the parameters after setting them (so effectively they are still readonly).
// error!
a.Name = "Uncle Bob";
Under the hood, when you use object initializer syntax prior to C# 9 the compiler would call the default constructor first, and then set the property values you've specified. Obviously if those properties are readonly (i.e. only a get method), it can't set them. The init only setter allows setting the value only on initialization, either via a constructor method or object initializer syntax.
More info is available here: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#init-only-setters
Nope, you cannot use it with readonly properties.
Here are the different property and field types in comparism.
public class sometype {
public int readonlyProp{
get;
}
public int normalProp {
get;
set;
}
public const int constField = 3;
public readonly int readonlyField = 3;
public int normalField = 3;
public void test() {
sometype test = new sometype() { readonlyProp = 3}; // Doesn't work -> Property or indexer is readonly
sometype test1 = new sometype() { normalProp = 3 }; // ok
sometype test2 = new sometype() { constField = 3 }; // Doesn't work -> Static field or property
sometype test3 = new sometype() { readonlyField = 3 }; // Doesn't work -> readonly field
sometype test4 = new sometype() { normalField = 3 }; // ok
}
}
It is important to understand that const fields are considered static and thus are not instance members. And since the object initializer is used for instance members this doesn't work.
Object initializer will first construct the object, then set property values.
It needs setters.
It's short hand for:
Contact a = new Contact();
a.Name = "John";
a.Address = "23 Tennis RD";
A readonly field can't have it's values set once the object has been constructed. To have that class immutable, you'll need to create a constructor to take those values:
class Contact // Immutable class
{
// Read-only properties.
public string Name { get; }
public string Address { get; }
public Contact(string name, string address)
{
this.Name = name;
this.Address = address;
}
}
pretty simple setup.
public ObservableCollection<ParentNode> CreateTreeViewCollection(string ClassName)
{
EnumerateFullData AllData = new EnumerateFullData() { ClassName = ClassName.Clone() };
}
public class EnumerateFullData
{
public Object ClassName { get; set; }
public List<PropertyData> Properties { get; set; }
}
when the object is created the ClassName value is null even tho it has a value before the class I created.
Once the class has completed and the debugger comes back to the original class, the value is there again.
Im thinking this has to do with instance spawning, can anyone advise how I pass this reference into the newly created class?
The ClassName value gets assigned after the object is created since use are using class initializer. If you want to assign the value during object creation use a constructor. Also, you don't need to use ClassName.Clone() if you change the property to string type as string is immutable. Following code should work:
public ObservableCollection<ParentNode> CreateTreeViewCollection(string ClassName)
{
EnumerateFullData AllData = new EnumerateFullData(ClassName);
}
public class EnumerateFullData
{
public EnumerateFullData (string className)
{
ClassName = className;
}
public string ClassName { get; set; }
public List<PropertyData> Properties { get; set; }
}
You're mixing ClassName that is EnumerateFullData's property and ClassName that is
CreateTreeViewCollection's argument; so you're trying to clone null. Change your code for
// Let argument be in the camel case, "className" not "ClassName"
public ObservableCollection<ParentNode> CreateTreeViewCollection(string className)
{
// property "ClassName" is a clone of argument "className"
EnumerateFullData AllData =
new EnumerateFullData() { ClassName = className.Clone() };
}
You're using object initializer. They are just syntactic sugar. writing
EnumerateFullData AllData = new EnumerateFullData() { ClassName = ClassName.Clone() };
is actually this:
var temp = new EnumerateFullData();
temp.ClassName = ClassName.Clone();
EnumerateFullData AllData = temp;
First you create your class and only after that you set the ClassName Property.
You can do that in a single like because the compiler does that for you.
It means that when the EnumerateFullData instance is created the ClassName is really null.
I have the following class:
public class Topic
{
public string Topic { get; set; }
public string Description { get; set; }
public int Count { get; set; }
}
I would like to have the Count always set to zero when the class is created with the following:
var abc = new Topic {
Topic = "test1",
Description = "description1"
}
I am a bit confused with constructor. Is this possible or do I need to specify Topic, Description and Count when I create abc?
The default value of an int is 0.
All value types have default values as they can't be null.
See Initializing Value Types on this MSDN page.
You have a few different options.
1) int defaults to zero so it will be zero if you dont initialize it.
2) you can use a constructor
public Topic(){ Count = 0;}
3) You can use a backing field instead of auto-property and initialize that to zero
private int _count = 0;
public int Count {
get {return _count}
set {_count = value; }
}
Count will default to 0 on initialisation, since it is a value type and can't be null.
This following idiom is not only a constructor:
var abc = new Topic {
Topic = "test1",
Description = "description1"
}
It's a constructor and an object initializer.
What really happens is that new Topic() is called first, hence initializing all values to their defaults (property Topic is null, Description is null and Count is 0). After that, the value "test1" is assigned to Topic, and the value "description1" is assigned to Description.
All value types have a default value different than null (since they can't be null), and reference types default to null.
public class Program
{
public static void Main()
{
// Declare a StudentName by using the constructor that has two parameters.
StudentName student1 = new StudentName("Craig", "Playstead");
// Make the same declaration by using a collection initializer and sending
// arguments for the first and last names. The default constructor is
// invoked in processing this declaration, not the constructor that has
// two parameters.
StudentName student2 = new StudentName
{
FirstName = "Craig",
LastName = "Playstead",
};
// Declare a StudentName by using a collection initializer and sending
// an argument for only the ID property. No corresponding constructor is
// necessary. Only the default constructor is used to process object
// initializers.
StudentName student3 = new StudentName
{
ID = 183
};
// Declare a StudentName by using a collection initializer and sending
// arguments for all three properties. No corresponding constructor is
// defined in the class.
StudentName student4 = new StudentName
{
FirstName = "Craig",
LastName = "Playstead",
ID = 116
};
System.Console.WriteLine(student1.ToString());
System.Console.WriteLine(student2.ToString());
System.Console.WriteLine(student3.ToString());
System.Console.WriteLine(student4.ToString());
}
// Output:
// Craig 0
// Craig 0
// 183
// Craig 116
}
public class StudentName
{
// The default constructor has no parameters. The default constructor
// is invoked in the processing of object initializers.
// You can test this by changing the access modifier from public to
// private. The declarations in Main that use object initializers will
// fail.
public StudentName() { }
// The following constructor has parameters for two of the three
// properties.
public StudentName(string first, string last)
{
FirstName = first;
LastName = last;
}
// Properties.
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public override string ToString()
{
return FirstName + " " + ID;
}
}
EDIT
As I learned from the comments to this answer, it is perfectly valid to leave out the () in the initializer call.
The correct syntax would be My preferred syntax still is:
var abc = new Topic() {
Topic = "test1",
Description = "description1"
}
(note the ()).
This would initialize Count to 0, as 0 is the default value for int. In case you want to always specify Topic and Description, add an explicit constructor:
public Topic(string topic, string description)
{
Topic = topic;
Description = description;
// You may also set Count explicitly here, but if you want "0" you don't need to
}