I am relatively new to c# and have only been a developer for a couple years. I followed this really great tutorial on how to make a library with data models and interfaces to handle retrieving and inserting data into my database. I am working on a Blazor server side application. I utilize my data access library by injecting its interface onto the razor page via a service created in the startup.cs. All of this works fantastically.
However, Now I am making class that reads delimited files and inserts the data into the database table. I would like to use the same interfaces in my data access library that I use to connect my razor pages to the back end.
Here is my class where I want to use the InsertPermit method from IpermitData to insert a record to my database table but _db is null and will give me a null reference exception when I run it. How can I get a handle on this interface so I can use it to insert data?
{
public class TextFileParser
{
public TextFileParser()
{
}
public void InsertTextFileData(string filePath)
{
string errMessage = string.Empty;
FileInfo file = new FileInfo(filePath);
if (!File.Exists(file.FullName))
{
errMessage = "File not found!";
}
else if (file.Length >= 0)
{
errMessage = "File has no data!";
}
IPermitData _db;
PermitModel permitRecord = new PermitModel();
string vText = string.Empty;
string[] vString;
string delimiter = "\t";
StreamReader fileReader = new StreamReader(filePath);
List<PermitModel> dt = new List<PermitModel>();
//dt.Add("Parcel Number");
//dt.Columns.Add("Permit ID");
//dt.Columns.Add("Construction Loc");
//dt.Columns.Add("Submission Date");
//dt.Columns.Add("Issue Date");
//dt.Columns.Add("Permit Type");
//dt.Columns.Add("Const. Addr.");
//dt.Columns.Add("EST Cost");
//dt.Columns.Add("Referrer");
//dt.Columns.Add("Comments");
//dt.Columns.Add("Status");
int header = 1;
while (!fileReader.EndOfStream)
{
if (header == 1)
{
header += 1;
vText = fileReader.ReadLine();
vString = vText.Split(delimiter, StringSplitOptions.None);
continue;
}
vText = fileReader.ReadLine();
vString = vText.Split(delimiter, StringSplitOptions.None);
permitRecord.PD_ParcelID = vString[0];
permitRecord.PD_Situs1 = vString[1];
permitRecord.PD_Owner = vString[2];
permitRecord.PD_Addr1 = vString[3];
permitRecord.PD_Addr2 = vString[4];
permitRecord.ADDR_3 = vString[5];
permitRecord.PD_City = vString[6];
permitRecord.PD_State = vString[7];
permitRecord.PD_Zip = vString[8];
permitRecord.Type_Construction = vString[9];
permitRecord.Estimated_Cost = vString[10];
permitRecord.Permit_Issue_Date = vString[11];
permitRecord.Permit_Type = vString[12];
permitRecord.Property_Type = vString[13];
permitRecord.Permit_NO = vString[14];
permitRecord.Completion_Date = vString[15];
permitRecord.Percent_Complete = vString[16];
permitRecord.Modified_Date = vString[17];
permitRecord.Note = vString[18];
permitRecord.Note_Date = vString[19];
permitRecord.Submission_Date = vString[20];
permitRecord.Submitter = vString[21];
permitRecord.Submitter_Phone = vString[22];
permitRecord.Submitter_Email = vString[23];
permitRecord.Land_AV = vString[24];
permitRecord.Impr_AV = vString[25];
permitRecord.Sec_Location = vString[26];
try
{
_db.InsertPermit(permitRecord);
}
catch (Exception)
{
// dt.Add(vString[0], vString[14], vString[1], vString[20], vString[11], vString[12], vString[26], vString[10], vString[21], vString[18], "Failed " + ex.Message);
//dt.Add(permitRecord);
continue;
}
//dt.Rows.Add(vString[0], vString[14], vString[1], vString[20], vString[11], vString[12], vString[26], vString[10], vString[21], vString[18], "Passed");
}
}
}
}
Here are the code files from my dataaccesslibrary.
public interface IPermitData
{
Task<List<PermitModel>> GetPermitData();
Task InsertPermit(PermitModel permit);
Task DeletePermit(PermitModel permit);
}
public class PermitData : IPermitData
{
private readonly ISqlDataAccess _db;
public PermitData(ISqlDataAccess db)
{
_db = db;
}
public Task<List<PermitModel>> GetPermitData()
{
string sql = "select * from dbo.I_Permit";
return _db.LoadData<PermitModel, dynamic>(sql, new { });
}
public Task InsertPermit(PermitModel permit)
{
string sql = #"insert into dbo.I_Permit ( Parcel, Location, Owner_Name, ADDR_1, ADDR_2, ADDR_3, City, State_Sh, Zipcode, Type_Construction, Estimated_Cost, Permit_Issue_Date, Permit_Type, Property_Type, Permit_NO, Completion_Date, Percent_Complete, Modified_Date, Note, Note_Date, Submission_Date, Submitter, Submitter_Phone, Submitter_Email, Land_AV, Impr_AV, Sec_Location, PD_Owner, PD_Name2, PD_Addr1, PD_Addr2, PD_City, PD_State, PD_Zip, PD_ControlNo, PD_ParcelID, PD_Situs1, PD_Situs2, PD_S_City, PD_S_State, PS_S_Zip )
values ( #Parcel, #Location, #Owner_Name, #ADDR_1, #ADDR_2, #ADDR_3, #City, #State_Sh, #Zipcode, #Type_Construction, #Estimated_Cost, #Permit_Issue_Date, #Permit_Type, #Property_Type, #Permit_NO, #Completion_Date, #Percent_Complete, #Modified_Date, #Note, #Note_Date, #Submission_Date, #Submitter, #Submitter_Phone, #Submitter_Email, #Land_AV, #Impr_AV, #Sec_Location, #PD_Owner, #PD_Name2, #PD_Addr1, #PD_Addr2, #PD_City, #PD_State, #PD_Zip, #PD_ControlNo, #PD_ParcelID, #PD_Situs1, #PD_Situs2, #PD_S_City, #PD_S_State, #PS_S_Zip )";
return _db.SaveData(sql, permit);
}
public Task DeletePermit(PermitModel permit)
{
string sql = #"DELETE FROM dbo.I_Permit WHERE Record_ID =' " + permit.Record_ID + " ';";
return _db.DeleteData(sql, permit);
}
}
public interface ISqlDataAccess
{
string ConnectionStringName { get; set; }
Task<List<T>> LoadData<T, U>(string sql, U parameters);
Task SaveData<T>(string sql, T parameters);
Task DeleteData<T>(string sql, T parameters);
}
public class SqlDataAccess : ISqlDataAccess
{
private readonly IConfiguration _config;
public string ConnectionStringName { get; set; } = "Default";
public SqlDataAccess(IConfiguration config)
{
_config = config;
}
public async Task<List<T>> LoadData<T, U>(string sql, U parameters)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
var data = await connection.QueryAsync<T>(sql, parameters);
return data.ToList();
}
}
public async Task SaveData<T>(string sql, T parameters)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
await connection.ExecuteAsync(sql, parameters);
}
}
public async Task DeleteData<T>(string sql, T parameters)
{
string connectionString = _config.GetConnectionString(ConnectionStringName);
using (IDbConnection connection = new SqlConnection(connectionString))
{
await connection.ExecuteAsync(sql, parameters);
}
}
}
welcome to the community! The nice thing about .Net core is it makes the dependency injection system work very smoothly. So in your constructor for your parser, you just need to inject what you need from your data access layer. An example with your PermitData class would look like this:
public class TextFileParser
{
private IPermitData _permitData;
public TextFileParser(IPermitData permitData)
{
this._permitData= permitData;
}
// code that uses your injected service
_permitData.MethodToDoWorkEtc();
}
Notice that in the constructor you call for the interface you need as I have it written, but you can also call for a concrete class if that is how your dependency injection is set up. It then populates a backing field in your class with the injected service, and you use the backing field as the start point to do the work that needs done.
The DI framework will pick this all up when you build your app and assemble it for you, so as long as the service is registered in the Startup.cs file, you shouldn't need anything else. This method will also allow you to compose simple services together into larger more advanced services while keeping the simple parts separated for better testing and portability.
Hope this helps!
Related
I am using the following Code
public class GetTabelRealizari : ControllerBase
{
public class Realizare
{
String user;
String denumire;
String incasari;
public Realizare(String user, String denumire, String incasari)
{
this.user = user;
this.denumire = denumire;
this.incasari = incasari;
}
public String getUser()
{
return user;
}
public void setUser(String user)
{
this.user = user;
}
public String getDenumire()
{
return denumire;
}
public void setDenumire(String denumire)
{
this.denumire = denumire;
}
public String getIncasari()
{
return incasari;
}
public void setIncasari(String incasari)
{
this.incasari = incasari;
}
}
[HttpPost]
public string Post([FromBody] string[] value)
{
//SSMS connection
string connectionString = "Data Source=DESKTOP-QKC0G7V;Initial Catalog=Restaurant_gest;Integrated Security=True";
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
List<Realizare> realizari = new List<Realizare>();
double incasari;
String incasariString;
SqlCommand command = new SqlCommand("SELECT Users.Username," +
" Tip_Nota_Plata.Denumire," +
" sum(Nota_plata.Suma) as Incasari" +
" from Users" +
" INNER JOIN Nota_plata" +
" INNER JOIN Comandas" +
" ON Nota_plata.Id_comanda = Comandas.Id" +
" ON Comandas.User_Id = Users.Id" +
" INNER JOIN Tip_Nota_Plata" +
" ON Tip_Nota_Plata.Id = Nota_plata.Id_tip_nota" +
" Group by Username, Tip_Nota_Plata.Denumire", connection);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
incasari = (double)reader["Incasari"];
incasariString = incasari.ToString("#.##");
realizari.Add(new Realizare(reader["Username"].ToString(), reader["Denumire"].ToString(), incasariString));
}
}
return JsonConvert.SerializeObject(realizari);
//return "salut";
}
}
And I am receiving an empty JsonObject. Why?
[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]
I keep trying to make it work and I cannot. The list has the objects, i can test it with Console.Writeline(realizari[0].getDenumire()) and it works. I can also serialize a list of strings, it just doesn`t work for objects.
Because the object has no serializable properties.
I'm going to guess you are a Java developer based on this:
String user;
public String getUser()
{
return user;
}
public void setUser(String user)
{
this.user = user;
}
C# has "properties" which, while they compile down to methods very similar to this, the syntax in C# is a bit different. All of the above code can be simplified to a property:
public String User { get; set; }
The usage then becomes simpler as well, allowing for assignments instead of calling a method:
someObject.User = someUser;
In cases where you want to add logic to your getter/setter, you can expand the "auto implemented property" above into a manual one:
private string user;
public string User
{
get { return user; }
set { user = value; }
}
The get and set syntax still tells the compiler that this is a property, but within those blocks you can write any method logic you like. (In the setter value is a keyword for the value being assigned to the property.)
I have created the class at the bottom in c#. This class is referenced by webservices to determine user accesses, like this:
[WebMethod]
public List<FAFSA> getFAFSA(string pageID)
{
formValues fv = new formValues();
string personID = fv.personID;
List<FAFSA> lf = new List<FAFSA>();
if (fv.secBlur == "no_secBlur")
{
FAFSA f = new FAFSA();
f.fafsaCheck = "0";
lf.Add(f);
}
...
}
I'm trying to add the two variables fafsa and staff. The method getSecBlur() is returning all three values from my database for secBlur, fafsa, and staff. So how do I set up this class, so that the SecBlur method is only called once but populates all three of my variables so that they can be used in webservice calls? It will not work the way it is now because it says fafsa and staff need to be static, but if I make them static, then in the webservices it says that the members must be accessed with an instance reference.
Sorry if this isn't worded to well, but I'm new to this and still trying to learn...
public class formValues : System.Web.Services.WebService
{
public string userName = getUserName();
public string firstName = getFirstName();
public string personID = getPersonID();
public int fafsa = 0;
public int staff = 0;
public string secBlur = getSecBlur();
private static string getUserDataString(int ix)
{
string retValue = "";
if (HttpContext.Current.Request.IsAuthenticated)
{
HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
if (ticket != null)
{
string[] userData = { "" };
char[] delimiterChar = { '|' };
userData = ticket.UserData.Split(delimiterChar);
if (userData.Length > 1)
retValue = userData[ix];
else
{
FormsAuthentication.SignOut();
string redirUrl = "/DMC/loginNotFound.html";
HttpContext.Current.Response.Redirect(redirUrl, false);
}
}
}
}
return retValue;
}
private static string getUserName()
{
//This retrieves the person logged into windows/active directory
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
//string[] fullUsername = wp.Identity.Name.Split('\\');
string fullUsername = wp.Identity.Name;
return fullUsername;
}
private static string getFirstName()
{
string firstName = getUserDataString(1);
return firstName;
}
private static string getPersonID()
{
string personID = getUserDataString(0);
return personID;
}
private static string getSecBlur()
{
string secBlur = "no_secBlur";
string mySQL = "exec get_UserAdminStatus #personID";
string cf = System.Configuration.ConfigurationManager.ConnectionStrings["DistrictAssessmentDWConnectionString"].ConnectionString;
SqlConnection connection = new SqlConnection(cf);
SqlCommand command = new SqlCommand(mySQL, connection);
command.Parameters.AddWithValue("#personID", getUserDataString(0));
connection.Open();
SqlDataReader dr = command.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
connection.Close();
if (dt.Rows.Count > 0)
{
if (dt.Rows[0]["secBlur"].ToString() == "1")
secBlur = "secBlur";
fafsa = Convert.ToInt32(dt.Rows[0]["fafsa"]);
staff = Convert.ToInt32(dt.Rows[0]["staff"]);
}
return secBlur;
}
}
If you give any class static, public values the so called "Static" (or type) Constructor will be called to do the initialization work before any access is done: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
Another common way to do initlizsation or define default values, is to use the Factory Pattern. Afaik the Graphics Class in XNA has to adapt depending if you run ona X-Box or PC, so it uses the Factory Pattern.
Of coruse with Web(anything) there is the whole issue with variable Scope, even for Statics. Much less local variables.
I have experience in working and fixing bugs with existing code bases that implement MySql code, but have to design a new program from scratch at my new job. I am not sure what is the best way to return data from MySqlDataReader to my custom models. Please advise!
Here's what I have,
Folder structure:
Models (folder)
Metadata.cs
User.cs
MySqlDb.cs
Metadata.cs: Reresents data from metadata table
public class Metadata
{
public int Id { get; set; }
public string Title { get; set; }
public string Sku { get; set; }
public bool IsLive { get; set; }
}
User.cs: Represents data from user table
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public int Age { get; set; }
public string Address { get; set; }
}
MySqlDb.cs
using MySql.Data;
using MySql.Data.MySqlClient;
public class MySqlDb
{
public MySqlConnection Connection { get; set;}
public MySqlDb(string connectionString)
{
Connection = new MySqlConnection(connectionString);
}
public List<Metadata> RunSelectQueryForMetadata(string query)
{
var rdr = new MySqlCommand(query, Connection).ExecuteReader();
var metadata = new List<Metadata>();
using (rdr)
{
while(rdr.Read())
{
metadata.Add(
new Metadata {
Id = rdr["id"],
Title = rdr["title"],
Sku = rdr["sku"],
IsLive = rdr["islive"],
});
} // while
} // using
return metadata;
} // public void RunSelectQuery(string query)
} // public class MySqlDb
If I try to get Users data, I am thinking of writing another method (RunSelectQueryForUsers). I would like to avoid writing different methods for different tables. I am not sure how to use one method for retrieving data from different tables with different data structures and typecast them to the Model I want.
Any help is greatly appreciated!!
One way is to use micro-orm such as Dapper which is a simple object mapper built for .Net. Dapper extends the IDbConnection by providing useful extension methods to query your database.
Example of implementing dapper within your current menthod:
public List<Metadata> RunSelectQueryForMetadata(string query)
{
var metadata = new List<Metadata>();
try // implement proper error handling
{
Connection.Open();
metadata = Connection.Query<Metadata>(query).ToList();
Connection.Close();
}
catch(Exception ex)
{
// error here
}
return metadata;
}
Some useful links:
Dapper Github
Dapper Tutorial
Converting it to generic method: (not tested right now)
public List<T> RunSelectQuery<T>(string query)
{
try // implement proper error handling
{
Connection.Open();
metadata = Connection.Query<T>(query).ToList();
Connection.Close();
}
catch(Exception ex)
{
// error here
}
return metadata;
}
and use something like this below:
List<Metadata> myMetadata = RunSelectQuery<Metadata>(query);
I prefer a pattern more like this:
public class MySqlDb
{
//1. This should not be public!
// Keeping it private forces other code to go through your public methods,
// rather than using the connection directly.
// Even better if the class knows how to read the string from a
// config rile rather than accepting it via the constructor.
//2. Don't save a connection object for re-use.
// ADO.Net has a connection pooling feature that works when you
// create new objects for most queries
private string ConnectionString { get; set;}
public MySqlDb(string connectionString)
{
ConnectionString = connectionString;
}
//1. Use IEnumerable instead of List
// ...don't pull all of the results into memory at the same time until/unless you really have to.
//2. Methods that accept query strings should also accept parameters.
// Otherwise you are forced to build sql strings in insecure crazy-vulnerable ways
public IEnumerable<Metadata> RunSelectQueryForMetadata(string query, IEnumerable<MySqlParameter> parameters)
{
using (var cn = new MySqlConnection(ConnectionString))
using (var cmd = new MySqlCommand(query, cn))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
cn.Open();
using(var rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
yield return new Metadata {
Id = rdr["id"],
Title = rdr["title"],
Sku = rdr["sku"],
IsLive = rdr["islive"],
};
}
rdr.Close();
}
}
}
}
Ultimately, the ideal is for the RunSelectQuery__() method to be generic and private, and for public methods to not accept SQL statements. The goal is to force all SQL in your program to live in the MySqlDb class. Each query has a method that accepts specific typed inputs, and returns typed output. The reason you have that goal is to make it easy to manage your database access and easy to audit that all of your SQL code is safely using parameters (and not vulnerable to sql injection attacks! ). You want something like this:
//updated to remove the earlier explanatory comments
// and show example methods for isolating SQL from the rest of the application.
public class MySqlDb
{
private string ConnectionString { get; set;}
private string ReadConnectionStringFromConfigFile()
{
//TODO
throw NotImplementedException();
}
public MySqlDb()
{
ConnectionString = ReadConnectionStringFromConfigFile();
}
//This is now PRIVATE and generic, and allows for parameterized queries
private IEnumerable<T> RunSelectQuery(string query, Func<IDataReader, T> translateRecord, IEnumerable<MySqlParameter> parameters)
{
using (var cn = new MySqlConnection(ConnectionString))
using (var cmd = new MySqlCommand(query, cn))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
cn.Open();
using(var rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
yield return translateRecord(rdr);
}
rdr.Close();
}
}
}
////// Example methods showing how to use the generic method above
// These methods are the only public part of your class
public MetaData GetMetaDataById(int ID)
{
string sql = "SELECT * FROM MetatData WHERE ID= #ID";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#ID",
MySqlDbType = MySqlDbType.Int32,
Value = ID
}
};
return RunSelectQuery<MetaData>(sql, parameters, r =>
new Metadata {
Id = r["id"],
Title = r["title"],
Sku = r["sku"],
IsLive = r["islive"],
}).FirstOrDefault();
}
public IEnumerable<MetaData> GetAllMetaData()
{
string sql = "SELECT * FROM MetatData";
return RunSelectQuery<MetaData>(sql, null, r =>
new Metadata {
Id = r["id"],
Title = r["title"],
Sku = r["sku"],
IsLive = r["islive"],
});
}
public User GetUserByID(int ID)
{
string sql = "SELECT * FROM User WHERE ID= #ID";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#ID",
MySqlDbType = MySqlDbType.Int32,
Value = ID
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
}).FirstOrDefault();
}
public User GetUserByUsername(string UserName)
{
string sql = "SELECT * FROM User WHERE Username= #UserName";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#UserName",
MySqlDbType = MySqlDbType.VarChar,
Size = 20, //guessing at username lenght
Value = UserName
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
}).FirstOrDefault();
}
public IEnumerable<User> FindUsersByAge(int Age)
{
string sql = "SELECT * FROM User WHERE Age = #Age";
var parameters = new List<MySqlParameters> {
new MySqlParameter() {
ParameterName = "#Age",
MySqlDbType = MySqlDbType.Int32,
Value = Age
}
};
return RunSelectQuery<User>(sql, parameters, r =>
new Metadata {
Id = r["id"],
UserName = r["UserName"],
Age = r["Age"],
Address = r["Address"],
});
}
}
In larger applications, you abstract this further into a separate project, with a private class for the lower-level methods that are private here, and a public class for each of the object types you use via that database. You might even go full-blown service-oriented architecture, where you get all your data via web service calls, and only the service layer talks directly to any database.
Of course, at this level you can also use a mirco-ORM like Dapper. Micro-ORMs will help you avoid re-writing the same mapping code over and over, and also help more with the INSERT/UPDATE side of data operations. Their goal is to take over as much of the boilerplate code for you as they can.
The advantage of a micro-ORM over a full ORM is it keeps you closer to the SQL. This is a good thing. Full-blown ORMs like Entity Framework or NHibernate effectively force you to learn a whole new language on top of the SQL, while mostly limiting you to basic SQL statements that often lose the advantages from the "relational" part of a relational database. Eventually, you often end up needing to understand and write complex raw SQL anyway to optimize performace. Micro-ORMs try to offer a happy-medium... taking away as much of the boiler plate code needed to talk to a database as they can, while still leaving you to write your own SQL.
While not tailored to using MySql and straight up sql, the below code snippets provide a means to do what you're asking using generics. Could use some improvements though...
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data.General
{
public abstract class DataObject
{
protected abstract void Initialize(IDataRecord dataRow);
private static string _connectionString = "";
/// <summary>
/// Loads a single data object from the results of a stored procedure.
/// </summary>
protected static T ReadObject<T>(string procedureName, SqlParameter[] sqlParameters, Type dataType)
{
DataObject returnItem = null;
using (SqlConnection sqlConnection = new SqlConnection(GetConnectionString()))
using (SqlCommand command = BuildCommand(sqlConnection, procedureName, sqlParameters))
{
sqlConnection.Open();
//Execute the reader for the given stored proc and sql parameters
using (IDataReader reader = command.ExecuteReader())
{
//If we get no records back we'll still return null
while (reader.Read())
{
returnItem = (DataObject)Activator.CreateInstance(typeof(T));
returnItem.Initialize(reader);
break;
}
}
}
//Return our DataObject
return (T)Convert.ChangeType(returnItem, dataType);
}
/// <summary>
/// Reads a collection of data objects from a stored procedure.
/// </summary>
protected static List<T> ReadObjects<T>(string procedureName, SqlParameter[] sqlParameters)
{
//Get cached data if it exists
List<T> returnItems = new List<T>();
T dataObject;
using (SqlConnection sqlConnection = new SqlConnection(GetConnectionString()))
using (SqlCommand command = BuildCommand(sqlConnection, procedureName, sqlParameters, null))
{
sqlConnection.Open();
//Execute the reader for the given stored proc and sql parameters
using (IDataReader reader = command.ExecuteReader())
{
//If we get no records back we'll still return null
while (reader.Read())
{
dataObject = (T)Activator.CreateInstance(typeof(T));
(dataObject as DataObject).Initialize(reader);
returnItems.Add(dataObject);
}
}
}
//Return the DataObjects
return returnItems;
}
/// <summary>
/// Builds a SQL Command object that can be used to execute the given stored procedure.
/// </summary>
private static SqlCommand BuildCommand(SqlConnection sqlConnection, string procedureName, SqlParameter[] sqlParameters, SqlTransaction sqlTransaction = null)
{
SqlParameter param;
SqlCommand cmd = new SqlCommand(procedureName, sqlConnection);
if (sqlTransaction != null)
{
cmd.Transaction = sqlTransaction;
}
cmd.CommandType = CommandType.StoredProcedure;
// Add SQL Parameters (if any)
foreach (SqlParameter parameter in sqlParameters)
{
param = new SqlParameter(parameter.ParameterName, parameter.DbType);
param.Value = parameter.Value;
cmd.Parameters.Add(param);
}
return cmd;
}
private static string GetConnectionString()
{
return _connectionString;
}
public static void SetConnectionString(string connectionString)
{
_connectionString = connectionString;
}
}
}
namespace Data.Library
{
public class Metadata : General.DataObject
{
protected Data.Model.Metadata _metaData;
public Data.Model.Metadata BaseModel
{
get { return _metaData; }
set { _metaData = value; }
}
//Typically I have properties in here pointing to the Data.Model class
protected override void Initialize(System.Data.IDataRecord dataRow)
{
_metaData = new Model.Metadata();
_metaData.Id = Convert.ToInt32(dataRow["Id"].ToString());
_metaData.Title = (dataRow["Title"].ToString());
_metaData.Sku = (dataRow["Sku"].ToString());
_metaData.IsLive = Convert.ToBoolean(dataRow["IsLive"].ToString());
}
public static Metadata ReadByID(int id)
{
return General.DataObject.ReadObject<Metadata>("dbo.s_MetadataGet", new[] { new SqlParameter("#ID", id) },
typeof(Metadata));
}
public static Metadata[] ReadBySku(string sku)
{
List<Metadata> metaDatas = General.DataObject.ReadObjects<Metadata>("dbo.s_MetadataGetBySku", new[] { new SqlParameter("#Sku", sku) });
return metaDatas.ToArray();
}
}
}
namespace Data.Model
{
public class Metadata
{
public int Id { get; set; }
public string Title { get; set; }
public string Sku { get; set; }
public bool IsLive { get; set; }
}
}
I've been trying to create a windows phone and I'd like to use SQLite to both store my data and learn how to use it on windows phone apps. For this purpose I'm using "SQLite.Net-PCL", but I keep getting a file not found exception. This the code I've written:
String ConnectionString = Path.Combine(ApplicationData.Current.LocalFolder.Path, Connection);
if (File.Exists(ConnectionString))
{
SQLite.Net.Platform.WindowsPhone8.SQLitePlatformWP8 e = new SQLite.Net.Platform.WindowsPhone8.SQLitePlatformWP8();
Con = new SQLiteConnection(e,ConnectionString);
}
else {
SQLite.Net.Platform.WindowsPhone8.SQLitePlatformWP8 e = new SQLite.Net.Platform.WindowsPhone8.SQLitePlatformWP8();
File.Create(ConnectionString);
Con = new SQLiteConnection(e, ConnectionString);
}
I thought maybe I get this error because I manually create an empty file but if that is the problem, how can I create a DB in case no database exists in the phone ?
You don't need to create the file yourself, as the SQLiteConnection constructor manages that for you.
public SQLiteConnection(ISQLitePlatform sqlitePlatform, string databasePath, bool storeDateTimeAsTicks = false, IBlobSerializer serializer = null)
: this(
sqlitePlatform, databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, serializer)
{
}
So you should just open the connection, create the tables and that should be that.
class ExampleDataContext
{
public const string DATABASE_NAME = "data.sqlite";
private SQLiteConnection connection;
public TableQuery<Foo> FooTable { get; private set; }
public TableQuery<Bar> BarTable { get; private set; }
public ExampleDataContext()
{
connection = new SQLiteConnection(new SQLitePlatformWinRT(), Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, DATABASE_NAME));
Initialize();
FooTable = connection.Table<Foo>();
BarTable = connection.Table<Bar>();
}
private void Initialize()
{
connection.CreateTable<Foo>();
connection.CreateTable<Bar>();
}
}
Don't worry about that Initialize, the tables only get created when they're not there yet.
I have a static class Data:
public static class Data
{
public static SqlConnection connexion;
public static bool Connect()
{
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Initial Catalog"] = "Upload";
builder["Data Source"] = "base";
builder["integrated Security"] = true;
string connexionString = builder.ConnectionString;
connexion = new SqlConnection(connexionString);
try { connexion.Open(); return true; }
catch { return false; }
}
public static void Disconnect()
{
if (connexion != null) connexion.Close();
connexion = null;
}
}
in an the action Home :
public ActionResult Home()
{
Data.Connect();
if (CompteModels.Connected)
{
ArrayList model = new ArrayList();
ClientModels clients = new ClientModels();
model.AddRange(clients.Client_List());
AdminModels admins = new AdminModels();
model.AddRange(admins.Admin_List());
return View(model);
}
else return RedirectToAction("Login", "Account");
}
the client class:
public List<ClientModels> Client_List()
{
List<ClientModels> l = new List<ClientModels>();
using (Data.connexion)
{
string queryString = #"select Login, Password, Mail, Name, Tentatives from Compte where User_type_id in ( select Id from User_type where Fonction = 'Client')";
SqlCommand command = new SqlCommand(queryString, Data.connexion);
try
{
SqlDataReader reader = command.ExecuteReader();
do
{
while (reader.Read())
{
ClientModels admin = new ClientModels { Login = reader.GetString(0), Password = reader.GetString(1), Mail = reader.GetString(2), Name = reader.GetString(3), Tentatives = reader.GetInt32(4) };
l.Add(admin);
}
} while (reader.NextResult());
return l;
}
catch { return null; }
}
For the function AdminList, the implementation is the same like Client_List but in the class Admin.
The problem is in the static variable connexion : in the first function Client_List its value is correct and i get the list of the clients , but it's become null in the second function despite it is a static variable in static class !!!
What is the reason of this alteration ? How can i fix it?
You're either setting connexion to null somewhere or not initializing it before you use it.
Most likely, one class is calling Disconnect which sets connexion to null, while another class assumes it's not null and tries to use it.
As mentioned in the comments, keeping a static reference to a resource like a SqlConnection is not a good idea. If you want to re-use code you can create a static function that returns a new SqlConnection instance and make the connection string static, but having a static reference to a connection that's share across the entire web site will give you more problems that it's worth (as you're already seeing).
One way to do it in a static function would be:
public static SqlConnection GetConnection()
{
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Initial Catalog"] = "Upload";
builder["Data Source"] = "base";
builder["integrated Security"] = true;
string connexionString = builder.ConnectionString;
connexion = new SqlConnection(connexionString);
return connexion;
}
Your client code would then look something like:
using (SqlConnection conn = Data.GetConnection())
This is a bad idea, as others have already mentioned.
You should simply create and open a connection when needed, and dispose it afterwards:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// ... now use it
}
Of course, there are other patterns, that hide this mechanism, but that might be overkill in your case.
Your using statement disposes of the connexion using the IDisposable interface
public List<ClientModels> Client_List()
{
List<ClientModels> l = new List<ClientModels>();
using (Data.connexion) <--- here
}
Change this to create a new connection
public List<ClientModels> Client_List()
{
List<ClientModels> l = new List<ClientModels>();
using (var connection = Data.CreateConnexion())
}
similar to this
public static class Data
{
public static SqlConnection CreateConnection()
{
System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
builder["Initial Catalog"] = "Upload";
builder["Data Source"] = "base";
builder["integrated Security"] = true;
string connexionString = builder.ConnectionString;
var connexion = new SqlConnection(connexionString);
connexion.Open();
return connexion;
}
}