Populate checkboxes via database - c#

I have to populate checkboxes with data coming from database, but no checkboxes are showing on my page. Please let me know the correct way to do that. In C#, the page_load method I've written is this:
public partial class dbTest1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string Server = "al2222";
string Username = "hshshshsh";
string Password = "sjjssjs";
string Database = "database1";
string ConnectionString = "Data Source=" + Server + ";";
ConnectionString += "User ID=" + Username + ";";
ConnectionString += "Password=" + Password + ";";
ConnectionString += "Initial Catalog=" + Database;
string query = "Select * from Customer_Order where orderNumber = 17";
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
if (!IsPostBack)
{
Interests.DataSource = dr;
Interests.DataTextField = "OptionName";
Interests.DataValueField = "OptionName";
Interests.DataBind();
}
}
conn.Close();
conn.Dispose();
}
}
}
}
And in the .aspx, I have this:
<asp:CheckBoxList ID="Interests" runat="server"></asp:CheckBoxList>
Please tell me the correct way to accomplish this.

Although your question is already answered (via the connection string comments), I thought I'd chime in with a possible way to rewrite this. I'd started this off as a comment, but it got a bit long and unwieldy. Note that this doesn't directly answer your question, but it is something to consider for code cleanliness and a possible (likely very mild) performance boost on postbacks.
protected void Page_Load(object sender, EventArgs e)
{
// If we're in postback, let's not poll the database.
if (Page.IsPostback)
return; // Change this if you do need some postback processing here.
// I assume that in the real world you pull this info from web.config
string Server = "al2222";
string Username = "hshshshsh";
string Password = "sjjssjs";
string Database = "database1";
string ConnectionString = "Data Source=" + Server + ";";
ConnectionString += "User ID=" + Username + ";";
ConnectionString += "Password=" + Password + ";";
ConnectionString += "Initial Catalog=" + Database;
string query = "Select * from Customer_Order where orderNumber = 17";
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(query, conn))
{
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
// Going to assume that you're only getting 1 record
// due to apparent key (orderNumber = 17) in query?
// You can also consider "if (dr.Read())", but fundamentally
// they will do the same thing.
while (dr.Read())
{
Interests.DataSource = dr;
Interests.DataTextField = "OptionName";
Interests.DataValueField = "OptionName";
Interests.DataBind();
}
// I've excised the calls to .Close() and .Dispose(),
// as the using block covers them for you.
}
}
}
Why would we go this route?
In your original code, you were polling the database (and potentially looping, if my assumption about that being a single-record query was wrong) every page load, whether or not you were in postback. You weren't checking postback until you were inside the loop, where the damage was mostly already done. In the code I've listed, you short-circuit out of Page_Load() altogether if you're in postback. You can, of course, change that to an if/else and bracket the groups if you need some load-event processing on postbacks as well. This also simplified your in-loop code.
Your using blocks covered the disposal/closure of the connection for you. Thus, you do not need that additional code.
As OrbMan stated in the comments, hopefully in your actual code you're retrieving all the connection string info from your web.config file instead of hard-coding it, correct?
Final final unrelated note: This is a lot of data access code that newer versions of the .NET Framework simplify greatly with tools such as Entity Framework and LINQ-to-SQL. There are also 3rd-party data access layer tools (such as SubSonic and ActiveRecord) that will simplify this. Using tools such as those will greatly reduce the amount of code you're writing here -- and I'm guessing you're using quite a bit of similar code throughout your app as well, so those tools will provide you the developer with quite the productivity boost. (And much simpler down-the-road maintenance.)
Just food for thought.

Related

Error saving data from textbox to sql server table

I have created a WPF desktop application before and I was able to write up the code to save data from a textbox to a table I created in sql server 2012. I tried to create a WPF Web browser application and the same code was not working to save my data to my sql database. I am now trying to create another WPF desktop application and the same code that worked the last time is not working anymore. Please look at my code and help.
private void savebuyers_Click(object sender, RoutedEventArgs e)
{
string connectionstring = null;
connectionstring = "Data Source=FRANCIS;Initial Catalog=Pam Golding;Integrated Security=SSPI";
SqlConnection con = new SqlConnection(connectionstring);
try
{
string query;
query = "insert into buyers (name,number,email) values ('" + namebuyers.Text + "'," + Convert.ToInt32(numberbuyers) + ",'" + emailbuyers.Text + "')";
SqlCommand command = new SqlCommand(query, con);
message1.Text = "Data Saved Successfully!";
con.Open();
command.ExecuteNonQuery();
con.Close();
}
catch
{
message1.Text = "Error While Saving Data!";
}
}
You have missed the Text property of numberbuyers. So it is unable to cast object of type TextBox to type System.IConvertible.
You can fix it like this:
Convert.ToInt32(numberbuyers.Text)
Also you should always use parameterized queries to avoid Sql Injection.

