Access Modifiers - Should be loaded only once - c#

Can We prevent the following from loading more than once in my application. ie any other alternative than this?
public IEnumerable<User> users()
{
var users = Userlist();
return users.ToList();
}
public static List<User> Userlist()
{
string strSQL = "";
List<User> users = new List<User>();
strSQL = "select USERID,USERNAME,PASSWORD from USERS";
//if (Userlist().Count > 0)
//{
// return Userlist();
//}
//else
//{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString))
{
using (var command = new SqlCommand(strSQL, connection))
{
connection.Open();
using (var dataReader = command.ExecuteReader())
{
while (dataReader.Read())
{
users.Add(new User { Id = Convert.ToInt32(dataReader["USERID"]), user = dataReader["USERNAME"].ToString(), password = Decrypt(dataReader["PASSWORD"].ToString()), estatus = true, RememberMe = true });
}
}
}
}
return users;
// }
}
I just wanted the solution to be like the commented part(which does not work here).
EDIT : I just wanted to avoid unnecessary database calls.
Thanks in Advance!

The usual trick is to lazily load them. You could just use a Lazy<T>, but a double-checked simple field works too:
static List<Foo> fetched;
static readonly object syncLock = new object(); // because: threading
public static List<Foo> Whatever {
get {
var tmp = fetched;
if(tmp != null) return tmp;
lock(syncLock) {
tmp = fetched;
if(tmp != null) return tmp; // double-checked lock
return fetched = GetTheActualData();
}
}
}
private static List<Foo> GetTheActualData() {...}
Additional thoughts:
storing passwords is never a good idea
List<T> is mutable; you should make sure people can't change the list or the items in the list if you are storing it statically
what do you do when the data changes at the database? how does it update?

You can also use caching for this.
The idea is that, the List<Users> will be cached, and any time when applications asks for the user list, we return it from the cache, and avoiding the database hit thereof.
A sample implementation could be something like this. Suggest to read more about caching, as there are many aspects that needs to taken care like, when the cache will expire, how it will get invalidate if new users are entered in database etc.
public List<User> Userlist()
{
ObjectCache cache = MemoryCache.Default;
var users = cache["users"];
if (users == null)
{
CacheItemPolicy policy = new CacheItemPolicy();
//For dmonstration, I used cache expring after 1 day
//Set the cache policy as per your need
policy.AbsoluteExpiration = DateTime.Now.AddDays(1);
// Fetch the users here from database
List<User> userList = GetUsersFromDatabase();
//Set the users in the cache
cache.Set("users", userList, policy);
}
return cache["users"] as List<User>;
}
private static List<User> GetUsersFromDatabase()
{
string strSQL = "";
List<User> users = new List<User>();
strSQL = "select USERID,USERNAME,PASSWORD from USERS";
//if (Userlist().Count > 0)
//{
// return Userlist();
//}
//else
//{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString))
{
using (var command = new SqlCommand(strSQL, connection))
{
connection.Open();
using (var dataReader = command.ExecuteReader())
{
while (dataReader.Read())
{
users.Add(new User { Id = Convert.ToInt32(dataReader["USERID"]), user = dataReader["USERNAME"].ToString(), password = Decrypt(dataReader["PASSWORD"].ToString()), estatus = true, RememberMe = true });
}
}
}
}
return users;
}

Use Lazy, it is thread safe. Lazy
private Lazy<IEnumerable<User>> users = new Lazy<IEnumerable<User>>(Userlist);
public Lazy<IEnumerable<User>> Users
{
get
{
return this.users;
}
}
public static IEnumerable<User> Userlist()
{
string strSQL = "";
List<User> users = new List<User>();
strSQL = "select USERID,USERNAME,PASSWORD from USERS";
//if (Userlist().Count > 0)
//{
// return Userlist();
//}
//else
//{
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString))
{
using (var command = new SqlCommand(strSQL, connection))
{
connection.Open();
using (var dataReader = command.ExecuteReader())
{
while (dataReader.Read())
{
users.Add(new User { Id = Convert.ToInt32(dataReader["USERID"]), user = dataReader["USERNAME"].ToString(), password = Decrypt(dataReader["PASSWORD"].ToString()), estatus = true, RememberMe = true });
}
}
}
}
return users;
// }
}

Related

Reading from CSV file with CsvHelper

