Return Multiple Values C# [duplicate] - c#

This question already has answers here:
Return multiple values to a method caller
(28 answers)
Closed 7 years ago.
I have written a separate class that holds the permissions of my website. I make calls to this class in every class I have. I am having trouble bringing back the values from my permissions class. I will show you what I have below:
Permissions Class
public class Permissions
{
public static string selectedNumber = "";
public static string selectedName= "";
public static string selectedLocation= "";
public Info SetInfo(string selectedValue)
{
string selectInfo = "SELECT [Num] AS 'Number', [Name] AS 'Name', CONVERT(nvarchar(50),RTRIM(b.Num])) + ' - ' + CONVERT(nvarchar(50),b.Name) AS 'Location' "
+ "FROM [TBL_Info] a "
+ "left join [TBL_Where] b on (a.[ID] = b.[ID]) "
+ "WHERE a.ID = #ID";
sqlCmd = new SqlCommand(selectInfo, sqlConn);
sqlConn.Open();
sqlCmd.Parameters.AddWithValue("#ID", selectedValue);
SqlDataReader rdrInfo = sqlCmd.ExecuteReader();
if (rdrInfo.HasRows)
{
rdrInfo.Read();
selectedNumber = rdrInfo .GetSqlString(rdrInfo .GetOrdinal("Number")).ToString();
selectedName= rdrInfo .GetSqlString(rdrInfo .GetOrdinal("Name")).ToString();
selectedLocation = rdrInfo .GetSqlString(rdrInfo .GetOrdinal("Location")).ToString();
}
sqlCmd.Connection.Close();
return new Info()
{
number= selectedNumber,
name= selectedName,
location= selectedLocation
};
}
public class Info
{
public String number{ get; set; }
public String name{ get; set; }
public String location{ get; set; }
}
}
And I am currently trying to call it in another class like this:
Classes.Permissions permission = new Classes.Permissions();
permission.SetInfo(selectedUserValue);
The end product is that I set textboxes in the class I am trying to make the call from with the 3 return values from permission.SetInfo()
Currently I am not able to get anything returned.... I know I am doing something wrong, clearly. So can someone please give me some advice on how to achieve this?

