This question already has answers here:
How to connect to database from Unity
(3 answers)
Closed 4 years ago.
I am trying to build an android application from Unity to connect with a MS SQL server database. I have found some approaches that worked on editor, mostly based on directly connecting from Unity to SQL server. However, this approach does not work when I build the android app. I only need to perform "select" (to login) and "insert" (to insert session data). Can you guys give some tips on the best way to do it? In addition, I thought maybe using a web service could be necessary, but I haven't found many examples using ASP.NET as intermediary web server.
With this kind of set-up you would generally call the the unity app the "client", because it's the part of the system which the end-user uses to view and interact with the world.
Your client would never normally have direct access to the database. This would generally represent a significant security problem. What is more typical is that clients would talk to a dedicated multiuser server application using socket connections (for example, something like SmartFox server, or often - for MMOs - a custom written server app).
It's also the case with simpler database interactions - such as a server-side scoreboard - that your client would never access the database directly, for similar security reasons. Instead, your client (the game) would talk to specially written server-side scripts (eg, in PHP or ASP), which in turn would access the database themselves.
Therefore it's only ever server-side programs that are under your complete control which have authorisation to directly access the databases, and your clients (which are less trusted, because they're in your user's hands!) are restricted to making higher level requests via an API of your own design, which is restricted to relevant commands such as "SubmitScore", and "RetrieveScoreboard" in the case of scores, or things like "MoveToLocation", "TakeItem", "Say Message", etc for a multiplayer RPG.
This multiuser server would then deal with handling interactions in your world, and it would be responsible for interacting with the database behind the scenes to create, read, update and delete information from the DB such as user details and persistent world data.
For this reason, your Unity client need never know about the existence of such things as SQL, tables, records, etc :-) These are all things which should stay on your server, either as server scripts or within your multiuser server application.
public void connect(){
string connectionString =
"Server=servername;" +
"Database=dbname;" +
"User ID=userid;" +
"Password=pass;" +
"Integrated Security=True";
result = new List<float> ();
resultCas = new List<DateTime> ();
using(SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand c; SqlDataReader da; SqlParameter param1; SqlParameter param2; SqlParameter param3; SqlParameter param4;
conn.Open();
c = new SqlCommand();
c.Connection = conn;
c.CommandType = CommandType.StoredProcedure;
c.CommandText = "commandtext";
param1 = c.Parameters.Add("#identify",SqlDbType.Int);
param1.Value = 1;
param2 = c.Parameters.Add("#startTime",SqlDbType.DateTime);
param2.Value = "2010-11-10 07:45:00.000";
param3 = c.Parameters.Add("#endTime",SqlDbType.DateTime);
param3.Value = "2010-11-12 10:15:00.000";
param4 = c.Parameters.Add("#args",SqlDbType.NVarChar);
param4.Value = "I";
da = c.ExecuteReader();
while (da.Read())
{
resultCas.Add(da.GetDateTime(0));
result.Add((float)da.GetDouble(1));
}
}
}
Related
PRE
I'm using SQLExpress to store information.
I have a very basic user set-up in my app. I have a Login table that contains only three fields, these are Username, Password and Security Level. There are only three security levels; High, Medium and Low.
On my login form, I have the following code to save the user that is logging into a Settings.Default[], so I can read it later.
private void Loginbtn_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=.\SQLEXPRESS;Initial Catalog=MySupportHub;Integrated Security=True");
SqlDataAdapter sda = new SqlDataAdapter(#"SELECT * FROM [dbo].[Login] Where Username='" + usernameTextBox.Text + "' and Password='" + passwordTextBox.Text + "'", con);
DataTable dt = new DataTable();
sda.Fill(dt);
if (dt.Rows.Count == 1)
{
Settings.Default["CurrentUserName"] = usernameTextBox.Text;
Settings.Default.Save();
this.Hide();
Home home = new Home();
home.Show();
}
else
{
MessageBox.Show("Invalid Username and/or Password..!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Clearbtn_Click(sender, e);
}
}
Then on the Home page, I have the following code to get the value and display the current logged in user in the bottom status bar. On page load.
//Identify and display current logged in user
CurrentUser.Caption = Settings.Default["CurrentUserName"].ToString();
What I would like to be able to do now is check the current logged in users security level (User value would be stored here "CurrentUser.Caption" on the main form) and be able to disable features like creating a new user etc.
So I would need to be able to read the CurrentUser.Caption value, check that against the (SQL Express) [dbo].[Login] table and find the user's security level and limit features based on the result.
Any help would be appreciated.
Thanks
Phil
You are essentially implementing user authentication and authorization in WebForms.
What would happen in your scenario if a user types the following string for a username and password (without the quotes): "foo OR 1=1" ? You must never concatenate strings in database CRUID opearations to prevent SQL injections.
I could advise you to use FormsAuthentication and encrypted tickets containing user roles and not store passwords in plain text or even encrypted, but rather store hashes using PBKDF2 and cryptographic salts. But you might not be concerned about a production environment and this might be just a learning example. In this case you still don't want to practice what you will never use in a real world application, namely taking the value of a user control to match against permissions stored in a table. One can change the value at the client (browser)and trick the system.
Getting to grips with FormsAuthentication and using the User.Identity.IsInRole would be one way to proceed, you can check this on one of Microsoft Docs tutorials: https://support.microsoft.com/en-us/help/301240/how-to-implement-forms-based-authentication-in-your-asp.net-applicatio
I assume you would like to gain industry valid skills for implementing authentication and authorization in a asp.net web environment. If this is the objective behind your question, it would likely take you the time to learn all of this for the same time it would take you to learn asp.net Identity, which handles many things for you.
In either scenarios it will take you a long time to develop your app without using the Entity Framework and unless you use Identity you have to go a long way to implement your own security features, especially of high quality. Trust me I have been there, done that.
There will be a learning curve, but the best way forward is to use EF to save you tons of time, also ASP.NET Identity for nowadays relevant skills and also to save you tons of time. It's worth comparing Web Forms and MVC too, MVC is a better choice for a dynamic full featured modern application.
I wish there was a simpler answer to your question, but solving this problem the way you intend to will not help you at all in the future and it will not be used in a real application.
The answer is subjective, but in my opinion you can make a fresh start with MVC, EF and Identity, it will take you the same time to learn the right way to do what you intend to do now, but it will give you skills you can use in the future in a production environment and also concepts not only limited to ASP.NET. Take the learning curve.
Are you looking for
SELECT CURRENT_USER;
or
SELECT SUSER_NAME();
and check with your table records?
I was thinking about login trough SQL
I created basic login form as shown below
protected void btnLogin_Click(object sender, EventArgs e)
{
string cs="Data Source=Dev-PC;Initial Catalog=CodeSolution;User ID=sa; password=12345678"
SqlConnection con = new SqlConnection(cs);
con.Open();
String sqlCommand = "SELECT * FROM tblLogin WHERE "+
"username = #user AND password = #pwd"
SqlCommand cmd = new SqlCommand(sqlCommand, con);
cmd.Parameters.AddWithValue("#user", TextBox1.Text);
cmd.Parameters.AddWithValue("#pwd", TextBox2.Text);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
Response.Redirect("Details.aspx");
}
else
{
Response.Write("<script>alert('Please enter valid Username and Password')</script>");
}
}
But then this got me thinking because without login people might guess the url of Details.aspx
So then i wondered what if the login was successful, to also add a column to the tblLogin and there store the sessionID of that user.
Then pages should only check if the sessionID equals that of tblLogin for that user.
I'm wondering 2 things here about this:
How to store the session id into tblLogin as is known by the IIS server
Is this a poormansolution, or would it be acceptable (with the drawback that each asp page should start with a function to check against valid session id).
One error removed, the LIKE in sqlCommand its now =
You appear to be doing so many things wrong, that writing a proper answer would automatically make this question too broad. I'll give it a try:
Don't hardcode connection strings. Store them in your configuration file and reference them by name.
Don't use your sa account ever, not even for development.
Don't prefix tables with tbl, it's redundant.
Don't use like for usernames and passwords, you want an exact match.
Don't store passwords in plaintext.
"people might guess the url of Details.aspx" - you check authorization ("is this user allowed to perform this action") on every action.
Explaining how to fix all these issues will require too much effort. Don't reinvent the wheel called authorization and authentication. Use a secure, tested, supported library for this.
You may want to look into ASP.NET Membership or its successor ASP.NET Identity.
Not exact answer to your question, but this will be a good guide/advice.
Your idea of having a session ID is not bad at all (depending on requirement of your site/system). But in this case not the session from the IIS because you need to decrypt it which will make you vulnerable to in-line attack (makes your DB/root folder accessible).
Here's the example on how to get session id of the session during the login but not from IIS (you can use this for logging):
string sessionId = System.Web.HttpContext.Current.Session.SessionID;
And as per Database Design, better to have separate table for logging sessions of the user.
I'm trying to optimize my code performance accessing SQL Server 2014 database and noticed that connection pooling doesn't seem to work as advertised by ADO.NET. By default it's supposed to be enabled and working out of the box (as long as same connection string is used). My experiments are showing however that opening/closing connection on SqlConnection does in fact cause Audit Login / Logout to be raised.
As per https://msdn.microsoft.com/en-us/library/vstudio/8xx3tyca(v=vs.100).aspx this should not be the case:
Login and logout events will not be raised on the server when a connection is fetched from or returned to the connection pool. This is because the connection is not actually closed when it is returned to the connection pool.
My simple test was to create a console app, similar to the following:
var conn = new SqlConnection("Data Source=(local);Integrated Security=SSPI;Initial Catalog=icedb;");
for (int i = 0; i < 3; i++)
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = "select 1";
cmd.ExecuteScalar();
conn.Close();
}
Running this code yeilds results captured by Sql Profiler similar to the following. Observer the multiople login/logout events which should have not been captured if pooling was working as advertised. I've been able to reproduce this on multiple machines (Windows 8/10, .NET 4.5, Sql Server 2014 Developer Edition).
Main question: How do I get connection pooling to work.
If sp_reset_connection is issued then connection pooling is enabled. See What does sp_reset_connection do?
To see if an Audit Login is pooled or non-pooled in the Profiler, you need to add EventSubClass column to the Trace and check the Audit Login and Audit Logout events. The new column will show either "1 - Nonpooled" or "2 - Pooled".
In your example only the first connection has "1 - Nonpooled", the next 2 Audit Logins have EventSubClass = "2 - Pooled".
I have an asp.net web page which interacts with a SQL Server database, grabs some data and then returns an XML response (which I feed into Freeswitch using xml_curl).
Because Freeswitch (FS from now on) does not store cookies, each request creates a new session.
When the number of requests gets too much (about 97 to 100), the SqlConnection.Open() method gets timeout from the SQL Server Instance, which then results in HTTP Error 500.
To test my assumption, I have created a small script using PHP and cURL, which make repeated requests to my asp.net page. If I store cookies (and thus sessions) in the PHP script I can make 10000 successful requests in almost 314 seconds.
But without sessions, I get stuck at about 97~100 requests, and then I get HTTP Error 500.
Is there anyway to overcome this problem?
==Edit==
Here is how I interact with the database:
String connectionString = WebConfigurationManager.ConnectionStrings["SqlServerConnection"].ConnectionString;
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
command.CommandText = "Select * from dbo.Template where Name = '" + name + "'";
Template template = new Template();
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
template.Name = reader["Name"].ToString();
template.XMLContent = reader["XMLContent"].ToString();
}
else
{
template.Name = "";
template.XMLContent = "";
}
reader.Close();
connection.Close();
return template;
And the Template table has these fields:
ID => int, identity, primary key
Name => nvarchar(255), unique
XMLContent => ntext
It appears you are using a connection pool. By default these pools have a max of 100 connections to your SQL server and queue any additional connections. The queue has a timeout (default 15 seconds) which can be extended if you wish to queue your requests longer. This means that you might get backed up on your server. You can also increase the pool max size if your SQL server can handle it.
Here is how you increase your connection settings by adding these parameters:
Timeout=60
Max Pool Size=150
etc etc
Some steps to impove this code.
If you do not need session, disabled it for this page so not cookie is going to be made.
Use some cache here base on the name If the request for name is the same, get it from cache and not open the database
Use a static variable to read only one time the connection string.
Place it on [try catch | using] , to be sure that you close the connection in case of failure
Maybe you can try a mutex lock logic, to avoid too many request together.
Use parameters on your sql call.
In addition to #Aristos suggestions:
Use Async-Pages!
Example and "Benchmark"
Some time ago I asked nearly the same question here on so
I have some problems in database connection and wonder if I have something wrong in my code. Please review. This question is related: Switch between databases, use two databases simultaneously question.
cs="Data Source=mywebsite.com;Initial Catalog=database;User Id=root;Password=toor;Connect Timeout=10;Pooling='true';"
using (SqlConnection cnn = new SqlConnection(WebConfigurationManager.ConnectionStrings["cs"].ConnectionString))
{
using (SqlCommand cmmnd = new SqlCommand("", cnn))
{
try
{
cnn.Open();
#region Header & Description
cmmnd.Parameters.Add("#CatID", SqlDbType.Int).Value = catId;
cmmnd.CommandText = "SELECT UpperID, Title, Description FROM Categories WHERE CatID=#CatID;";
string mainCat = String.Empty, rootCat = String.Empty;
using (SqlDataReader rdr = cmmnd.ExecuteReader())
{
if (rdr.Read())
{
mainCat = rdr["Title"].ToString();
upperId = Convert.ToInt32(rdr["UpperID"]);
description = rdr["Title"];
}
else { Response.Redirect("/", false); }
}
if (upperId > 0) //If upper category exists add its name
{
cmmnd.Parameters["#CatID"].Value = upperId;
cmmnd.CommandText = "SELECT Title FROM Categories WHERE CatID=#CatID;";
using (SqlDataReader rdr = cmmnd.ExecuteReader())
{
if (rdr.Read())
{
rootCat = "<a href='x.aspx'>" + rdr["Title"] + "</a> ยป ";
}
}
}
#endregion
#region Sub-Categories
if (upperId == 0) //show only at root categories
{
cmmnd.Parameters["#CatID"].Value = catId;
cmmnd.CommandText = "SELECT Count(CatID) FROM Categories WHERE UpperID=#CatID;";
if (Convert.ToInt32(cmmnd.ExecuteScalar()) > 0)
{
cmmnd.CommandText = "SELECT CatID, Title FROM Categories WHERE UpperID=#CatID ORDER BY Title;";
using (SqlDataReader rdr = cmmnd.ExecuteReader())
{
while (rdr.Read())
{
subcat.InnerHtml += "<a href='x.aspx'>" + rdr["Title"].ToString().ToLower() + "</a>\n";
description += rdr["Title"] + ", ";
}
}
}
}
#endregion
}
catch (Exception ex) { HasanG.LogException(ex, Request.RawUrl, HttpContext.Current); Response.Redirect("/", false); }
finally { cnn.Close(); }
}
}
The random errors I'm receiving are:
A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Cannot open database "db" requested by the login. The login failed. Login failed for user 'root'.
There's no real issues here.
You don't need the extraneous finally { cnn.close(); } as the using clause will take care of that for you. However changing it will have exactly zero impact.
Another thing is that I would put the try .. catch outside of the using clause with a redirect. But, again, I don't think that would affect the dispose from being called.
It's interesting that you would get connection pool errors (timeout expired) if you are always properly disposing of your connections, as you've shown.
Which leaves us with only one real solution: switch hosting providers. They have either overloaded their DB server to the point of unusability or some hardware element in their network setup (nic, switch, router, etc) is bad and dropping packets.
There are couple of inconsistencies which need to fixed:
description = rdr["Title"]; no proper casting defined.
Same command object is used for each sql statement and even you are not clearing parameters, it would be ideal if a separate command should be used for each sql statement.
Too many redirections as well, it is best to handle redirection at the end of method.
Check the database server health as well, it looks like database server is not responsive enough.
Hope it will help.
If you're connecting remotely to a database provider, you need to look at several possibilities like your own network configuration, firewall setup, etc.
Use a packet sniffer to figure out if lost packets are the issue.
Connection pooling is setup on your local machine, the server making the connections. If the database provider only allows for 5 connections and your connection pool is setup for 50 connections, well... you can do the math. It looks like you're closing the connections properly, so no issues there.
True... one error on "description = rdr["Title"];", should that be "description = rdr["Description"].ToString()"?
No need to put a using statement around the SqlCommand object and since you're using ad-hoc queries, just use string.Format("sql test {0}", param). This way, you can reuse the SqlCommand object without having to clear the parameters.
The biggest issue I see here is that you've mixed the presentation layer with the business layer with the datasource layer. Dump the try...catch and allow the business layer to handle logging stuff. Return an object to the presentation layer and allow it to perform the redirects. Keep the datasource layer very simple... get the data and return an entity. The business layer can handle any business logic on the entity itself.
SQL Server not found could be your fault or the providers... if the provider is at fault often, change providers.
Are you sure that the DB is configured to grant remote access using TCP?