I managed to write contents from a List to a CSV file. Now I'm trying to read from the created CSV file and show the content in a Console Application. I'm getting a exception which lead me to some possible solutions on the internet. None of them are working for my Console Application. I hope you can help me.
My code from Program.cs which calls the method in FileOperations.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using CsvHelper;
namespace GimpiesConsoleOOcsvListUI
{
class Program
{
static void Main(string[] args)
{
// First line to skip a row and position all users correctly under the right headers
User user0 = new User("", "", "", "");
// Create user default instances
User user1 = new User("Beheer", "beheer#gimpies.nl", "123", "admin");
User user2 = new User("Inkoop", "inkoop#gimpies.nl", "123", "purchase");
User user3 = new User("Verkoop", "verkoop#gimpies.nl", "123", "sales");
// List of default users (with a list you can add, get and remove items in the list)
List<User> users = new List<User>();
users.Add(user0);
users.Add(user1);
users.Add(user2);
users.Add(user3);
// Create login instance
LoginManager loginMgr = new LoginManager();
// Create stock instance
Stock stock = new Stock();
// Create UserList instance
UserList usrlst = new UserList();
// Call method in UserList.cs
usrlst.Users(users);
Start:
// Welcome message
Console.WriteLine("Welcome to the Gimpies Console Application! Choose 1 to login or 2 to register as guest:");
// Get input from user
string input = Console.ReadLine();
bool successfull = false;
while (!successfull)
{
if(input == "1")
{
Console.WriteLine("Enter your username:");
string username = Console.ReadLine();
Console.WriteLine("Enter your password:");
string password = Console.ReadLine();
foreach (User user in users)
{
if (username == user.UserName && password == user.PassWord && user.UserRole == "admin")
{
// Create Admin instance to be able to call methods in that class
Admin am = new Admin();
// Calling the method in Admin.cs to start Menu logic
am.AdminLoggedIn();
successfull = true;
break;
}
if (username == user.UserName && password == user.PassWord && user.UserRole == "purchase")
{
// Create Purchase instance to be able to call methods in that class
Purchase pc = new Purchase();
// Calling the method in Purchase.cs to start Menu logic
pc.PurchaseLoggedIn();
successfull = true;
break;
}
if (username == user.UserName && password == user.PassWord && user.UserRole == "sales")
{
// Create Sales instance to be able to call methods in that class
Sales sl = new Sales();
// Calling the method in Sales.cs to start Menu logic
sl.SalesLoggedIn();
successfull = true;
break;
}
if (username == user.UserName && password == user.PassWord && user.UserRole == "guest")
{
// Create Guest instance to be able to call methods in that class
Guest gt = new Guest();
// Calling the method in Guest.cs to start Menu logic
gt.GuestLoggedIn();
successfull = true;
break;
}
}
if (!successfull)
{
Console.WriteLine("Your username or password is incorrect, try again !!!");
}
}
else if (input == "2")
{
// Create instance to go to method in class RegisterManager.cs
RegisterManager rm = new RegisterManager();
rm.Register(users);
successfull = true;
goto Start;
}
else if (input == "3")
{
FileOperations fo = new FileOperations();
// Calling the method from FileOperations.cs to write the List here to a CSV file
fo.WriteUsersToCSV(users);
goto Start;
}
else if (input == "4")
{
FileOperations fo = new FileOperations();
// Calling the method from FileOperations.cs to write the List here to a CSV file
fo.ReadUsersFromCSV(users);
goto Start;
}
else
{
Console.WriteLine("Try again !!!");
break;
}
}
// // Loop over stored users within instances inside the list where users are added
// foreach (User user in users)
// {
// if(loginMgr.Login(user))
// {
// // Login successfull
// Console.WriteLine("Login user " + user.UserName);
// }
// else
// {
// // Not successfull
// }
// }
}
}
}
In Program.cs it's about the else if with input == "4".
Then the method ReadUsersFromCSV is called inside FileOperations.cs:
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;
using System.Linq;
using CsvHelper;
namespace GimpiesConsoleOOcsvListUI
{
// Handles CRUD within CSV files and able to save them
public class FileOperations
{
// Writes to CSV file from List
public void WriteUsersToCSV(List<User> users)
{
// using (var mem = new MemoryStream())
// using (var writer = new StreamWriter(mem))
using (var writer = new StreamWriter("users.csv"))
using (var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csvWriter.Configuration.Delimiter = ";";
csvWriter.Configuration.HasHeaderRecord = true;
csvWriter.Configuration.AutoMap<User>();
csvWriter.WriteHeader<User>();
csvWriter.WriteRecords(users);
writer.Flush();
// var result = Encoding.UTF8.GetString(mem.ToArray());
// Console.WriteLine(result);
Console.WriteLine("Data saved to users.csv");
}
}
// Reads from CSV file and displays content from it (Work in progress...)
public void ReadUsersFromCSV(List<User> users)
{
// using (var mem = new MemoryStream())
// using (var writer = new StreamWriter(mem))
using (var reader = new StreamReader("users.csv"))
using (var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture))
{
// csvReader.Configuration.Delimiter = ";";
// csvReader.Configuration.HasHeaderRecord = true;
// csvReader.Configuration.AutoMap<User>();
// csvReader.ReaderHeader<User>();
// var records = csvReader.GetRecords<dynamic>();
// reader.Flush();
// var result = Encoding.UTF8.GetString(mem.ToArray());
csvReader.Read();
csvReader.ReadHeader();
csvReader.Configuration.HeaderValidated = null;
csvReader.Configuration.MissingFieldFound = null;
var records = csvReader.GetRecords<User>();
foreach (var record in records)
{
Console.WriteLine(records);
}
// Console.WriteLine(records);
}
}
// Writes to CSV file from List
public void SaveGimpiesToCSV(List<Gimpies> gimpies)
{
// using (var mem = new MemoryStream())
// using (var writer = new StreamWriter(mem))
using (var writer = new StreamWriter("gimpies.csv"))
using (var csvWriter = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csvWriter.Configuration.Delimiter = ";";
csvWriter.Configuration.HasHeaderRecord = true;
csvWriter.Configuration.AutoMap<Gimpies>();
csvWriter.WriteHeader<Gimpies>();
csvWriter.WriteRecords(gimpies);
writer.Flush();
// var result = Encoding.UTF8.GetString(mem.ToArray());
// Console.WriteLine(result);
}
}
}
}
The exception:
Unhandled exception. CsvHelper.MissingFieldException: Field with name 'username' does not exist. You can ignore missing fields by setting MissingFieldFound to null.
at CsvHelper.Configuration.ConfigurationFunctions.MissingFieldFound(String[] headerNames, Int32 index, ReadingContext context)
at CsvHelper.CsvReader.GetFieldIndex(String[] names, Int32 index, Boolean isTryGet, Boolean isOptional)
at CsvHelper.CsvReader.GetFieldIndex(String name, Int32 index, Boolean isTryGet)
at CsvHelper.Expressions.ExpressionManager.CreateConstructorArgumentExpressionsForMapping(ClassMap map, List`1 argumentExpressions)
at CsvHelper.Expressions.ObjectRecordCreator.CreateCreateRecordDelegate(Type recordType)
at CsvHelper.Expressions.RecordCreator.GetCreateRecordDelegate(Type recordType)
at CsvHelper.Expressions.RecordCreator.Create[T]()
at CsvHelper.Expressions.RecordManager.Create[T]()
at CsvHelper.CsvReader.GetRecords[T]()+MoveNext()
at GimpiesConsoleOOcsvListUI.FileOperations.ReadUsersFromCSV(List`1 users) in /home/pascalmariany/Projects/Csharp/GimpiesConsoleOOcsvList/GimpiesConsoleOOcsvListUI/FileOperations.cs:line 55
at GimpiesConsoleOOcsvListUI.Program.Main(String[] args) in /home/pascalmariany/Projects/Csharp/GimpiesConsoleOOcsvList/GimpiesConsoleOOcsvListUI/Program.cs:line 132
The contents of the CSV file:
My User.cs:
namespace GimpiesConsoleOOcsvListUI
{
public class User
{
// Constructor
public User(string username, string email, string password, string userrole)
{
_UserName = username;
_Email = email;
_Password = password;
_UserRole = userrole;
}
private string _UserName;
private string _Email;
private string _Password;
private string _UserRole;
public string UserName
{
get { return _UserName; }
set { _UserName = value; }
}
public string Email
{
get { return _Email; }
set { _Email = value; }
}
public string PassWord
{
get { return _Password; }
set { _Password = value; }
}
public string UserRole
{
get { return _UserRole; }
set { _UserRole = value; }
}
}
}
What would I need to change?
Update:
I read all the feedback and I changed some thing in the method within FileOperations.cs:
// Reads from CSV file and displays content from it (Work in progress...)
public void ReadUsersFromCSV(List<User> users)
{
// using (var mem = new MemoryStream())
// using (var writer = new StreamWriter(mem))
using (var reader = new StreamReader("users.csv"))
using (var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture))
{
try
{
csvReader.Configuration.Delimiter = ";";
csvReader.Configuration.IgnoreBlankLines = true;
csvReader.Configuration.HasHeaderRecord = true;
csvReader.Configuration.PrepareHeaderForMatch = (string header, int index) => header.ToLower();
csvReader.Configuration.MissingFieldFound = null;
// csvReader.Configuration.AutoMap<User>();
// csvReader.ReaderHeader<User>();
// var records = csvReader.GetRecords<dynamic>();
// reader.Flush();
// var result = Encoding.UTF8.GetString(mem.ToArray());
csvReader.Read();
csvReader.ReadHeader();
// csvReader.Configuration.HeaderValidated = null;
// Store all content inside a new List as objetcs
var records = csvReader.GetRecords<User>().ToList();
// users.ForEach(Console.WriteLine);
foreach (var record in records)
{
Console.WriteLine(record);
}
}
catch (CsvHelper.HeaderValidationException exception)
{
Console.WriteLine(exception);
}
}
}
I don't get an exception now, but I get this as output:
GimpiesConsoleOOcsvListUI.User
GimpiesConsoleOOcsvListUI.User
GimpiesConsoleOOcsvListUI.User
GimpiesConsoleOOcsvListUI.User
It's the correct amount of rows, but as you can see it doesn't display the contents which is stored from the CSV file into a List as objects.
Am I missing something?
I managed to solve it. I needed to adjust the foreach loop:
public void ReadUsersFromCSV(List<User> users)
{
// using (var mem = new MemoryStream())
// using (var writer = new StreamWriter(mem))
using (var reader = new StreamReader("users.csv"))
using (var csvReader = new CsvReader(reader, CultureInfo.InvariantCulture))
{
try
{
csvReader.Configuration.Delimiter = ";";
csvReader.Configuration.IgnoreBlankLines = true;
csvReader.Configuration.HasHeaderRecord = true;
csvReader.Configuration.PrepareHeaderForMatch = (string header, int index) => header.ToLower();
csvReader.Configuration.MissingFieldFound = null;
csvReader.Read();
csvReader.ReadHeader();
// Store all content inside a new List as objetcs
var records = csvReader.GetRecords<User>().ToList();
// Loop through the List and show them in Console
foreach (var record in records)
{
Console.WriteLine($"{record.username } {record.email} {record.password} {record.userrole}");
}
}
catch (CsvHelper.HeaderValidationException exception)
{
Console.WriteLine(exception);
}
}
}
Thanks, but the most I learned from this video: Link for Lists