Other than some stylistic things I have issue with, your code looks like it should work (as long as you have data in your database). To return multiple values from a method, create a new object (ie Info) that defines the things to be returned.
So, you should be able to write something like:
Textbox txtBox;
var info = new Classes.Permissions().SetInfo(SelectedUserValue);
txtBox.Text =
info.number ?? "<null>" + " " +
info.name ?? "<null> + " " +
info.location ?? "<null>'
Note that I used the null coalescing operator (??) as SetInfo could return an instance of Info, where all members are null if no rows are returned from your database query.
By the way, the other way to return multiple values from a method is to use out parameters:
Textbox txtBox;
string number;
string name;
string location;
new Classes.Permissions().SetInfo(SelectedUserValue, out number, out name, out location);
And then your SetInfo would look like:
public void SetInfo(string SeelctedUserValue,
out string number, out string name, out string location)
{
//assign values to number,name and location
}
To return multiple instances of the same object, than your method should return
an IEnumerable (ie a List or Array),
a Tuple<>,
a container object.
For example, if you know you are going to return exactly three, you might want to have your method return Tuple<Info,Info,Info>:
public Tuple<Info, Info, Info> SetInfo(string SeelctedUserValue)
{
//query db
return new Tuple<Info, Info, Info>(
new Info{ number = "number", name = "name", location="location},
new Info{ number = "number", name = "name", location="location},
new Info{ number = "number", name = "name", location="location});
}

Related

how to pass generic list parameter into a method?

I am getting phone contacts into a list<> and saving it in a database.
Below is my code.
This is my method to get the contacts-List
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
try {
SetContentView(Resource.Layout.Main);
TextView txtcount = this.FindViewById<TextView>(Resource.Id.textView1);
List<PersonContact> a1 = GetPhoneContacts();
Phone gp = new Phone();
gp.insertContact(a1);
} catch (System.Exception ex) {
alert(ex.Message);
}
}
Via the following method I am trying to store contacts in database
[WebMethod]
public string insertContact<T>(List<PersonContact> a) {
OpenConnection();
if (a.Count > 0) {
for (int i = 0; i < a.Count; i++) {
string str = "insert into phone_contact (FirstName,LastName,PhoneNumber)values('" + a[i].FirstName + "','" + a[i].LastName + "','" + a[i].PhoneNumber + "')";
SqlCommand cmd = new SqlCommand(str, con);
cmd.ExecuteNonQuery();
}
return "1";
} else {
return "1";
}
}
public class PersonContact {
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
}
I am getting an error while passing parameter
gp.insertContact(a1);
Your method is generic, as it introduces a new type parameter T. That's what the <T> at the end of the method name means.
However, you don't use T anywhere - so just make it a non-generic method:
public string InsertContact(List<PersonContact> a)
At the same time, I would very strongly urge you to change the way you're doing database access: it's currently vulnerable to SQL injection attacks. Instead, you should use parameterized SQL: have one parameter for each of FirstName, LastName and PhoneNumber.
You're also returning "1" regardless of the input. Your method could be written more simply as:
// Consider renaming to InsertContacts, as it's not just dealing with a single
// contact
public string InsertContact(List<PersonContact> contacts)
{
// You should almost certainly use a using statement here, to
// dispose of the connection afterwards
OpenConnection();
foreach (var contact in contacts)
{
// Insert the contact. Use a using statement for the SqlCommand too.
}
return "1";
}
That's assuming you need the value returned at all - if you're always returning the same value, why not just make it a void method?

How to Output from Generic Array List, to Listbox?

I am trying to output from Array list to a Listbox. My problem is I think is I do not know how to connect the Class to the Generic array list a made? The end result should look like this:
And the information should be then sorted like so: all the information enters the first list box, and then the above 18 goes to adults, and the below 18 to kids. My class looks like this:
namespace Patients
{
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public String Password { get; set; }
public Person() //Constructor
{
Age = 0;
Password = "";
}
public Person (string name, int age, string password) //Parameters
{
this.Name = name;
this.Age = age;
this.Password = password;
}
public override string ToString() //
{
return Name + Age.ToString() + Password; //outputs as a string
// return Name + " (" + Age + " years) " + Password ;
}
}
}
namespace Patients
{
public partial class WebForm1 : System.Web.UI.Page
{
public static void Page_Load(object sender, EventArgs e)
{
}
public void ButtonAdd_Click(object sender, EventArgs e)
{
Person p = new Person();
List<string> People = new List<string>();
People.Add(TextBoxName.Text);
People.Add(TextBoxAge.Text);
People.Add(TextBoxPassword.Text);
foreach (object Person in People)
{
ListBoxAll.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
if (p.Age > 18)
{
ListBoxAdults.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
else
{
ListBoxKids.Items.Add(p.Name + p.Age.ToString() + p.Password);
}
}
}
}
I think your problem is, that you don't set the Properties. In Fact you don't need a List at all, but you can use a List to keep hold of your patients. It's still not necessary though:
namespace Patients
{
public partial class WebForm1 : System.Web.UI.Page
{
// Define Property and initialize List
public List<Person> patients{ get; } = new List<Person>();
public static void Page_Load(object sender, EventArgs e)
{
}
public void ButtonAdd_Click(object sender, EventArgs e)
{
// Use the Constructor with Parameters
Person p = new Person(TextBoxName.Text, TextBoxAge.Text, TextBoxPassword.Text);
// Add your patient to your List
patients.Add(p);
// Use the ToString() of your Person
ListBoxAll.Items.Add(p.ToString());
if (p.Age > 18)
{
ListBoxAdults.Items.Add(p.ToString());
}
else
{
ListBoxKids.Items.Add(p.ToString());
}
}
}
}
Looks like you are mixing and matching a bit.
Try something like this.
Person p = new Person();
p.Name = TextBoxName.Text;
p.Age= TextBoxAge.Text;
p.Password= TextBoxPassword.Text;
ListBoxAll.Items.Add(p);
A few tricks that are nice to us, first off you can declare defaults for properties like so:
public string Name { get; set; } = "Steve";
public int Age { get; set; } = 1;
public String Password { get; set; } = "password";
However, it should also be noted that "" is the default for strings already and 0 is the default for non-nullable int, so you don't even need to worrying about those default values.
Declaring Age = 0; in the constructor is basically a waste of time in this case. (If it was a nullable int however the default is null)
Next up, since you are okay with defaults, you don't need to declare properties in the constructor like you are.
You can completely remove the constructor and just do the following:
var myPerson = new Person { Name = "Steve", Age = 18, Password = "Foo" };
Next up, you are losing all your existing people as soon as you exit the scope of the button click.
Instead you'll want to declare two lists of people outside the scope of the click method (that way they persist), something like "Adults" and "Children"
Then perhaps make a method called "PopulateLists" that would do the following:
Clear all list boxes
Add to each box the list of each groups names that apply (you can make an IQueryable by using Linq and Select statements on your list)
When you click the button, you should make a new person, assign it to the right list, then call PopulateLists()
Here's the info you need to get started:
Linq selection to get list of properties (in this case Im going to turn a List of People into a List of Ages, you can do the same with names though)
var ages = People.Select(p => p.Age);
The .Items property of a ListBox works the same as a list, it just visually shows itself. It's a list of strings specifically.
So for example you can do things like...
MyListBox.Items.Clear();
MyListBox.Items.Add(...);
MyListBox.Items.AddRange(...);
etc etc.
That should get you started!

Formatting and displaying data from an access database (WPF, C#)

Evening all,
To the point: Within my WPF application I would like to display data from an access database within a listbox in the format of a ToString method created within another class. -- I can display the data, but it does not contain formatting.
Context of my question:
I am creating an application for my graded unit at college which adds, deletes and displays data from an access database. I am having no trouble with adding or deleting data to the database, however, I am struggling to display the data in a particular format.
Due to specific requirements, I have had to create an abstract Games class, with the subclasses Platform and Mobile (games).
I would like to know how to display data from an access database in a listbox (though this is flexible to change), whilst formatting the content to a previously created ToString() method in both the Platform and Mobile class. I understand that I may have to create two separate methods to display platform and mobile games, as they each have an additional variable.
Currently, I am storing my listPlatform() method within my Catalogue class, which is accessed from a separate window (EmployeeWindow, which contains the list view box, then accessing this method and calling it via a button_click event.
Catalogue class --
public List<string> listPlatform()
{
List<string> data = new List<string>();
string queryString = "SELECT ID, Game_Name, Developer, Publisher, Genre, Age_Rating, Price, Quantity, Description, Platform FROM GameDetails";
using (OleDbConnection connection = new OleDbConnection(ConnString))
{
OleDbCommand command = new OleDbCommand(queryString, connection);
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int id = reader.GetInt32(0);
string gName = reader.GetString(1);
string gDeveloper = reader.GetString(2);
string gPublisher = reader.GetString(3);
string gGenre = reader.GetString(4);
int gAgeRating = reader.GetInt32(5);
var gPrice = reader.GetValue(6);
var gQuantity = reader.GetValue(7);
var gDescription = reader.GetValue(8);
var gPlatform = reader.GetValue(9);
data.Add(id + gName + gDeveloper + gPublisher + gGenre + gAgeRating + gPrice
+ gQuantity + gDescription + gPlatform);
}
reader.Close();
}
return data;
}
EmployeeWindow --
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
List<string> data = theCatalogue.listPlatform();
lstvwGames.Items.Clear();
foreach (string s in data)
{
lstvwGames.Items.Add(s);
}
}
Platform class --
/// <summary>
/// Returns a string representation of a Platform game
/// </summary>
/// <returns></returns>
public override string ToString()
{
string strout = string.Format(base.ToString() + "Platform:{0}", platform);
return strout;
}
I hope that my question makes sense and that I have provided enough information to give you some understanding of what it is that I am trying to do.
I think that, in this scenario, you need to add another class to your code.
The Game class that you could model looking at the fields present in your database table
public class Game
{
public int ID {get;set;}
public string GameName {get;set;}
public string Developer {get;set;}
public string Publisher {get;set;}
public string Genre {get;set;}
public string Age_Rating {get;set;}
public decimal Price {get;set;}
public int Quantity {get;set;}
public string Description {get;set;}
public string Platform {get;set;}
public override string ToString()
{
return this.GameName + " - " + this.Genre;
}
}
Now in the Catalogue class, when you read your database you build a List<Game> not a List<String>
public List<Game> listPlatform()
{
.....
List<Game> games = new List<Game>();
while (reader.Read())
{
Game g = new Game();
g.ID = reader.GetInt32(0);
g.GameName = reader.GetString(1);
... and so on for the rest of fields
games.Add(g);
}
...
return games;
}
Finally, when you need to add the games to your ListBox, you could write
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
List<Game> data = theCatalogue.listPlatform();
lstvwGames.Items.Clear();
foreach (Game g in data)
{
lstvwGames.Items.Add(g.ToString());
}
}
And you have a list filled with GameName and Genre.
EDIT to complete this answer,
Finally, you could directly set the DataSource, DisplayMember and ValueMember properties of the ListBox with your List<Game> and some of its properties removing the need to have a loop to fill items
private void btnDisplay_Click(object sender, RoutedEventArgs e)
{
lstvwGames.ValueMember = "ID";
lstvwGames.DisplayMember = "GameData";
lstvwGames.DataSource = theCatalogue.listPlatform();
}
In this example the DisplayMember property is assigned to a new GameData field that you should define inside your Game class. This new readonly property could be the actual return value of the ToString method of the same class or another value of your choice
public class Game
{
.....
public string GameData
{
// Only a getter, thus readonly
get
{
return this.ToString();
}
}
}
Of course you could change the ToString method or the GameData property inside the Game class to return the info you really want to display in the listbox.
I do not see you using the tostring override method. Maybe you meant to use it here?
lstvwGames.Items.Add(s.ToString());

C# Sorting using Delegate

I'm new to C# & having trouble wi/ this current assignment. I need to use a Delegate to sort Sort the employees by social security number in descending order and by last name in ascending order. If anyone can just point me in how to get started it will help greatly. This is just the employee class, but if needed, I can post all the classes.
using System;
// Fig. 12.4: Employee.cs
// Employee abstract base class.
using System.Text;
public abstract class Employee : IPayable
{
// read-only property that gets employee's first name
public string FirstName { get; private set; }
// read-only property that gets employee's last name
public string LastName { get; private set; }
// read-only property that gets employee's social security number
public string SocialSecurityNumber { get; private set; }
// three-parameter constructor
public Employee( string first, string last, string ssn )
{
FirstName = first;
LastName = last;
SocialSecurityNumber = ssn;
} // end three-parameter Employee constructor
// return string representation of Employee object, using properties
public override string ToString()
{
return string.Format( "{0} {1}\nsocial security number: {2}",
FirstName, LastName, SocialSecurityNumber );
} // end method ToString
// abstract method overridden by derived classes
public abstract decimal GetPaymentAmount(); // no implementation here
} // end abstract class Employee
// Fig. 12.15: PayableInterfaceTest.cs
// Tests interface IPayable with disparate classes.
using System;
public class PayableInterfaceTest
{
public static void Main( string[] args )
{
// create four-element IPayable array
IPayable[] payableObjects = new IPayable[8];
// populate array with objects that implement IPayable
payableObjects[0] = new SalariedEmployee("John", "Smith", "111-11-1111", 700M);
payableObjects[1] = new SalariedEmployee("Antonio", "Smith", "555-55-5555", 800M);
payableObjects[2] = new SalariedEmployee("Victor", "Smith", "444-44-4444", 600M);
payableObjects[3] = new HourlyEmployee("Karen", "Price", "222-22-2222", 16.75M, 40M);
payableObjects[4] = new HourlyEmployee("Ruben", "Zamora", "666-66-6666", 20.00M, 40M);
payableObjects[5] = new CommissionEmployee("Sue", "Jones", "333-33-3333", 10000M, .06M);
payableObjects[6] = new BasePlusCommissionEmployee("Bob", "Lewis", "777-77-7777", 5000M, .04M, 300M);
payableObjects[7] = new BasePlusCommissionEmployee("Lee", "Duarte", "888-88-888", 5000M, .04M, 300M);
Console.WriteLine(
"Lab 2 output:\n" );
// generically process each element in array payableObjectsWW
foreach ( var currentPayable in payableObjects )
{
// output currentPayable and its appropriate payment amount
Console.WriteLine( "payment due {0}: {1:C}\n",
currentPayable, currentPayable.GetPaymentAmount() );
} // end foreach
} // end Main
} // end class PayableInterfaceTest
Why don't you use LINQ ?
var sortedArray = payableObjects
.OrderByDescending(e => e.SocialSecurityNumber)
.ThenBy(e => e.LastName)
.ToArray();
Array.Sort(T[], delegate int(T, T)) is probably what you're looking for.
Call Array.Sort with your array as the first parameter, and create a function in your Employee that fills delegate int(Employee, Employee). If I recall correctly, that function needs to return 0 if they are equal, a negative number if the first comes first ascending, and a positive number if the first comes second ascending.
//to order by SSN desc
foreach (var e in payableObjects.OrderByDescending(e => e.SocialSecurityNumber))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}
Console.WriteLine("");
//to order by Lastname Asc
foreach (var e in payableObjects.OrderBy(e => e.LastName))
{
Console.WriteLine(e.FirstName + " " + e.LastName + " " + e.SocialSecurityNumber);
}

Custom property attribute

Consider the following function, it simply generates an Sql UpdateCommand for an Object
public static string UpdateCommand<T>(this T obj,string idPropertyName, List<string> Except = null, List<string> Only = null)
{
List<PropertyInfo> properties = FilterPropertyList<T>(Except, Only);
StringBuilder query = new StringBuilder();
query.Append("UPDATE " + typeof(T).Name + " SET ");
for (int i = 0; i < properties.Count; i++)
{
if (idPropertyName.ToLower() == properties[i].Name.ToLower())
continue;
query.Append("[" + properties[i].Name + "] = #" + properties[i].Name + ",");
}
if (properties.Count > 1)
{
query.Length -= 2;
}
query.Append(" WHERE " + idPropertyName + "=#" + idPropertyName);
return query.ToString();
}
the second parameter is just the property name which refers to the property name that represents the primary key in the Sql table, i was wondering if its possible to represent that property with an attribute that would be available in the property info, this way i wont have to send it as a parameter.
if this was my object
public class SomeObject
{
//add a custom attribute to the id so it would be recognized in the above function without having to send the property name as a parameter
public int id {get;set;}
public string name {get;set}
}
the following is how i use the function with a SomeObject instance
var someObject = new SomeObject();
var someObjectUpdateCommandString = someObject.UpdateCommand<SomeObject>("id");
can i use some built in attributes, or is it better to create my own attribute?
Here is my try, not sure if its write and i cant seem to use it
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class IsPrimaryKey : Attribute
{
public IsPrimaryKey()
{
this.isPrimaryK = true;
}
private bool isPrimaryK;
public virtual bool IsPrimaryK
{
get { return isPrimaryK; }
set { isPrimaryK = value; }
}
}
The name of the attribute class should end with the Attribute suffix.
Generally, it is a good idea to reuse existing annotations, if they have exactly the purpose that you are looking for, and don't pull unnecessary dependencies.
In your case, the System.ComponentModel.DataAnnotations.KeyAttribute attribute, added in .NET 4.0, would probably fulfill the purpose you are looking for.

Categories