First Look at my code
namespace HealthClub
{
public partial class frmTrainerMaster : Form
{
DataTable dt = new DataTable();
frmHome Home = new frmHome();
public frmTrainerMaster()
{
InitializeComponent();
}
}
private void frmTrainerMaster_Load(object sender, EventArgs e)
{
FillValues("UserNameText");
}
public void FillValues(string UserName)
{
DataTable DT;
SqlCommand cmd = new SqlCommand();
try
{
cmd.Connection = Connections.Connection[UserName];
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "TrainerMaster_pro";
cmd.Parameters.AddWithValue("Option", "FillValues".Trim());
if (Connections.Connection[UserName].State == ConnectionState.Closed)
Connections.Connection[UserName].Open();
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DT = new DataTable();
adp.Fill(DT);
lblId___.Text = DT.Rows[0][0].ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
cmd.Parameters.Clear();
cmd.Dispose();
Connections.Connection[UserName].Close();
}
}
}
Now I am calling FillValues() from another class like this
class AnotherClass
{
public void TestMethod(string FormName)
{
Type tp = Type.GetType("HealthClub." + FormName);
object myobj = Activator.CreateInstance(tp);
MethodInfo method = myobj.GetType().GetMethod("FillValues");
object[] parametersArray = new object[] { UserName };
method.Invoke(myobj, parametersArray);
}
}
If you look at the FillValues(), I am assigning the database value to a label. When I am calling it in my first class in page load it's working fine.
But when I am Invoking the medthod from second class, Method invokes but database value does not assign to the label.
What extra effort I need to do ?
There is a class and there is an instance. This is very basic concept you need to understand (not just in C#, but in any objective-oriented language).
// class example
class FrmTrainerMaster { public int SomeProperty { get; set;} }
When you create new instance:
// creates one window
var frmTrainerMasterInstanceOne = new FrmTrainerMaster();
frmTrainerMasterInstanceOne.SomeProperty = 1;
// creates second window
var frmTrainerMasterInstanceTwo = new FrmTrainerMaster();
frmTrainerMasterInstanceTwo.SomeProperty = 2;
Instances are SEPARATE - so at this point querying
// will return 1
Console.Out.WriteLine(frmTrainerMasterInstanceOne.SomeProperty);
// will return 2
Console.Out.WriteLine(frmTrainerMasterInstanceTwo.SomeProperty);
With reflection var myobj = Type.GetType("HealthClub.FrmTrainerMaster"); is equal to var myobj = new FrmTrainerMaster(); so by doing anything with myobj, you still can't affect frmTrainerMasterInstanceOne or frmTrainerMasterInstanceTwo.
What do you need is actually method how to pass reference to instance (of FrmTrainerMaster class) to place where you need it (lets call it AnotherForm), there is no magic list of all instances for given class unless you explicitly build it.
public partial class FrmTrainerMaster : Form
{
public void FillValues(string userName) { ... }
}
One way is via constructor injection (generally proffered - since at time when object (AnotherForm) is constructed you have it in valid state (i.e. with all dependencies initialized).
public class AnotherForm : Form {
private readonly FrmTrainMaster _frmTrainMaster;
public AnotherForm(FrmTrainMaster frmTrainMaster) {
if (frmTrainMaster == null) {
throw new ArgumentNullException(nameof(frmTrainMaster));
}
_frmTrainMaster = frmTrainMaster;
}
}
Or via setter injection:
public class AnotherForm : Form {
private FrmTrainMaster _frmTrainMaster;
public FrmTrainMaster MasterForm { set { _frmTrainMaster = value; } }
}
Either way the reflection is not necessary at all. At any place in your AnotherForm you can just call
class AnotherForm : Form {
...
public void FooMethodThatCallFillValues() {
_frmTrainMaster.FillValues("...");
}
}
Related
I have the following interface and class
public interface IOwner
{
int Owner_pkid { get; set; }
string Name { get; set; }
}
public class Owner : IOwner
{
public Owner()
{
}
public int Owner_pkid { get; set; }
public string Name { get; set; }
}
I then have the following Data Access Methods in a separate class
public List<IOwner> GetAllOwners()
{
var sql = "SELECT owner_pkid, name from dbo.Owners ";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
List<IOwner> owners = new List<IOwner>();
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
BindResultSet<IOwner>(reader, owners);
return owners;
}
}
private void BindResultSet<T>(SqlDataReader reader, List<T> items) where T : new()
{
int counter = 0;
if (!reader.IsClosed)
{
while (reader.Read())
{
T record = GetNextDataObject<T>(items, counter);
counter++;
BindRecord<T>(reader, record);
}
}
}
private T GetNextDataObject<T>(List<T> items, int pointer) where T : new()
{
if (pointer < items.Count)
{
return items[pointer];
}
else
{
items.Add(new T());
return items[items.Count - 1];
}
}
private void BindRecord<IOwner>(SqlDataReader reader, IOwner owner)
{
owner.Name = (string)reader["name"];
owner.Owner_pkid = (int)reader["owner_pkid"];
}
I am getting 2 separate errors with this code:
In the GetAllOwners method I am getting an error with the call to BindResultSet which says
IOwner must be a non-abstract type with a parameterless constructor in order to be used here
I have a parameterless constrctor on the implementing class - don't think i can add one to the interface
In the final BindRecord method I am getting an error whereby the two property names are not recognized. This is possibly as a result of the first issue
The problem is that you cannot do new on an interface. So "new T" where T is IOwner will not work.
There are a number of different ways to fix this. The cleanest approach would be to add a factory interface whose only purpose is to create IOwner objects, say IOwnerFactory. Instantiate the factory at the top level and pass it down to the methods that need to create objects.
So instead of item.Add(new T()) use item.Add(ownerFactory.Create())
I am really stuck on calling my function InlogLeerling() from .cs file Login.cs into MainPage.xaml.cs.
I did try everything and I already found some answers but I do not understand how I can get it working in my project. When i call the function InlogLeerling() I get the error There is no argument given that corresponds to the required formal parameter 'mainpage' of 'Login.InlogLeerling(MainPage)'
Here is the code I am using in my Login.cs
namespace VerlofXamarin.Logical_Layer
{
public class Login
{
public string pu_Gebruikersnaam, pu_Wachtwoord, pu_LogLeerling;
string Gebruikersnaam
{
get { return pu_Gebruikersnaam; }
set { pu_Gebruikersnaam = value; }
}
string Wachtwoord
{
get { return pu_Wachtwoord; }
set { pu_Wachtwoord = value; }
}
public MainPage mainpage;
private void InlogLeerling(MainPage mainpage)
{
Data_Layer.Verbinding vv = new Data_Layer.Verbinding();
this.mainpage = mainpage;
try
{
if(string.IsNullOrEmpty(pu_Gebruikersnaam) == true || string.IsNullOrEmpty(pu_Wachtwoord) == true)
{
mainpage.pu_LeerlingLog = "Vul gebruikersnaam en wachtwoord in!";
return;
}
vv.con.Open();
MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand("SELECT leerlinggebruikersnaam, leerlingwachtwoord FROM arabignl_project.myfirstmodule$leerlinglogin WHERE (leerlinggebruikersnaam = #gebruiker AND leerlingwachtwoord = #wachtwoord)", vv.con);
cmd.Parameters.AddWithValue("#gebruiker", pu_Gebruikersnaam.ToString());
cmd.Parameters.AddWithValue("#wachtwoord", pu_Wachtwoord.ToString());
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
mainpage.pu_LeerlingLog = "Inloggen gelukt";
}
}
else
{
mainpage.pu_LeerlingLog = "Inloggen mislukt";
}
reader.Close();
}
catch (MySqlException ex)
{
mainpage.pu_LeerlingLog = ex.ToString();
}
finally
{
vv.con.Close();
}
}
}
}
And the MainPage.xaml.cs
namespace VerlofXamarin
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
public Login login;
public string pu_LeerlingLog
{
get { return LoginLog.Text; }
set { LoginLog.Text = value; }
}
public string pu_LeerlingGebruikersnaam
{
get { return LeerlingGebruikersnaam.Text; }
}
public string pu_LeerlingWachtwoord
{
get { return LeerlingWachtwoord.Text; }
}
public void LoginKlik(Login login)
{
this.login = login;
login.InlogLeerling();
}
I have already tried so many things.
You have two problems in your code.
You can´t call any private member from outside your class. So make your method public
your method expects a parameter of type MainPage. So you have to provide it, which is exactly what your error states:
public void LoginKlik(Login login)
{
this.login = login;
login.InlogLeerling(this);
}
Apart from those you shouldn´t expose a field publicily. Instead use a public property which you can modify within your class and read outside your class Login:
public MainPage MainPage { get; private set; }
You should study access modifier. With private, other classes could not access it. So you could change it to either internal or public. Here is reference
- https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/access-modifiers
internal void InlogLeerling(MainPage mainpage)
You will not be able to call the method because they are not in same class.
When you give an access modifier of privte, the accesses is possible only within the class.
If you would like to give more access there is:
protected - A protected member is accessible within its class and by derived class instances.
Internal- Internal types or members are accessible only within files in the same assembly.(i.e. the same compiled program)
public- . Public access is the most permissive access level. There are no restrictions
Therefor by using internal or public (depends on what you need in more large aspect) you will be able to access this method within other classes in your project
I have a design problem with a 3 Tier application. Usually my database layer is only 1 class like this one:
public class DA
{
string _connString = "";
public DA()
{
_connString = ConfigurationManager.ConnectionStrings["MyConnString"].ToString();
}
public DA(string connString)
{
_connString = connString;
}
private SqlConnection GetConnection()
{
...
}
public Employee GetEmployee(int idEmployee)
{
...
}
public Whatever GetWhatever(int idWhatever)
{
...
}
...
But now I have a pretty big project and I would like to separate the DA class into smaller classes like DA_Employee, DA_Whatever, etc.
I would like to instantiate only DA one time and access the other classes like that:
DA db = new DA(connString);
db.Employee.GetEmployee(12);
db.Whatever.GetWhatever(89);
db.Whatever.UpdateWhatever(89, "newname");
I would prefer NOT having something like this:
DA db = new DA(connString);
DA_Employee dbEmployee = new DA_Employee(connString);
DA_Whatever dbWhataver = new DA_Whatever(connString);
I think I can instantiate all my classes in my main constructor and have some properties to access them?
How can I give access to all classes to GetConnection()?
Any help and reference appreciated.
Thanks!
Yes, you make the classes a property of DA.
public interface IRepository<T>
{
T GetById(int id);
}
public class EmployeeRepository : IRepository<Employee>
{
private SqlConnection sqlConn;
public EmployeeRepository(SqlConnection sqlconn)
{
this.sqlConn = sqlConn;
}
public Employee GetById(int id)
{
return new Employee();
}
}
Pass the SqlConnection as a constructor dependency.
public class DA : IDisposable
{
private SqlConnection sqlConn;
private IRepository<Employee> employeeRepo;
private IReposiotry<Whatever> whateverRepo;
public DA(string connectionString)
{
this.sqlConnection = GetSqlConnection(connectionString);
this.employeeRepo = new EmployeeRepository(this.sqlConnection);
this.whateverRepo = new WhateverRepository(this.sqlConnection);
}
public IRepository<Employee> Employee { get { return employeeRepo; } }
public IRepository<Whatever> Whatever { get { return whateverRepo; } }
}
And its usage
using (var db = new DA("connectionString"))
{
db.Employee.GetById(1);
db.Whatever.GetById(10);
}
I have an application which caches some data at startup. There are several things to put in a cache, but they are very similar. I created classes like this, the only difference in them is the type of the item to be added to the dictionary (in this example the Setting class), and the _sqlNotifyCommand.CommandText.
public class SettingsCache : ILoggerClass
{
private Dictionary<int, Dictionary<int, Setting>> _cachedItems;
private string _entityConnectionString;
private SQLNotifier _sqlNotifier;
private SqlCommand _sqlNotifyCommand = new SqlCommand();
private bool _dataLoaded = false;
private void AddItem(Setting item)
{
if (!_cachedItems.ContainsKey(item.PartnerId))
{
_cachedItems.Add(item.PartnerId, new Dictionary<int, Setting>());
}
if (_cachedItems[item.PartnerId].ContainsKey(item.Id))
{
_cachedItems[item.PartnerId].Remove(item.Id);
}
_cachedItems[item.PartnerId].Add(item.Id, item);
}
public Setting GetSetting(int partnerId, int id)
{
if (_cachedItems.ContainsKey(partnerId))
{
if (_cachedItems[partnerId].ContainsKey(id))
{
return _cachedItems[partnerId][id];
}
return null;
}
return null;
}
public SettingsCache(string connectionString)
{
_entityConnectionString = connectionString;
_cachedItems = new Dictionary<int, Dictionary<int, Setting>>();
LoadData();
try
{
using (var db = new partnerEntity(connectionString))
{
string adoSqlConnectionString = ((EntityConnection) db.Connection).StoreConnection.ConnectionString;
_sqlNotifier = new SQLNotifier(adoSqlConnectionString);
_sqlNotifier.NewMessage += _sqlNotifier_NewMessage;
_sqlNotifyCommand.CommandType = CommandType.Text;
_sqlNotifyCommand.CommandText = "SELECT setting_id, setting_value, partner_id FROM dbo.setting";
_sqlNotifyCommand.Notification = null;
_sqlNotifier.RegisterDependency(_sqlNotifyCommand);
}
}
catch (Exception exception)
{
this.Log(this, LogLevel.Error, 0, exception);
}
}
private void _sqlNotifier_NewMessage(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Insert || e.Info == SqlNotificationInfo.Update)
{
this.Log(this, LogLevel.Info, 0, string.Format("Database changed, reloading settings data..."));
LoadData();
}
_sqlNotifier.RegisterDependency(_sqlNotifyCommand);
}
private void LoadData()
{
_dataLoaded = false;
try
{
using (var db = new partnerEntity(_entityConnectionString))
{
var dbData = db.setting.ToList();
foreach (var cItem in dbData)
{
AddItem(new Setting
{
PartnerId = cItem.partner_id,
Id = cItem.setting_id,
Value = cItem.setting_value
});
}
}
_dataLoaded = true;
}
catch (Exception exception)
{
this.Log(this, LogLevel.Error, 0, exception);
}
if (!_dataLoaded)
{
Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(_ => { LoadData(); });
}
}
}
Is there a more generic way to do this? The last thing which was needed in the classes this part:
if (!_dataLoaded)
{
Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(_ => { LoadData(); });
}
And I had to modify every Caching class. I needed to declare the variable, add it to the try-catch block, and after the block insert the same line in 6 classes. This code seems very boilerplate to me, I can't believe there is no simpler solution. I tried to make an Interface with AddItem, LoadData, OnLoadDataFailed methods, but in the AddItem method I need to specify the item, I'm stuck.
Here is the basic structure of what (I think) you want to accomplish using Generics.
First, declare a common interface for cached items so that the AddItem method still works:
public interface ICacheItem
{
int Id { get; set; }
int PartnerId { get; set; }
}
Now you can create the SettingsCache<T> that holds the items:
public class SettingsCache<T> : ILogger where T : ICacheItem
{
private Dictionary<int, Dictionary<int, T>> _cachedItems;
//...
public SettingsCache(string connectionString, string commandText)
{
//...
}
private void AddItem(T item)
{
if (!_cachedItems.ContainsKey(item.PartnerId))
{
_cachedItems.Add(item.PartnerId, new Dictionary<int, T>());
}
if (_cachedItems[item.PartnerId].ContainsKey(item.Id))
{
_cachedItems[item.PartnerId].Remove(item.Id);
}
_cachedItems[item.PartnerId].Add(item.Id, item);
}
}
I left out most of the implementation to focus on your two biggest concerns, the command text and the AddItem method. The generic has a constraint so that it can only accept items that are of ICacheItem, so you can use any properties that ICacheItem defines.
To use a cache, simply create one with the specific type (assuming Setting implements ICacheItem):
var settingsCache = new SettingsCache<Setting>("connection string", "command string");
I've made an attempt to unit test a methods in the database layer of my app. In my test I was planning to write mocks to avoid having a live connection to MySQL.
I'll write down a simplified version of my code to illustrate. Let's start with the unit test:
[TestMethod()]
public void TestMethod1()
{
DataBaseInterface database = new DataBaseInterface(); // this is the class which has the method I want to unit test
DataSet ds = new DataSet();
ds.Tables.Add(new DataTable("signals"));
ds.Tables[0].Columns.Add(new DataColumn("ID"));
ds.Tables[0].Columns.Add(new DataColumn("Content"));
DataRow row = ds.Tables[0].NewRow();
row["ID"] = "1";
row["Content"] = "Foo";
ds.Tables[0].Rows.Add(row);
MockDataBaseConnection db = new MockDataBaseConnection();
string result = database.GetContent(1); // this is the method I want to unit test
}
The method I want to unit test looks something like this:
public class DatabaseInterface
{
private MySqlConnectionAdapter _sqlConn;
public string GetContent(int i)
{
MySqlCommand command = _sqlConn.CreateCommand();
command.CommandText = String.Format("");
_sqlConn.Open();
MySqlDataReader reader = command.ExecuteReader();
/// more code here
}
To prevent using an actual database connection in my unitest I wrote a few mockups and here lies the problem.
The mockups looks like this:
public class MySqlConnectionAdapter
{
public MySqlConnection _sqlConn;
public MySqlConnectionAdapter()
{
}
public MySqlConnectionAdapter(string con)
{
_sqlConn = new MySqlConnection(con);
}
public event MySqlInfoMessageEventHandler InfoMessage
{
add
{
_sqlConn.InfoMessage += value;
}
remove
{
_sqlConn.InfoMessage -= value;
}
}
public virtual event StateChangeEventHandler StateChange
{
add
{
_sqlConn.StateChange += value;
}
remove
{
_sqlConn.StateChange -= value;
}
}
public virtual void Open()
{
_sqlConn.Open();
}
public void Close()
{
_sqlConn.Close();
}
public string ServerVersion
{
get
{
return _sqlConn.ServerVersion;
}
}
protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
{
return _sqlConn.BeginTransaction(isolationLevel);
}
public void ChangeDatabase(string databaseName)
{
_sqlConn.ChangeDatabase(databaseName);
}
public string ConnectionString
{
get
{
return _sqlConn.ConnectionString;
}
set
{
_sqlConn.ConnectionString = value;
}
}
public virtual MySqlCommand CreateCommand()
{
return _sqlConn.CreateCommand();
}
public string DataSource
{
get
{
return _sqlConn.DataSource;
}
}
public string Database
{
get
{
return _sqlConn.Database;
}
}
public ConnectionState State
{
get
{
return _sqlConn.State;
}
}
}
public class MockDataBaseConnection : MySqlConnectionAdapter
{
public DataSet ds;
public new MockMySqlCommand CreateCommand()
{
return new MockMySqlCommand(ds);
}
}
public class MockMySqlCommand
{
private DataSet _ds;
public MockMySqlReader ExcecuteReader()
{
return new MockMySqlReader(_ds);
}
public MockMySqlCommand(DataSet ds)
{
_ds = ds;
}
}
public class MockMySqlReader
{
public DataSet _ds;
private int currRow = 0;
public MockMySqlReader(DataSet ds)
{
_ds = ds;
}
public bool Read()
{
if (currRow < _ds.Tables[0].Rows.Count)
{
currRow++;
return true;
}
else return false;
}
public object GetValue(int i)
{
return _ds.Tables[0].Rows[currRow][i];
}
}
When the line _sqlConn.CreateCommand() is executed it throws and exeption. Why is that?
The _sqlConn fiels in my unit test method is a MockDataBaseConnection object and I want it to return a MockMySqlCommand object.
The sealed classed of the MySQL dll is giving me a headache. Can anyone explain how I can fix my code or show a simple example of how to unit test a method
which queries a database without actually querying the database.
Like suggested by vulkanino, I think it would be a lot easier to setup a local database instance, and run the tests directly on it.
I've been in the same situation, and developed this project that simply sets up a local running MySql test server instance, without you having to care about server administration: https://github.com/stumpdk/MySql.Server