I am using Azure function to connect to Sql database and retrieve values. I am able to install the necessary Nuget packages and perform Sql connection and querying in the Run() function
I wish to keep this database access function alone in a separate file, and return value to the main function(Run() in this case). But when i create a new class in the Azure function project and write SQL connection code, no "using" statements can be used or installed packages can be used.
I am new to Azure functions and may be i am wrong in this approach. Can you help me out? Thanks.
.
You just need to wrap your code in a method. Here's a sample:
public class SqlQuery
{
private string cs = Environment.GetEnvironmentVariable("SqlConnectionString");
public void Query()
{
using(var conn = new SqlConnection(cs))
{
conn.Open();
var cmd = conn.CreateCommand("SELECT * FROM Table");
var dr = cmd.ExecuteReader();
if(dr == null) return;
while(dr.Read())
{
}
}
}
}
the screenshot you shared contains a class and has started implementation inside the class, whereas you should be creating at least one method inside your class and then add your code into the method which returns the data you expect.
try that it should work!
public class SqlQuery{
public void ExecuteReader()
{
private string cs =Environment.GetEnvironmentVariable("SqlConnectionString");
using(var conn = new SqlConnection(cs))
{
conn.Open();
var cmd = conn.CreateCommand("SELECT * FROM Table");
var dr = cmd.ExecuteReader();
if(dr == null) return;
while(dr.Read())
{
}
}
}}
Related
I tried to create a CLR stored procedure in VS2017 but encountering error "NOT Connected." while executing that stored procedure.
I need to connect to other database server to grab some data. Therefore I cannot use context=true in SqlConnection.
Stored procedure will be created in serverA
This stored procedure will query data from serverB
Data will be stored back to serverA.
Is there anything I need to do in order to have regular connection in CLR stored procedure?
Please advise. Thanks!
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void udp_CLR_GetData()
{
string ConnStr = "server=MyServer; database=MyDB; user id=accabc; password=abc123";
string sql = " select top 1 ID from [dbo].Table1 ";
SqlDataReader dr = null;
DataTable dt = new DataTable();
try
{
using (SqlConnection fcon = new SqlConnection(ConnStr))
{
if (fcon.State == ConnectionState.Open)
{
SqlContext.Pipe.Send("Connected.");
using (SqlCommand fcmd = new SqlCommand(sql, fcon))
{
SqlContext.Pipe.Send("Before executing reader...");
dr = fcmd.ExecuteReader();
SqlContext.Pipe.Send("After executing reader...");
SqlContext.Pipe.Send("Before send...");
SqlContext.Pipe.Send(dr);
SqlContext.Pipe.Send("After send...");
}
}
else
{
SqlContext.Pipe.Send("NOT Connected.");
}
}
}
catch(Exception ex)
{
SqlContext.Pipe.Send("Exception error (udp_CLR_GetData): " + ex.Message);
}
finally
{
if(dr != null && !dr.IsClosed)
{
dr.Close();
}
}
}
}
Creating a new instance of a SqlConnection in:
using (SqlConnection fcon = new SqlConnection(ConnStr))
does not create it in an "open" state. You need to actually open it for it to be "open". So, I would remove the if (fcon.State == ConnectionState.Open) and the associated else part of it. I would also remove the SqlContext.Pipe.Send("Connected."); line.
Then, just before the dr = fcmd.ExecuteReader(); line, add a line for:
fcon.Open();
This way you open the connection and immediately execute the command. No need to open the connection only to do other work getting the command ready.
For more info on working with SQLCLR in general, please visit: SQLCLR Info
Try defining the data source in the connection string instead of server
string ConnStr = "DataSource=MyServer;Initial Catalog=MyDB;User Id=accabc;Password=abc123";
other than that, make sure clr is enabled on the server:
https://learn.microsoft.com/en-us/sql/relational-databases/clr-integration/clr-integration-enabling?view=sql-server-ver15
I have a problem. I want to get data from my SQL Server database. When the code is running, the first row isn't getting added to my arraylist. All other rows get added successfully. In SQL Server, the query works fine, but in VS, it doesn't work.
private void button1_Click(object sender, EventArgs e)
{
SqlConnection baglanti = new SqlConnection("server=.; Initial Catalog=TripData;Integrated Security=SSPI");
baglanti.Open();
SqlCommand komut = new SqlCommand();
komut.CommandText = "select top 50 trip_ID from Sayfa1$";
komut.Connection = baglanti;
komut.ExecuteNonQuery();
SqlDataReader oku = komut.ExecuteReader();
while (oku.Read())
{
foreach (var item in oku)
{
gecici.Add(oku["trip_ID"].ToString());
}
}
}
You're trying to iterate over the reader in two different ways - using both a foreach loop and using while (reader.Read()). You should do one or the other - I've personally seen more code using while (reader.Read()) than the foreach approach.
Additionally I'd suggest using using statements for the disposable objects in your code - and not calling ExecuteNonQuery first. (It's not clear why you're doing that at all). So you could write:
// Note that this looks like a mixture of UI and non-UI code; consider separating
// them for greater code reuse, separation of concerns etc.
private void button1_Click(object sender, EventArgs e)
{
// Declare connectionString elsewhere, or have a common method to open the connection.
// You're likely to need that in multiple places.
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand("select top 50 trip_ID from Sayfa1$", connection))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
gecici.Add(oku["trip_ID"].ToString());
}
}
}
}
}
(Also if you're really using ArrayList as your post suggests, I'd definitely urge you to start using the generic collections, e.g. List<string> in this case.)
I am attempting to get the information of user whenever user logged in to the website, it success when I used a DataSet, but if I want to use the SqlDataReader, the error says: Invalid attempt to read when reader is closed. I have search why is it like that and I have found an article says that
SqlDataReader requires connection remains open in order to get the
data from the server, while DataSet does not need requires
connection remains open.
My question is: I want to know how can I use SqlDataReader as well? So that I don't have to depends on DataSet all the times when I want to get the data from the database.
My problem is occurs when I am trying to change the structure of reading the data function using SqlDataReader, so that it can be re-usable anytime.
Here is the code:
DatabaseManager class:
public SqlDataReader GetInformationDataReader(string procName, SqlParameter[] parameters)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
reader = cmd.ExecuteReader();
}
}
return reader;
}
Web Manager class:
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("#Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
reader = DatabaseManager.Instance.GetInformationDataReader("GetInformation", parameters);
while(reader.Read())
{
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
}
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
Controller:
public ActionResult MainMenu(ModelContexts.InformationContext context, string firstName, string lastName, string username, string email)
{
context = WebManager.Instance.GetInformation(User.Identity.Name);
firstName = context.FirstName;
lastName = context.LastName;
username = User.Identity.Name;
email = context.Email;
return View(context);
}
Model contains string return value with getter and setter (FirstName, LastName and Email).
View contains the html label and encode for FirstName, LastName and Email from the Model.
Appreciate your answer.
Thanks.
Here is an approach you can use to keep the code pretty clean that allows you to read from the SqlDataReader while the connection is still open. It takes advantage of passing delegates. Hopefully the code is understandable. You can adjust it to fit your specific needs, but hopefully it illustrates another option at your disposal.
public void GetInformationDataReader(string procName, SqlParameter[] parameters, Action<SqlDataReader> processRow)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
using (SqlDataReader dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
// call delegate here.
processRow(dataReader);
}
}
}
}
return reader;
}
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("#Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
// Instead of returning a reader, pass in a delegate that will perform the work
// on the data reader at the right time, and while the connection is still open.
DatabaseManager.Instance.GetInformationDataReader(
"GetInformation",
parameters,
reader => {
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
});
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
Brief explanation:
You'll notice that the overall structure of the code is very similar to what you already have. The only changes are:
Instead of returning a SqlDataReader, the GetInformationDataReader() method accepts an Action<SqlDataReader> delegate.
Within the GetInformationDataReader() method, the delegate is invoked at the correct time, while the connection is still open.
The call to GetInformationDataReader() is modified to pass in a block of code as a delegate.
This sort of pattern can be useful for exactly these cases. It makes the code reusable, it keeps it pretty clean and separate, and it doesn't prevent you from benefiting from the using construct to avoid resource/connection leaks.
You have wrapped your SqlConnection object in a using clause, therefore at the end of it SqlConnect.Dispose is called, closing the connection. Whatever caller is consuming your SqlDataReader no longer has the open connection, therefore you're getting your error.
while DataSet does not need requires connection remains open.
That is not entirely correct. DataSet is just an object that is typically filled when called by SqlDataAdapter (the Fill() method of that class). The SqlDataAdapter handles the opening and closing of the SqlConnection, which is most likely why that comment states that. But it's a different class that handles that, not the DataSet itself. Think of the DataSet as just the object that holds the result set of the SqlCommand.
To answer your comment...
So, shouldn't I use using keyword for this matter? In all of the Sql keyword?
I wouldn't take that approach either. You could have a connection leak bug quite easily with that model, and running out of pooled connections could be a not-so-fun thing to troubleshoot.
Typically it's best to consume your data and then close/dispose your connection. There's a saying, "open late, close early". That's typically how you'd want to approach this. I wouldn't try to pass a SqlDataReader object between class methods for this very issue that you're dealing with. The workaround (leaving the connection open) is very error prone.
Another though process, going back to something we mentioned, don't use the SqlDataReader. You have no benefit to cyclically loop through reading each row. Depending on your result set, just fill a DataSet (or usually more appropriate, a DataTable) and return either that Data[Set | Table] or, even better, an object that is more representative of the data it pertains to.
I've written a function to perform MySQL statements. In this function I give in a statement and get back the MySqlDataReader, but the problem is my function do not close the connection. After a short while of using the Programm, it crashs because the new connection can't be open. This is the error i got by trying open the new connection:
error connecting: 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.
My code look like this:
MySQL Class:
class mySql
{
string cs = "server=123.123.123.123;" +
"uid=abcabc;" +
"pwd=123456;" +
"database=overflow_test;";
private MySqlConnection conn_f() // create a Connection
{
MySql.Data.MySqlClient.MySqlConnection conn;
conn = new MySql.Data.MySqlClient.MySqlConnection();
conn.ConnectionString = cs;
try
{
conn.Open();
return conn;
}
catch
{
return null;
}
}
public MySqlDataReader CMD_f(string comand) //execute SQL Command
{
MySql.Data.MySqlClient.MySqlCommand cmd;
cmd = new MySql.Data.MySqlClient.MySqlCommand();
MySqlConnection conn = conn_f();
cmd.Connection = conn;
cmd.CommandText = comand;
cmd.Prepare();
rdr = cmd.ExecuteReader();
return rdr;
}
}
and an example how i use it Main Class
class main{
mySql DB = new mySql();
public void main(){
MySqlDataReader rdr = DB.CMD_f("SELECT * FROM tbl_kategorie");
int i = 0;
while (rdr.Read())
{
string str = rdr.GetString(1);
Console.WriteLine(str);
}
}
Has someone an Idea to solve the Problem.
Sincere regards LFS96 (Fabian Harmsen)
The main problem is the inbalance in the code. The CMD_f method creates a connection, but doesn't take responsibility for it. You should rather make the method that creates the connection public, so that you can take responsibility for it in the code that can close it.
That's a bigger change, so first let's look at a smaller change to fix the code.
The data reader should expose the data connection as the Connection property. I don't see that in the documentation, so it's possible that it doesn't, but the data readers in the .NET framework does.
If the property is exposed, then you can make a quick fix to make the code work with minimal change:
MySqlDataReader rdr = DB.CMD_f("SELECT * FROM tbl_kategorie");
int i = 0;
while (rdr.Read())
{
string str = rdr.GetString(1);
Console.WriteLine(str);
}
rdr.Close();
rdr.Connection.Close();
If you make the conn_f method public and don't call it in the CMD_f method, you can write much more robust code, that never leaves a connection or data reader hanging even if there is an error. That's a bigger change in the code, but definitely worth the effort when you have time to implement and test it. Using a using block to make sure that the objects are always disposed correctly makes the code much more resilient:
using (MySqlConnection conn = DB.conn_f()) {
using (MySqlDataReader rdr = DB.CMD_f(conn, "SELECT * FROM tbl_kategorie")) {
int i = 0;
while (rdr.Read()) {
string str = rdr.GetString(1);
Console.WriteLine(str);
}
}
}
Side note: Identifiers in C# tend to be descriptive. I suggest names like DB.CreateConnection and DB.ExecuteReader rather than DB.conn_f and DB.CMD_f.
i work with N tiers tec in C# for ado, trying to make it easy to use and capable to change any database kind with out write all the cod all over again ,
my code here doesn't get any error but it doesn't get any values to my textbox
(i am trying to get data from table to many textboxs to update it later)
and here how code works:{
at first i make some functions to take any set any kind of parameters or set any command and then i make other function to to execute what ever i set or get from database all that Function i build it in folder name (Data Access Layer)
then i made other folder (Data Build layer)to take use all those function for what ever i want to do in any page (insert , update , delete , Select),
the last think i do it to call the function i made at at (Data Build layer) to my page or control ,
i do all that because if i Change the database Type ,i change only one class and other classes still the same
i hope i explain enough (sorry for my English not good enough)}
Code :
Class DataAccessLayer
public static void Setcommand (SqlCommand cmd,CommandType type,string commandtext)
{
cmd.CommandType=type;
cmd.CommandText=commandtext;
}
public static void AddSQLparameter(SqlCommand cmd, int size,SqlDbType type,object value,string paramName,ParameterDirection direction)
{
if (cmd == null)
{
throw (new ArgumentException("cmd"));
}
if (paramName == null)
{
throw (new ArgumentException("paramName"));
}
SqlParameter param=new SqlParameter();
param.ParameterName= paramName;
param.SqlDbType=type;
param.Size=size;
param.Value=value;
param.Direction=direction;
cmd.Parameters.Add(param);
}
public static SqlDataReader ExecuteSelectCommand(SqlCommand cmd)
{
if (cmd == null)
{
throw (new ArgumentNullException("cmd"));
}
SqlConnection con = new SqlConnection();
cmd.Connection = con;
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
con.Close();
return dr ;
}
Class DatabuildLayer
SqlCommand com;
public DatabuildLayer()
{
com = new SqlCommand();
//
// TODO: Add constructor logic here
//
}
public SqlDataReader SelectCatalog(int catid)
{
DataAccessLayer.Setcommand(com, CommandType.Text, "select catname,catdescription,photo from category where catid=#catid" );
DataAccessLayer.addSQLparameter(com,16,SqlDbType.Int,catid,"#catid",ParameterDirection.Input);
return DataAccessLayer.ExecuteSelectCommand(com);;
}
and here my last code that retrieve my data to some textbox
in my Pageload :
protected void Page_Load(object sender, EventArgs e)
{
DatabuildLayer= new DatabuildLayer();
SqlDataReader dr ;
dr = obj.SelectCatalog(catselectddl.SelectedIndex);
if (dr.Read())
{
catnametxt.Text = dr["catname"].ToString();
catdestxt.Text = dr["catdescription"].ToString();
}
}
Is it possible that the query is returning nothing, and dr.Read() is returning false? Assuming the code actually executes (it is hard to tell from here) that is probably the only thing that would stop it working - either that or empty columns.
For what it is worth I think that your code needs to be tidied up a bit from a structural and conventions point of view. You should probably look through your code and consider the naming guidelines for the .NET framework. When others read your code they will want it formatted and consistent with this documentation. http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Further, most people doing ASP.NET these days try to look for some way to inject external dependencies (such as databases) into their code using a framework like WebFormsMVP available at
http://webformsmvp.com/ in conjunction with an IoC container like autofac available at http://code.google.com/p/autofac/.
Using this approach you can push all external dependencies out of your application behind interfaces which would make it fairly trivial to plug in a different database engine.
Your current wrapper code is not doing anything particularly useful (just subsituting the existing methods or your own tht do the same thing), and it is not closing the connections correctly. It is... a bit of a mess.
If you aren't already massively familiar with the raw ADO.NET interfaces, then maybe consider something like "dapper" which will do all this for you, with a sane API:
short catid = 16;
using(var conn = GetOpenConnection()) {
var row = conn.Query(
"select catname,catdescription,photo from category where catid=#catid",
new { catid }).FirstOrDefault();
if(row != null) {
string name = row.catname, desc = row.catdescription;
// ...
}
}
Or if you have a class with CatName / CatDescription properties:
var obj = conn.Query<Catalogue>(
"select catname,catdescription,photo from category where catid=#catid",
new { catid }).FirstOrDefault();
from my experience, when you close a connection associated with a DataReader, nothing can be retrieved from the reader anymore.
//You closed the connection before returning the dr in the your method below:
public static SqlDataReader ExecuteSelectCommand(SqlCommand cmd)
{
if (cmd == null)
{
throw (new ArgumentNullException("cmd"));
}
SqlConnection con = new SqlConnection();
cmd.Connection = con;
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
con.Close(); //here your connection was already closed
return dr ; //this dr is disconnected
}