Removing from the List and the use of API

I am trying to make a program where permit's needs to be deleted from the user account. API's are used to pull up the user account and the permits. After the permits and restrictions are removed an event is created to confirm the deletion. Right now it's not giving me any output what so ever.
String User;
String License;
List<String> Permit;
public RemovePermit(String User, String license, List<String> Permit)
{
this.User = user;
this.License = license;
this.Permit = Permit;
}
public void Run()
{
List<PPermitGroups> permitGroupAdd = new List<PPermitGroups>();
using (var apiUser = new ApiUser())
{
var user = apiGetPerson(User);
var userPermit = apiGetPermitGroup(user.ID);
String license = License;
var deletePermitResult = userPermit.Where(x => Permit.Any(l => x.PermitGroup.Equals(l)|(!String.IsNullOrEmpty(x.Company)&& x.Company.Equals(l))));
foreach (PPermitGroups p in deletePermitResult)
{
apiDeletePermitAndRestrictions(p);
if(p.PermitGroupType == PPermitGroupsPermitGroupTypeEnum.ApplicationPermit)
if (p.LicenseType.Equals("Not Fixed") || p.LicenseType.Equals("Fixed"))
if (p.PermitGroup.StartsWith("Framework") || p.PermitGroup.StartsWith("Release"))
{
var temp = new UserEvent
EventType= " Delete"
License= ""
Permit = ""
}
api.CreateEvent(temp)

Select data from SQL Server in WPF app

Having problems with accessing what is inside my list in a wpf application of a mafia game I am creating.
Basically I read from SQL Server 2016, then add it to my user collection list. Later when I use my list in a display, they all are there.
However the moment I use a foreach to loop through it to set a temp user equal to a found username, it only finds hard coded users and not ones added using the data read from SQL Server. Need help.
SQL Server read code:
using (connect = new SqlConnection(connetionString))
{
connect.Open();
string readString = "select * from Users";
SqlCommand readCommand = new SqlCommand(readString, connect);
using (SqlDataReader dataRead = readCommand.ExecuteReader())
{
if (dataRead != null)
{
while (dataRead.Read())
{
tempEmail = dataRead["Email"].ToString();
tempName = dataRead["Name"].ToString();
UserCollection.addUser(tempEmail, tempName);
}
}
}
connect.Close();
}
UserCollection relevant parts
private static List<User> UserList = new List<User>();
// add a user
public static void addUser(string email, string name)
{
UserList.Add(new User(email, name, 0, "unset", false, false, false, false, false, false,
false, "", false, false, false, 0, 0));
}
//return list of users for use elsewhere
public static List<User> ReturnUserList()
{
return UserList;
}
Use of a list to set tempPlayer in a wpf window
PlayersList = UserCollection.ReturnUserList();
// tempPlayer = UserCollection.ReturnAUser(sessionUser);
foreach (var element in PlayersList)
{
if (element.UserName == sessionUser)
{
tempPlayer = element;
}
}
Example of code where the list works.
// set listing of current players
ListOfPlayers = UserCollection.ReturnUserList();
var tempList = from player in ListOfPlayers
where player.UserBlocked == false
select new
{
Name = player.UserName,
Email = player.UserEmail,
};
this.PlayerListBox.ItemsSource = tempList;
hard coded User add that works fine and can be found by foreach statement from my app.xaml.cs
UserCollection.addUser("g", "Tom");
Firstly, is there a reason why you need a static method to add users to a collection? Even if you need access to the list via a static accessor, you are better having a static property on the same class which you're using to read the DB
The following snippet should hopefully be of some help.
public class UserManagement {
//Static property
private static List<User> _users;
public static List<User> Users {
get {
if (_users == null) {
_user = new List<User>();
}
return _users;
}
set { }
}
//Static load method must be called before accessing Users
public static void LoadDBUsers() {
using (SqlConnection connection = new SqlConnection(connetionString)) {
connection.Open();
string readString = "select * from Users";
using (SqlCommand command = new SqlCommand(readString, connection)) {
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
String tempEmail = reader["Email"].ToString();
String tempName = reader["Name"].ToString();
User user = new User(tempEmail, tempName, 0, "unset", false, false, false, false, false, false, false, "", false, false, false, 0, 0));
users.Add(user);
}
}
}
}
}
}
To use from another class :
UserManagement.LoadDBUsers();
var dbUsers = UserManagement.Users;
If you have another list of users (say from a file), you could add a loader method to your class which will handle that for you.
If at some point you need to clear down the list of users, you can simply set it to a new instance...
UserManagement.Users = new List<User>();
A slightly better way to do this would be to remove the static property Users first, change the method definition to return a List<User> from the LoadDBUsers method - eg.
public static List<User> LoadDBUsers() {
List<User> Users = new List<User>();
//Handle the database logic as in the previous example..
//Then at the end of the method..
return Users;
}
and call as follows
List<User> dbUsers = UserManagement.LoadDBUsers();
Using the latter approach, you don't need to worry about multiple locations in your code maintaining a static property. Just call and assign it to a variable.
It also has the advantage that you will always get the most current list of users from the DB without having to clear down and reload the list before you access it from the static property.
An added advantage of not using a global static property is that it can avoid potential memory issues. Static properties can be difficult to dispose by the garbage collector if a reference to them is held open.
With instance variables, it's quite obvious when one goes out of scope and is not referenced anymore, but with static variables, the reference is sometimes not disposed until the program ends.
In many cases, this isn't an issue, but in larger systems it can be a pain to debug.
tempEmail = dataRead["Email"].ToString();
tempName = dataRead["Name"].ToString();
tempEmail,tempName you are declare above on
using (connect = new SqlConnection(connetionString))
the tempEmail and tempName are reference type so, if loop it's first record add after second time loop tempName and tempEmail values update also previously added so,because it's also pointing same memory. so the data's are duplicate record list so, the users cannot add in user properly.
so, you can change your code
var tempEmail = dataRead["Email"].ToString();
var tempName = dataRead["Name"].ToString();
and before tempEmail,tempName remove the declaration before using statement.
I Hope this is Helpful for you.
I believe your while loop was causing the issue. Since "using" was in effect, the global, assumed, variables "tempEmail" & "tempName" remained null. I tested the code using MySql on my end and this solution was effective.
private List<User> PlayersList;
public Tester()
{
using (SqlConnection connect = new SqlConnection(connectionString))
{
connect.Open();
string readString = "select * from user";
SqlCommand readCommand = new SqlCommand(readString, connect);
using (SqlDataReader dataRead = readCommand.ExecuteReader())
{
if (dataRead != null)
{
while (dataRead.Read())
{
string tempEmail = dataRead["Email"].ToString();
string tempName = dataRead["Name"].ToString();
UserCollection.addUser(tempEmail, tempName);
}
}
}
connect.Close();
}
PlayersList = UserCollection.ReturnUserList();
}
public class User
{
public string email;
public string name;
// A constructor that takes parameter to set the properties
public User(string e, string n)
{
email = e;
name = n;
}
}
public static class UserCollection
{
private static List<User> UserList = new List<User>();
// add a user
public static void addUser(string email, string name)
{
UserList.Add(new User(email, name));
}
//return list of users for use elsewhere
public static List<User> ReturnUserList()
{
return UserList;
}
}
This is the area of concern.
while (dataRead.Read())
{
//Set local variable during while loop
string tempEmail = dataRead["Email"].ToString();
string tempName = dataRead["Name"].ToString();
UserCollection.addUser(tempEmail, tempName);
}