how to use retrieve and use setting value to connect to adatabase

i am developing window application and i want the user of the app can change the connection string so i create aform to save connection string to setting and able to retrieve it but the problem is how to use this setting
private void button1_Click(object sender, EventArgs e)
{
var serv = Properties.Settings.Default.server;
var db = Properties.Settings.Default.database;
var userid = Properties.Settings.Default.userid;
var pass = Properties.Settings.Default.password;
SqlConnection conn = new SqlConnection("Data Source=serv;Initial Catalog=db;User ID=userid password=pass");
SqlDataAdapter sda = new SqlDataAdapter("SELECT count(*) FROM users WHERE username='" + txtUsername.Text + "' and password='" + txtPassword.Text + "'", conn);
}
Put your connection string in the App.config/Web.config, it will make it that much easier to alter later on if need be.
Also remember to always make use of the using statement when working with SqlConnection in general.
For example:
In the App.config/Web.config add the following:
<appSettings>
<add key="myConnectionString" value="Data Source=serv;Initial Catalog=db;User ID=userid password=pass" />
</appSettings>
Then you can easily access it anywhere in your project:
using (SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["myConnectionString"]))
{
using(SqlCommand sqlCommandConn = new SqlCommand(InsertStatement))
{
sqlCommandConn.Connection = conn;
//TODO: Open connection, Execute queries...
}
}
Note
You can alter these settings via code as well if you wish:
private void UpdateConfig(string key, string value)
{
var configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
configFile.AppSettings.Settings[key].Value = value;
configFile.Save();
}
Try
SqlConnection conn = new SqlConnection("Data Source=" + serv.ToString() + ";Initial Catalog=" + db.ToString() + ";User ID=" + userid.ToString() + ";password= " + pass.ToString());
As you can see from your code snippet, the connection string value is just a string that you are passing to the SqlConnection constructor, so for your case you could pull the value at runtime and load that value dynamically.
While it is possible to manipulate the app.config file which does hold connection string values, I typically prefer to manipulate secondary files. A better option could be to use a secondary file in XML for example and preform CRUD like operations on it as your users change their connection strings. At runtime, you can pull their specific connection string value and load it into the constructor as your doing above. A sample XML structure could be as follows:
<connections>
<connection userID="12345">Data Source=servA;Initial Catalog=db123;User ID=jSmith password=pass1</connection>
<connection userID="43532">Data Source=servB;Initial Catalog=db456;User ID=rJSmith password=abc321</connection>
</connections>
If all that is changing is the user, pass, catalog, and datasource values and the remainder of the connection string is static, you could just store these individual values as opposed to the entire connection string and then inject those dynamically to build the connection string at runtime.
Reading the XML is not difficult when using something like LINQ to XML which would allow you to query the XML file and get a specific connection string by the userID field. A good reference for LINQ to XML is at the following: http://msdn.microsoft.com/en-us/library/bb387098.aspx

Getting timeout errors with SqlTransaction on same table