dotMemory and tracking memory leaks

I have what I consider a basic web application MVC, EF6. The user has a dashboard that presents a table that contains data from two different database systems. MSSQL and Informix (Using IBM.Data.Informix).
Over time the IIS process just keeps eating away at the ram. I grabbed dotMemory to help me try to locate it but now trying to figure out how to read this data.
I left the web page open and every 10 seconds there is an Ajax call that returns the new data.
The 4th snapshot was taken a few hours after the 3rd one.
The total doesn't match the numbers below but something isn't as it should be.
The picture below seems to tell me I'm only using at most 10mb with my application.
I'm also still looking into the heap but it doesn't seem like this is where the big chunk is.
I'm still digging up videos and guides to help me figure out locating this issue. I'm using a lot of the built in framework and I really don't see an issue with the code I'm using unless there is a bug somewhere or I really am missing something I shouldn't be doing within the code.
DatabaseManager
public class DatabaseManager : IDisposable
{
private bool disposed = false;
private SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
private PatientCheckinEntities db { get; set; }
private IfxConnection conn { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public DatabaseManager()
{
string ifxString = System.Configuration.ConfigurationManager.ConnectionStrings["ifx"].ConnectionString;
conn = new IfxConnection(ifxString);
db = new PatientCheckinEntities();
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
IfxClose();
conn.Dispose();
db.Dispose();
}
disposed = true;
}
private void IfxClose()
{
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
}
private void IfxOpen()
{
if (conn.State == System.Data.ConnectionState.Closed)
{
conn.Open();
}
}
public ProviderModel GetProviderByResourceID(string id)
{
ProviderModel provider = new ProviderModel();
using (IfxDataAdapter ida = new IfxDataAdapter())
{
ida.SelectCommand = new IfxCommand("SELECT description FROM sch_resource WHERE resource_id = ? FOR READ ONLY", conn);
IfxParameter ifp1 = new IfxParameter("resource_id", IfxType.Char, 4);
ifp1.Value = id;
ida.SelectCommand.Parameters.Add(ifp1);
IfxOpen();
object obj = ida.SelectCommand.ExecuteScalar();
IfxClose();
if (obj != null)
{
string name = obj.ToString();
provider.ResourceID = id.ToString();
string[] split = name.Split(',');
if (split.Count() >= 2)
{
provider.LastName = split[0].Trim();
provider.FirstName = split[1].Trim();
}
else
{
provider.LastName = name.Trim();
}
ProviderPreference pp = db.ProviderPreferences.Where(x => x.ProviderID.Equals(id, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
int globalWait = Convert.ToInt32(GetConfigurationValue(ConfigurationSetting.WaitThreshold));
if (pp != null)
{
provider.Preferences.DisplayName = pp.DisplayName;
provider.Preferences.WaitThreshold = pp.WaitThreshold.HasValue ? pp.WaitThreshold.Value : globalWait;
}
else
{
provider.Preferences.WaitThreshold = globalWait;
}
}
}
return provider;
}
public List<PatientModel> GetCheckedInPatients(List<string> providers)
{
List<PatientModel> patients = new List<PatientModel>();
foreach (string provider in providers)
{
List<PatientModel> pats = db.PatientAppointments
.Where(x => provider.Contains(x.ProviderResourceID)
&& DbFunctions.TruncateTime(x.SelfCheckInDateTime) == DbFunctions.TruncateTime(DateTime.Now))
.Select(x => new PatientModel()
{
Appointment = new AppointmentModel()
{
ID = x.AppointmentID,
DateTime = x.AppointmentDateTime,
ArrivalTime = x.ExternalArrivedDateTime
},
FirstName = x.FirstName,
LastName = x.LastName,
SelfCheckIn = x.SelfCheckInDateTime,
Provider = new ProviderModel()
{
ResourceID = x.ProviderResourceID
}
}).ToList();
patients.AddRange(pats.Select(x => { x.Provider = GetProviderByResourceID(x.Provider.ResourceID); return x; }));
}
using (IfxDataAdapter ida = new IfxDataAdapter())
{
ida.SelectCommand = new IfxCommand("SELECT arrival_time::char(5) as arrival_time FROM sch_app_slot WHERE appointment_key = ? FOR READ ONLY", conn);
IfxOpen();
foreach (PatientModel patient in patients)
{
ida.SelectCommand.Parameters.Clear();
IfxParameter ifx1 = new IfxParameter("appointment_key", IfxType.Serial);
ifx1.Value = patient.Appointment.ID;
ida.SelectCommand.Parameters.Add(ifx1);
using (IfxDataReader dr = ida.SelectCommand.ExecuteReader())
{
while (dr.Read())
{
if (dr.HasRows)
{
string arrival = dr["arrival_time"].ToString();
if (!string.IsNullOrWhiteSpace(arrival) && !patient.Appointment.ArrivalTime.HasValue)
{
PatientAppointment pa = new PatientAppointment();
pa.AppointmentID = patient.Appointment.ID;
pa.AppointmentDateTime = patient.Appointment.DateTime;
pa.FirstName = patient.FirstName;
pa.LastName = patient.LastName;
string dt = string.Format("{0} {1}", patient.Appointment.DateTime.ToString("yyyy-MM-dd"), arrival);
pa.ExternalArrivedDateTime = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
patient.Appointment.ArrivalTime = pa.ExternalArrivedDateTime;
pa.ProviderResourceID = patient.Provider.ResourceID;
pa.SelfCheckInDateTime = patient.SelfCheckIn;
db.PatientAppointments.Attach(pa);
db.Entry(pa).State = EntityState.Modified;
db.SaveChanges();
}
}
}
}
}
IfxClose();
}
patients = patients.Select(x => { x.Appointment.WaitedMinutes = (int)Math.Round(((TimeSpan)x.Appointment.ArrivalTime.Value.Trim(TimeSpan.TicksPerMinute).Subtract(x.SelfCheckIn.Trim(TimeSpan.TicksPerMinute))).TotalMinutes); return x; }).ToList();
List<PatientModel> sorted = patients.Where(x => !x.Appointment.ArrivalTime.HasValue).OrderBy(x => x.SelfCheckIn).ThenBy(x => x.Provider.ResourceID).ToList();
sorted.AddRange(patients.Where(x => x.Appointment.ArrivalTime.HasValue).OrderBy(x => x.Appointment.DateTime).ThenBy(x => x.Provider.ResourceID));
return sorted;
}
private string GetConfigurationValue(string id)
{
return db.Configurations.Where(x => x.ID.Equals(id)).Select(x => x.Value).FirstOrDefault();
}
}
Controller
[HttpPost]
[Authorize]
public ActionResult GetCheckedIn(List<string> provider)
{
DashboardViewModel vm = new DashboardViewModel();
try
{
if (provider.Count > 0)
{
using (DatabaseManager db = new DatabaseManager())
{
vm.Patients = db.GetCheckedInPatients(provider);
}
}
}
catch (Exception ex)
{
//todo
}
return PartialView("~/Views/Dashboard/_InnerTable.cshtml", vm);
}
Your app consumes a lot of native memory, not .NET memory. Look at .NET memory consumption it's about 12Mb and does not grow intensively. It seems that you do not call Dispose method on some objects which uses native memory, e.g. database connection objects or something like that. Check a number of such objects if you does not release them the number will grow constantly.
I see you are using System.xml.Schema.... depending on the version, will create an unmanaged memory leak for each and every instance of about 80K. So, every 12 uses and you have a one meg memory leak in un-managed memory.
Instead of creating a new one each time, cache it if possible.

Creating objects from IDataReader

I have created the following to create User objects with an ObservableCollection of UserModule objects called UserModules.
I have approx. 100000 user records, each user could have up to 10 module records and this is taking minutes to finish.
This may mean changing from IDataReader, open to suggestion. Can someone suggest a more efficient way to do this?
public void LoadUsers()
{
clsDAL.SQLDBAccess db = new clsDAL.SQLDBAccess("USERS");
clsDAL.SQLDBAccess db_user_modules = new clsDAL.SQLDBAccess("USERS");
try
{
db.setCommandText(#"SELECT * FROM Users");
using (var reader = db.ExecuteReader())
{
while (reader.Read())
{
var user = new User();
MapUser(reader, user);
_users.Add(user);
db_user_modules.setCommandText(#"SELECT MODULE_ID, USER_MODULE_ACCESS FROM USER_MODULE_SECURITY Where USER_ID = " + user.User_ID);
using (var reader_user_modules = db_user_modules.ExecuteReader())
{
while (reader_user_modules.Read())
{
MapUserModule(reader_user_modules, user);
}
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
finally
{
db = null;
}
}
MapUser
public static void MapUser(IDataRecord record, User user)
{
try
{
user.User_ID = NullSafeGetter.GetValueOrDefault<int>(record, "USER_ID");
user.Username = NullSafeGetter.GetValueOrDefault<string>(record, "USERNAME");
user.Name = NullSafeGetter.GetValueOrDefault<string>(record, "NAME");
user.Job_Title = NullSafeGetter.GetValueOrDefault<string>(record, "JOB_TITLE");
user.Department = NullSafeGetter.GetValueOrDefault<string>(record, "DEPARTMENT");
user.Company = NullSafeGetter.GetValueOrDefault<string>(record, "COMPANY");
user.Phone_Office = NullSafeGetter.GetValueOrDefault<string>(record, "PHONE_OFFICE");
user.Phone_Mobile = NullSafeGetter.GetValueOrDefault<string>(record, "PHONE_MOBILE");
user.Email = NullSafeGetter.GetValueOrDefault<string>(record, "EMAIL");
user.Password = NullSafeGetter.GetValueOrDefault<string>(record, "PASSWORD");
user.Last_Login = NullSafeGetter.GetValueOrDefault<DateTime>(record, "LAST_LOGIN");
user.Status = NullSafeGetter.GetValueOrDefault<int>(record, "STATUS");
user.Session_Timeout = NullSafeGetter.GetValueOrDefault<int>(record, "SESSION_TIMEOUT");
}
catch (Exception ex)
{
MessageBox.Show("Mapping User error: " + ex.Message);
throw;
}
}
MapUserModule
private static void MapUserModule(IDataRecord record, User user)
{
try
{
int m_id = NullSafeGetter.GetValueOrDefault<int>(record, "MODULE_ID");
int uma = NullSafeGetter.GetValueOrDefault<int>(record, "USER_MODULE_ACCESS");
user.UserModules.Add(new Users.UserModule(user.User_ID, m_id, uma));
}
catch (Exception ex)
{
throw new Exception("Mapping UserModule error:\n" + ex.Message);
}
}
public IEnumerable<UserModule> GetUserModules()
{
using(var db = ....)
db.setCommandText("SELECT * FROM USERMODULES");
using (var reader = db.ExecuteReader())
{
while (reader.Read())
{
var userId = reader[...];
var m_id = reader[...];
var uma = reader[...];
yield return new UserModule (userid, m_id, uma)
}
}
}
public IEnumerable<User> GetUsers()
{
var userModulesLookup = GetUserModules().ToLookup(x => x.UserId);
using (var db = ...)
{
db.setCommandText("SELECT * FROM USERS");
using (var reader = db.ExecuteReader())
{
while (reader.Read())
{
var userId = reader["userId"];
...blah blah blah...
var user = return new User();
user.Modules = new ObservableCollection<UserModule>
(userModulesLookup[userId]);
...blah blah blah...
yield return user;
}
}
}
}
public void LoadUsers()
{
var users = GetUsers();
foreach(var u in users)
_users.Add(u);
}
As far as I know, there is no faster solution than using a DataReader.
I would recommend you profile the code to see what is taking up most of the time. IIRC, adding a large number of items to an observable collection one at a time is slow. Try adding them to a List<> temporarily to try and isolate the problem.

Categories