public TransImport()
{
ConnString = ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;
SqlConnection conn_new;
SqlCommand command_serial_new;
SqlConnection conn;
SqlCommand command_serial;
SqlTransaction InsertUpdateSerialNumbers;
conn = new SqlConnection(ConnString);
command_serial = conn.CreateCommand();
conn_new = new SqlConnection(ConnString);
command_serial_new = conn_new.CreateCommand();
command_serial_new.CommandText = "SELECT 1 FROM YSL00 WHERE SERLNMBR = #slnr";
var p = new SqlParameter("#slnr", SqlDbType.NVarChar, 50);
command_serial_new.Parameters.Add(p);
//Here you will start reading flat file to get serialnumber.
InsertUpdateSerialNumbers = conn.BeginTransaction();
while (!headerFileReader.EndOfStream)
{
headerRow = headerFileReader.ReadLine();
if (CheckSerialNumber(headerFields[0].Trim()))
DisplayMessage("Good serialnumber"); //this function is not copied here.
}
InsertUpdateSerialNumbers.Commit();
}
private Boolean CheckSerialNumber(string SerialNumber)
{
command_serial_new.Parameters["#slnr"].Value = SerialNumber;
try
{
var itExists = Convert.ToInt32(command_serial_new.ExecuteScalar()) > 0;
if (!itExists)
{
command_serial.Transaction = InsertUpdateSerialNumbers;
command_serial.CommandText = "INSERT INTO YSL00([Manifest_Number],[PONUMBER],[ITEMNMBR],[SERLNMBR]"
+ "VALUES ('" + Manifest + "','" + PONr + "','" + itemNumber + "','" + serialNr + "')";
var insertStatus = command_serial.ExecuteNonQuery();
return true;
}
}
catch (Exception ex)
{
LogException(ex, "Error in CheckSerialNumber =>"+ command_serial_new.CommandText.ToString());
}
return false;
}
I get error "Timeout expired. The timeout period elapsed prior to completion of the operation or server is not responding".
The CheckSerialNumber function also does an insert to YSL00 (the same table where I had executescalar. See code above).
As I mentioned earlier there are 1000s of line in a flat file that I read and update YSL000 table.
Note that I have two separate sqlcommands and also two separate connections to handle this. Reason is with sqltransaction it doesn't let me to query on the same table. I think timeout may be happening because of this?
Thanks for reading. Please suggest
Update 1: Since I have not pasted entire code, I want to mention that dispose is done using below code in the program.
if (conn != null)
{
conn.Close();
conn.Dispose();
}
if (conn_new != null)
{
conn_new.Close();
conn_new.Dispose();
}
you can increase the time out of your SqlConnection object.
you can do this with your ConnString:
string connStr = "Data Source=(local);Initial Catalog=AdventureWorks;Integrated
Security=SSPI;Connection Timeout=300";
I think default isolation level - read commited - is preventing your 'CheckSerialNumber' method from being effective. Command_serial_new will not take into consideration rows inserted in your loop - this might lead to some troubles. To be honest I would also look for some deadlock. Perhaps command_serial_new is actually completely blocked by the other transaction.
To start off:
Set command_serial_new query as:
SELECT 1 FROM YSL00 WITH (NOLOCK) WHERE SERLNMBR = #slnr
Think about using lower isolation level to query inserted rows as well (set it to read uncommited).
Close your connections and transactions.
Use just one SqlConnection - you don't need two of them.
Many of the objects you are using implement IDisposable, and you should be wrapping them with using statements. Without these using statements, .NET won't necessarily get rid of your objects until an undetermined time when the garbage collector runs, and could block subsequent queries if it's still holding a transaction open somewhere.
So for example, you'll need to wrap your connections with using statements:
using (conn_new = new SqlConnection(ConnString)) {
...
If I am not mistaken you need to merge the file content with the table content.
For this purpose I would recommend you
Copy the file content in to a temporary table (see temporary tables and BulkInsert)
Use command MERGE (http://msdn.microsoft.com/en-us/library/bb510625.aspx) to merge the temporary table content with the original table

how to update data from table using C#.net

I've a form opened which is has loaded some sort of data (like username, CNIC, Contact no, etc etc) in Check boxes, now I want to update the data in such manner that I simply change the text in the text boxes and click on the save changes to save it. I've tried it but I am not able to do it in correct manner.
Let me show you how I've coded, the code I did in frmViewformList savechanges button is :
private void btnSaveChanges_Click(object sender, EventArgs e)
{
string sql;
string UserName;
UserName = txtUserName.Text; // saving data loaded on run time to UserName
sql = "";
sql += "UPDATE UserLogin";
sql += "SET Name = "+ //how to access data I've changed in TextBox after loading +"";
sql += "WHERE Name= " + //how to access data which was in text box right after loading + ""; //
}
I am a bit confused about how to refer to data, like the name already in the text box or the name which I have changed and how to write it in SQL query...
This question is a bit confusing, I know. Let me explain; the form is loaded, there are text boxes which is being populated with the data in database on load event, I change the data in text boxes and save on click so that the update query runs and changes the data in database as well.
I'm not able to create logic here how to do this, can any one help me out, I am sorry I am a new developer of C# that's why I am a bit confused.
You should use Sql Parameters in order to avoid SQL Injection which could leave your database vulnerable to malicious exploitation.
It's a good idea to separate the logic for performing the update to the logic where you create your query so you don't have to repeat code and so that you can maintain your code easier.
Here is an example you can reference:
public void DoWork()
{
// Build Query Use #Name Parameters instead of direct values to prevent SQL Injection
StringBuilder sql = new StringBuilder();
sql.Append("UPDATE UserLogin");
sql.Append("SET Name = #UpdatedName");
sql.Append("WHERE Name = #Name");
// Create parameters with the value you want to pass to SQL
SqlParameter name = new SqlParameter("#Name", "whatEverOldNameWas");
SqlParameter updatedName = new SqlParameter("#UpdatedName", txtUserName.Text);
Update(sql.ToString(), new [] { name, updatedName });
}
private static readonly string connectionString = "Your connection string"
private static readonly DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
public static int Update(string sql, SqlParameter[] parameters)
{
try
{
using (DbConnection connection = factory.CreateConnection())
{
connection.ConnectionString = connectionString;
using (DbCommand command = factory.CreateCommand())
{
command.Connection = connection;
command.CommandText = sql;
foreach (var parameter in parameters)
{
if (parameter != null)
command.Parameters.Add(parameter);
}
connection.Open();
return command.ExecuteNonQuery();
}
}
}
catch (Exception)
{
throw;
}
}
You will want to strip all ', ", and ` characters out of your input so that people can't inject SQL. When you do SET Name = " +, you'll want to actually wrap whatever you're including in quotes because it's a string: SET Name = '" + UserName "' " +...
This is probably best done using
string.Format("UPDATE UserLogin SET Name = '{0}' WHERE Name = '{1}'", UserName, FormerUserName);
Then you will execute your query by using System.Data.SqlClient; and then work with SqlConnection to establish a connection to the server, and execute a SqlCommand of some kind; take a look at: http://www.codeproject.com/Articles/4416/Beginners-guide-to-accessing-SQL-Server-through-C
The following is a code snippet to insert data into database using ADO.NET and assuming SQL Server database.
At the top of your .cs file you should have.
using System.Data.SqlClient; // for sql server for other data bases you should use OleClient instead.
And inside your button click event you could put the following.
// to know how to get the right connection string please check this site: http://www.connectionstrings.com
string connString = "database connection string here";
using (SqlConnection con = new SqlConnection(connString))
{
con.Open();
//insert text into db
string sql_insert = "INSERT INTO ....."; // Use parameters here.
SqlCommand cmd_insert = new SqlCommand(sql_insert, con);
int rowsAffected = cmd_insert.ExecuteNonQuery();
}
Hopefully this is enough to get you started.

Trouble opening SQL connection to insert textbox values

First off, I'd like to say that this is the most brilliant forum I've encountered in my programming journey, and I've been google-fishing for all the help I can get for the last three months. Great support, and even greater style #necromancer badge.
Enough with the flattery.
I'm doing a practice project, insurance website. Right now, I need to get user input from the textboxes into the database. I have seen plenty of related questions here, but I'm getting an error message on my connection I haven't found on any of the other posts, and I'm so ignorant it's difficult to apply examples that don't fit exactly what I'm doing. (As a side note, my trainer specifically wants the most basic form of this code, and as such told me not to worry about parameterizing the queries for security or to use a try-catch block for exceptions, but many thanks to the answers here for those pointers)
The error message I get is "Failed to generate a user instance of SQL Server due to a failure in starting the process for the user instance. The connection will be closed."
Am I getting my syntax wrong? Am I using the 'TextBox1.Text' value right? Am I just too stupid to be doing this?
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class SubmissionPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
String connectionString = #"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\aspnetdb.mdf;Integrated Security=True;User Instance=True";
SqlConnection sqlConn = new SqlConnection(connectionString);
sqlConn.Open();
String thisQuery = "INSERT INTO Customer (" + " Name, SIC_NAIC, Address, City, State, Zip, " + ") VALUES (" + " #TextBox1.Text, #RadioButtonList1.SelecedItem, #TextBox2.Text, #DropDownList1.SelectedItem, #TextBox3.Text" + ")";
SqlCommand thisCommand = new SqlCommand(thisQuery, sqlConn);
thisCommand.ExecuteNonQuery();
sqlConn.Close();
}
}
Check this and use sql parameters:
using (var conn = new SqlConnection("Data Source=.\SQLEXPRESS;Initial Catalog=aspnetdb;Integrated Security=True;Connect Timeout=30;User Instance=True"))
{
conn.Open();
using (var cmd = new SqlCommand("INSERT INTO Customer (Name,SIC_NAIC) VALUES (#Name,#SIC_NAIC)",conn))
{
try
{
cmd.Parameters.Add("#Name", SQlDbType.VarChar, 50).Value = TextBox1.Text;
cmd.Parameters.Add("#SIC_NAIC", SQlDbType.VarChar, 50).Value = RadioButtonList1.SelecedItem.ToString();
cmd.ExecuteNonQuery();
}
catch (Exception)
{
{
}
throw;
}
finally
{
if (conn.State == ConnectionState.Open) conn.Close();
}
}
Make sure you have downloaded sqlmanagement studio 2008 express.. and then attach asp.netdb on it and change your sql connectionstring.
sql ms
Regards
This error is often caused because the parent instance (for whatever reason) can't copy the system databases to the users local isolated storage folders. Sometimes it is because of a previous install of SQL Express has left files in that directory. Full story here
http://social.msdn.microsoft.com/Forums/en/sqldatabaseengine/thread/f5eb164d-9774-4864-ae05-cac99740949b
could be due to permission issue, check eventlog. you may try removing User Instance=True from connection string
In addition to answers regarding SQL database instances, your SQL query looks wrong. Try changing your embedded query to:
String thisQuery = "INSERT INTO Customer (Name, SIC_NAIC, Address, City, State, Zip) VALUES ('" + TextBox1.Text + "', '" + RadioButtonList1.SelecedItem.Text + "', '" + TextBox2.Text + "', '" + DropDownList1.SelectedItem.Text + "', '" + TextBox3.Text + "', '" + TextBoxZip.Text + "')";
This will format the SQL statement from the values on your form.
NOTE: Assuming field SIC_NAIC and State are storing text values and an additional field for Zip (TextBoxZip).
First of all, if your mdf resides at App_Data, then your connection string is all wrong.
Put the following in your web.config
<connectionStrings>
<add name="MyConnectionString" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\aspnetdb.mdf;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
Now call it like
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
Here is a fix for your error. But you should have an instance of SQL Express installed, which I supposecomes standard with Visual Studio.
http://www.aspdotnetfaq.com/Faq/fix-error-Failed-to-generate-a-user-instance-of-SQL-Server-due-to-a-failure-in-starting-the-process-for-the-user-instance.aspx
And while you are at it, please alter your Button_Click event like this
protected void Button1_Click(object sender, EventArgs e)
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
string sql = "INSERT INTO Customer (Name, SIC_NAIC, Address, City, State, Zip) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}')";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string query = String.Format(sql,
TextBox1.Text,
RadioButtonList1.SelectedItem.Text,
TextBox2.Text,
DropDownList1.SelectedItem.Text,
TextBox3.Text,
"000000");
using (SqlCommand command = new SqlCommand(query, connection))
{
command.ExecuteNonQuery();
}
}
}
Also keep in mind that you cannot insert RadioButtonList1.SelecedItem or DropDownList1.SelectedItem to database. You must append either .Text or .Value to it as per your requirement.
Ok guys, first of all I'm overwhelmed by the helpfulness I found here. I'll be a stackoverflow-er for life. Thank you all for taking your time and experience to aid a total stranger. I couldn't have gotten this done without it.
I wanted to post the code I ended up with, just so as to have a record of what actually worked for me in case someone with the same issue needs to see the end result. As per #yetanothercoder's recommendation, I placed this connection string in my webconfig file, and it looked like this (from ?xml version... to configuration is just to show where I placed the code, since I had wondered about that myself, the connection string is wrapped between the tags):
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<connectionStrings>
<add name="MyConnectionString" connectionString="Data Source=The-Crushinator\SQLEXPRESS;AttachDbFilename=|DataDirectory|\ArgonautSubmission.mdf;Integrated Security=True;User Instance=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
And, although my trainer assured me that the using block #yetanothercoder suggested should be fine, it wasn't working for me, so I used the example from #chaps' answer, remembering to put in the TextBox4.Text value forgot. The code looks like this:
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class SubmissionPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
String thisQuery = "INSERT INTO Customer (Name, SIC_NAIC, Address, City, State, Zip) VALUES ('" + TextBox1.Text + "', '" + RadioButtonList1.SelectedItem.Text + "', '" + TextBox2.Text + "', '" + TextBox3.Text + "', '" + DropDownList1.SelectedItem.Text + "', '" + TextBox4.Text + "')";
using (SqlConnection sqlConn = new SqlConnection(connectionString))
{
sqlConn.Open();
using (SqlCommand command = new SqlCommand(thisQuery, sqlConn))
{
command.ExecuteNonQuery();
}
}
}
}
Next came the more convoluted part. To rid myself of the dreaded "Failed to generate a user instance of SQL Server due to a failure in starting the process for the user instance. The connection will be closed" error message, I followed this link from #yetanother coder,
and found that I needed to install Sql Server Management Studio Express. I had to use Microsoft Web Platform Installer because when I tried to follow the instructions at msdn.com for downloading ssmse, I kept getting a cyclical error. I installed ssmse, opened up the query window with the 'new query' button, and executed this command
exec sp_configure 'user instances enabled', 1.
Go
Reconfigure
then I restarted sql server, added a new database to my asp.net project, and BAM! It worked! User info was saved into the database, where it was supposed to go! I had been so conditioned to expecting failure from my code that it felt like watching my first rocket make it into orbit. Awesome. Thanks again, everyone, and I hope this helps someone in a similar situation.

Categories

Resources