I am trying to use AppFabric for fasten my image retrieval from SQL Database. I created my provider file and load it to my cache. However, I am struggling right now.
How can I call get function from cache using my provider file or do I need to use my provider file while retrieving data?
When I call .Get(key.Key), do I need to see the data coming from my database?
My Read method from provider is as follows, is this correct?
public override DataCacheItem Read(DataCacheItemKey key)
{
try
{
Object retrievedValue = null;
DataCacheItem cacheItem;
retrievedValue = *Running SQL Query Retrieving Image from DB*;//Is that a correct approach?
if (retrievedValue == null)
cacheItem = null;
else
cacheItem = DataCacheItemFactory.GetCacheItem(key, cacheName, retrievedValue, null);
return cacheItem;
}
catch
{
return null;
}
}
Example:>
using Microsoft.ApplicationServer.Caching;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
namespace SampleProvider
{
public class Provider : DataCacheStoreProvider
{
private readonly String dataCacheName;
private readonly Dictionary<string, string> config;
public Provider(string cacheName, Dictionary<string, string> config)
{
this.dataCacheName = cacheName; //Store the cache name for future use
this.config = config;
}
public override DataCacheItem Read(DataCacheItemKey key)
{
Object retrievedValue = null;
DataCacheItem cacheItem;
retrievedValue = ReadFromDatabase(key.Key); //Your implemented method that searches in the backend store based
if (retrievedValue == null)
cacheItem = null;
else
cacheItem = DataCacheItemFactory.GetCacheItem(key, dataCacheName, retrievedValue, null);
return cacheItem;
}
public override void Read(System.Collections.ObjectModel.ReadOnlyCollection<DataCacheItemKey> keys, IDictionary<DataCacheItemKey, DataCacheItem> items)
{
foreach (var key in keys)
{
items[key] = Read(key);
}
}
public override void Delete(System.Collections.ObjectModel.Collection<DataCacheItemKey> keys) { }
public override void Delete(DataCacheItemKey key) { }
protected override void Dispose(bool disposing) { }
public override void Write(IDictionary<DataCacheItemKey, DataCacheItem> items) { }
public override void Write(DataCacheItem item) { }
private string ReadFromDatabase(string key)
{
string value = string.Empty;
object retrievedValue = null;
using (SqlConnection connection = new SqlConnection(config["DBConnection"]))
{
SqlCommand cmd = new SqlCommand();
cmd.CommandText = string.Format("select Value from KeyValueStore where [Key] = '{0}'", key);
cmd.Connection = connection;
connection.Open();
retrievedValue = cmd.ExecuteScalar();
if (retrievedValue != null)
{
value = retrievedValue.ToString();
}
}
return value;
}
}
}
Related
I have created a class UserInfo and I have set values on user login response, now I don't know how to access those values in another part of my app (another form).
Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MYCUSTOMAPP
{
class UserInfo
{
public string userId;
public string userType;
public string userName;
public string userUserName;
public string userPhone;
public string userIdCard;
}
}
Login (set values to class stings)
//....
SqlDataReader result = cmd.ExecuteReader();
if (result.HasRows)
{
while (result.Read())
{
UserInfo loggedUser = new UserInfo();
loggedUser.userId = result.GetValue(result.GetOrdinal("Id")).ToString();
loggedUser.userName = result.GetValue(result.GetOrdinal("Name")).ToString();
loggedUser.userType = result.GetValue(result.GetOrdinal("UserType")).ToString();
loggedUser.userUserName = result.GetValue(result.GetOrdinal("UserName")).ToString();
loggedUser.userPhone = result.GetValue(result.GetOrdinal("Phone")).ToString();
loggedUser.userIdCard = result.GetValue(result.GetOrdinal("IdCard")).ToString();
}
}
// the rest...
Question
Now let say I am in MainWindow form, how can I get value of userId for instance?
Update
Logic
User login
Store user data in class (or anything else that you might suggest)
Get those user data globally in my app so I don't need to call database each time I need user info (it will be presented already)
Update 2
My login function
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["MYCUSTOMAPPDatabaseString"].ConnectionString))
{
if (cn.State == ConnectionState.Closed)
cn.Open();
using (DataTable dt = new DataTable())
{
using (SqlCommand cmd = new SqlCommand("dbo.LoginUser", cn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#UserName", usernameBox.Text);
cmd.Parameters.AddWithValue("#Password", passwordBox.Text);
SqlDataReader result = cmd.ExecuteReader();
if (result.HasRows)
{
while (result.Read())
{
// Name of logged user from database (this is database response)
Console.WriteLine(result.GetValue(result.GetOrdinal("Name")));
// Add logged user info to `UserInfo` class
UserInfo loggedUser = new UserInfo();
loggedUser.userId = result.GetValue(result.GetOrdinal("Id")).ToString();
loggedUser.userName = result.GetValue(result.GetOrdinal("Name")).ToString();
loggedUser.userType = result.GetValue(result.GetOrdinal("UserType")).ToString();
loggedUser.userUserName = result.GetValue(result.GetOrdinal("UserName")).ToString();
loggedUser.userPhone = result.GetValue(result.GetOrdinal("Phone")).ToString();
loggedUser.userIdCard = result.GetValue(result.GetOrdinal("IdCard")).ToString();
MainWindow mainWindow = new MainWindow();
this.Hide();
mainWindow.ShowDialog();
Show();
}
}
else
{
cn.Close();
MessageBox.Show("Your credentials are not match!", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
I think you can pass it as the parameters when you call another form's constructor, if you directly call that form. Like in this case : Communicate between two windows forms in C#
However, if you need to load it later on, you can either store it as user settings: https://www.youtube.com/watch?v=P432z8q9iVE. Or even more structural approach is store the data in database like MySQL, MSSQL, or another database that you can put when user click the call to action button.
Solved
I have changed my class to the following
class UserInfo
{
private string userId1;
private static string userType1;
private static string userName1;
private static string userUserName1;
private static string userPhone1;
private static string userIdCard1;
public string GetuserId()
{
return userId1;
}
public void SetuserId(string value)
{
userId1 = value;
}
public string GetuserType()
{
return userType1;
}
public void SetuserType(string value)
{
userType1 = value;
}
public string GetuserName()
{
return userName1;
}
public void SetuserName(string value)
{
userName1 = value;
}
public string GetuserUserName()
{
return userUserName1;
}
public void SetuserUserName(string value)
{
userUserName1 = value;
}
public string GetuserPhone()
{
return userPhone1;
}
public void SetuserPhone(string value)
{
userPhone1 = value;
}
public string GetuserIdCard()
{
return userIdCard1;
}
public void SetuserIdCard(string value)
{
userIdCard1 = value;
}
}
And changed my code in login response to:
while (result.Read())
{
UserInfo loggedUser = new UserInfo();
loggedUser.SetuserId(result.GetValue(result.GetOrdinal("Id")).ToString());
loggedUser.SetuserName(result.GetValue(result.GetOrdinal("Name")).ToString());
loggedUser.SetuserType(result.GetValue(result.GetOrdinal("UserType")).ToString());
loggedUser.SetuserUserName(result.GetValue(result.GetOrdinal("UserName")).ToString());
loggedUser.SetuserPhone(result.GetValue(result.GetOrdinal("Phone")).ToString());
loggedUser.SetuserIdCard(result.GetValue(result.GetOrdinal("IdCard")).ToString());
}
Now I can get my values in any form like:
UserInfo loggedUser = new UserInfo();
loggedUser.GetuserName(); // will return logged user name
See my repository code
public abstract class AdoRepository<T> where T : class
{
private static SqlConnection _connection;
public AdoRepository(string connectionString)
{
_connection = new SqlConnection(connectionString);
}
public virtual T PopulateRecord(SqlDataReader reader)
{
return null;
}
public virtual void GetDataCount(int count)
{
}
protected IEnumerable<T> GetRecords(SqlCommand command)
{
var list = new List<T>();
command.Connection = _connection;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
list.Add(PopulateRecord(reader));
}
reader.NextResult();
if (reader.HasRows)
{
while (reader.Read())
{
GetDataCount(Convert.ToInt32(reader["Count"].ToString()));
}
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
catch(Exception ex)
{
string Msg = ex.Message;
}
finally
{
_connection.Close();
}
return list;
}
protected T GetRecord(SqlCommand command)
{
T record = null;
command.Connection = _connection;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
record = PopulateRecord(reader);
break;
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
finally
{
_connection.Close();
}
return record;
}
protected IEnumerable<T> ExecuteStoredProc(SqlCommand command)
{
var list = new List<T>();
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
var record = PopulateRecord(reader);
if (record != null) list.Add(record);
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
finally
{
_connection.Close();
}
return list;
}
}
public class StudentRepository : AdoRepository<Student>
{
public int DataCounter { get; set; }
public StudentRepository(string connectionString)
: base(connectionString)
{
}
public IEnumerable<Student> GetAll()
{
// DBAs across the country are having strokes
// over this next command!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents"))
{
return GetRecords(command);
}
}
public Student GetById(string id)
{
// PARAMETERIZED QUERIES!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents WHERE Id = #id"))
{
command.Parameters.Add(new ObjectParameter("id", id));
return GetRecord(command);
}
}
public IEnumerable<Student> GetStudents(int StartIndex, int EndIndex, string sortCol, string sortOrder)
{
string strSQL = "SELECT * FROM vwListStudents WHERE ID >=" + StartIndex + " AND ID <=" + EndIndex;
strSQL += " ORDER BY " + sortCol + " " + sortOrder;
strSQL += ";SELECT COUNT(*) AS Count FROM vwListStudents";
var command = new SqlCommand(strSQL);
return GetRecords(command);
}
public override Student PopulateRecord(SqlDataReader reader)
{
return new Student
{
ID = Convert.ToInt32(reader["ID"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
IsActive = Convert.ToBoolean(reader["IsActive"]),
StateName = reader["StateName"].ToString(),
CityName = reader["CityName"].ToString()
};
}
public override void GetDataCount(int count)
{
DataCounter = count;
}
}
this way unit of work code added
public class UnitOfWorkFactory
{
public static IUnitOfWork Create()
{
var connection = new SqlConnection(ConfigurationManager.ConnectionStrings("MyDb").ConnectionString);
connection.Open();
return new AdoNetUnitOfWork(connection, true);
}
}
public class AdoNetUnitOfWork : IUnitOfWork
{
public AdoNetUnitOfWork(IDbConnection connection, bool ownsConnection)
{
_connection = connection;
_ownsConnection=ownsConnection;
_transaction = connection.BeginTransaction();
}
public IDbCommand CreateCommand()
{
var command = _connection.CreateCommand();
command.Transaction = _transaction;
return command;
}
public void SaveChanges()
{
if (_transaction == null)
throw new InvalidOperationException("Transaction have already been commited. Check your transaction handling.");
_transaction.Commit();
_transaction = null;
}
public void Dispose()
{
if (_transaction != null)
{
_transaction.Rollback();
_transaction = null;
}
if (_connection != null && _ownsConnection)
{
_connection.Close();
_connection = null;
}
}
}
using (var uow = UnitOfWorkFactory.Create())
{
var repos = new UserRepository(uow);
uow.SaveChanges();
}
public class UserRepository
{
private AdoNetUnitOfWork _unitOfWork;
public UserRepository(IUnitOfWork uow)
{
if (uow == null)
throw new ArgumentNullException("uow");
_unitOfWork = uow as AdoNetUnitOfWork;
if (_unitOfWork == null)
throw new NotSupportedException("Ohh my, change that UnitOfWorkFactory, will you?");
}
public User Get(Guid id)
{
using (var cmd = _unitOfWork.CreateCommand())
{
cmd.CommandText = "SELECT * FROM Users WHERE Id = #id");
cmd.AddParameter("id", id);
// uses an extension method which I will demonstrate in a
// blog post in a couple of days
return cmd.FirstOrDefault<User>();
}
}
}
but i want to add unit of work code in my repository class. may be in AdoRepository class or StudentRepository class as a result i do not have to write the below code again and again.
using (var uow = UnitOfWorkFactory.Create())
{
var repos = new UserRepository(uow);
uow.SaveChanges();
}
so when i will be working with any repository like UserRepository then i have to repeat again and again this lines of code UnitOfWorkFactory.Create() which i do not want rather i am looking for a way to embed unit of work code in some where centralize as a result i could reduce the repeated code.
so looking for idea and suggestion. if possible give me modified version of code.
thanks
I will be straightforward about this:
A UoW that contains repositories is an anti pattern at least if DDD is involved. If it isn't, then you're dealing with a very simple app so make you life easier and use a ORM which implements what you want.
As a thumb rule, a repository may contain a UoW as an implementation detail and you should be certain that you really need a repository. A repository inside a UoW is a very specific case which is valid ONLY with CRUD apps and you'll just be reinventing a very small part of an ORM.
Using ado.net directly is wasting time (and money). Either use an ORM or a data mapper aka micro-ORM. With the latter option the UoW is the Db transaction.
You don't gain anything significant using Ado.Net,the performance gain is so small it's not even funny (I know that because I've benchmarked it). The only thing you'll get is longer devel time and human errors.
Actually, if I take a better look at your code, you're basically building a micro ORM, without knowing it. It's still not a repository though, because the abstraction doesn't have business semantics and it's too tightly coupled to an implementation detail.
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 get a weird behavior with NHibernate with Fluent Configuration.
Whenever a generic exception unrelated to the NHibernate occurs i.e. in the view a DivideByZeroException every request after the exception throws.
An exception of type 'NHibernate.LazyInitializationException' occurred in NHibernate.dll but was not handled in user code. Additional information: Initializing[Entity]-Could not initialize proxy - no Session.
Due to nature of the bug the bug is critical due to the fact that 1 user can make the whole website dead if he generates an exception
Following it is my HttpModule for Nhibernate with Asp.Net MVC 5 that takes care of sessions.
NHibernateSessionPerRequest.cs
public class NHibernateSessionPerRequest : IHttpModule
{
private static readonly ISessionFactory SessionFactory;
// Constructs our HTTP module
static NHibernateSessionPerRequest()
{
SessionFactory = CreateSessionFactory();
}
// Initializes the HTTP module
public void Init(HttpApplication context)
{
context.BeginRequest += BeginRequest;
context.EndRequest += EndRequest;
}
// Disposes the HTTP module
public void Dispose() { }
// Returns the current session
public static ISession GetCurrentSession()
{
return SessionFactory.GetCurrentSession();
}
// Opens the session, begins the transaction, and binds the session
private static void BeginRequest(object sender, EventArgs e)
{
ISession session = SessionFactory.OpenSession();
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
// Unbinds the session, commits the transaction, and closes the session
private static void EndRequest(object sender, EventArgs e)
{
ISession session = CurrentSessionContext.Unbind(SessionFactory);
if (session == null) return;
try
{
session.Transaction.Commit();
}
catch (Exception)
{
session.Transaction.Rollback();
throw;
}
finally
{
session.Close();
session.Dispose();
}
}
// Returns our session factory
private static ISessionFactory CreateSessionFactory()
{
if (HttpContext.Current != null) //for the web apps
_configFile = HttpContext.Current.Server.MapPath(
string.Format("~/App_Data/{0}", CacheFile)
);
_configuration = LoadConfigurationFromFile();
if (_configuration == null)
{
FluentlyConfigure();
SaveConfigurationToFile(_configuration);
}
if (_configuration != null) return _configuration.BuildSessionFactory();
return null;
}
// Returns our database configuration
private static MsSqlConfiguration CreateDbConfigDebug2()
{
return MsSqlConfiguration
.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("MyConnection"));
}
// Updates the database schema if there are any changes to the model,
// or drops and creates it if it doesn't exist
private static void UpdateSchema(Configuration cfg)
{
new SchemaUpdate(cfg)
.Execute(false, true);
}
private static void SaveConfigurationToFile(Configuration configuration)
{
using (var file = File.Open(_configFile, FileMode.Create))
{
var bf = new BinaryFormatter();
bf.Serialize(file, configuration);
}
}
private static Configuration LoadConfigurationFromFile()
{
if (IsConfigurationFileValid == false)
return null;
try
{
using (var file = File.Open(_configFile, FileMode.Open))
{
var bf = new BinaryFormatter();
return bf.Deserialize(file) as Configuration;
}
}
catch (Exception)
{
return null;
}
}
private static void FluentlyConfigure()
{
if (_configuration == null)
{
_configuration = Fluently.Configure()
.Database(CreateDbConfigDebug2)
.CurrentSessionContext<WebSessionContext>()
.Cache(c => c.ProviderClass<SysCacheProvider>().UseQueryCache())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<EntityMap>()
.Conventions.Add(DefaultCascade.All(), DefaultLazy.Always()))
.ExposeConfiguration(UpdateSchema)
.ExposeConfiguration(c => c.Properties.Add("cache.use_second_level_cache", "true"))
.BuildConfiguration();
}
}
private static bool IsConfigurationFileValid
{
get
{
var ass = Assembly.GetAssembly(typeof(EntityMap));
var configInfo = new FileInfo(_configFile);
var assInfo = new FileInfo(ass.Location);
return configInfo.LastWriteTime >= assInfo.LastWriteTime;
}
}
private static Configuration _configuration;
private static string _configFile;
private const string CacheFile = "hibernate.cfg.xml";
}
Edit
The Repository Implementation i use
public class Repository<T> : IIntKeyedRepository<T> where T : class
{
private readonly ISession _session;
public Repository()
{
_session = NHibernateSessionPerRequest.GetCurrentSession();
}
#region IRepository<T> Members
public bool Add(T entity)
{
_session.Save(entity);
return true;
}
public bool Add(System.Collections.Generic.IEnumerable<T> items)
{
foreach (T item in items)
{
_session.Save(item);
}
return true;
}
public bool Update(T entity)
{
_session.Update(entity);
return true;
}
public bool Delete(T entity)
{
_session.Delete(entity);
return true;
}
public bool Delete(System.Collections.Generic.IEnumerable<T> entities)
{
foreach (T entity in entities)
{
_session.Delete(entity);
}
return true;
}
#endregion
#region IIntKeyedRepository<T> Members
public T FindBy(int id)
{
return _session.Get<T>(id);
}
#endregion
#region IReadOnlyRepository<T> Members
public IQueryable<T> All()
{
return _session.Query<T>();
}
public T FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
return FilterBy(expression).Single();
}
public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
{
return All().Where(expression).AsQueryable();
}
#endregion
}
Edit 2
The base controller class I use
public class BaseController : Controller
{
private readonly IRepository<UserEntity> _userRepository;
public BaseController()
{
_userRepository = new Repository<UserEntity>();
BaseModel = new LayoutModel {Modals = new List<string>()};
}
public UserEntity LoggedUser { get; set; }
public LayoutModel BaseModel { get; set; }
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
if (HttpContext.User.Identity.IsAuthenticated)
{
if (Session != null && Session["User"] != null)
{
LoggedUser = (User) Session["User"];
}
var curUsername = HttpContext.User.Identity.Name;
if (LoggedUser == null || LoggedUser.Entity2.un!= curUsername)
{
LoggedUser = _userRepository.FindBy(u => u.Entity2.un== curUsername);
Session["User"] = LoggedUser;
}
BaseModel.LoggedUser = LoggedUser;
BaseModel.Authenticated = true;
}
else
{
LoggedUser = new UserEntity
{
Entity= new Entity{un= "Guest"},
};
BaseModel.LoggedUser = LoggedUser;
}
}
}
The extended question and all the snippets - are finally helping to find out where is the issue.
There is a really big issue: Session["User"] = LoggedUser;
This would hardly work. Why?
because we place into long running object (Web Session)
an instance loaded via very shortly lasting Web Request
Not all its properties will/could be loaded, When we place LoggedUser into session. It could be just a root entity with many proxies representing references and collections. These will NEVER be loaded later, because its Mather session is closed... gone
Solution?
I would use .Clone() of the User object. In its implementation we can explicitly load all needed references and collections and clone them as well. Such object could be placed into the Web Session
[Serializable]
public class User, ICloneable, ...
{
...
public override object Clone()
{
var entity = base.Clone() as User;
entity.Role = Role.Clone() as Role;
...
return entity;
}
So, what would be placed into session?
Session["User"] = LoggedUser.Clone();
As Radim Köhler noted i was saving a lazy-loaded object in Session that caused the problem.
But i wanted to avoid the Serilization of all objects and i fixed it as follows.
I added the following method to eager-load an entity instead of lazy
public T FindByEager(int id)
{
T entity = FindBy(id);
NHibernateUtil.Initialize(entity);
return entity;
}
And changed BaseController to
if (Session != null) Session["User"] = userRepository.FindByEager(LoggedUser.Id);
I have a common database class for my application and in that class i have a function
public MySqlDataReader getRecord(string query)
{
MySqlDataReader reader;
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
reader = cmd.ExecuteReader();
return reader;
}
}
return null;
}
and on my code behind pages I use
String sql = "SELECT * FROM `table`";
MySqlDataReader dr = objDB.getRecord(sql);
if (dr.Read())
{
// some code goes hear
}
and I am having error as Invalid attempt to Read when reader is closed.
I know access the reader after the database connection is closed is not possible
bot I am looking for a work around in which I need not to change in the codebehind
EDIT: I WILL LIKE THE SOLUTION IN WHICH THE READER IS ASSIGNED TO OTHER OBJECT (SIMILAR TO READER ) AND THEN RETURN THAT OBJECT so i need not to change in all the application pages
You can load the results of your query to memory, then close the connection and still return an IDataReader that works as expected. Note that this costs memory.
public IDataReader getRecord(string query)
{
MySqlDataReader reader;
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
reader = cmd.ExecuteReader();
var dt = new DataTable();
dt.Load( reader );
return dt.CreateDataReader();
}
}
return null;
}
In the callers:
String sql = "SELECT * FROM `table`";
var dr = objDB.getRecord(sql); // or DataTableReader dr = ...
if (dr.Read())
{
// some code goes here
}
When the scope of your call to using (var connection = new MySqlConnection(connectionString))
ends, the connection will be closed.
However, you are still returning a reader based on that connection. Once you try to use it in your caller method, you will get the error as closed connection can't be used.
Besides, your method is called GetRecord but it returns a reader.
One of the options is to do this:
public void processQuery(string query, Action<MySqlDataReader> fn)
{
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
using (var reader = cmd.ExecuteReader())
{
fn(reader);
}
}
}
}
// caller
String sql = "SELECT * FROM `table`";
objDB.procesQuery(sql, dr => {
if (dr.Read())
{
// some code goes here
}
});
Your idea of creating an object 'similar to reader', so you don't have to change the caller, would not work: the returned object would need to contain both reader and an open connection, so that you can use the reader. This means you would have to close the connection in the caller. In best case, the caller would need to be modified as follows:
String sql = "SELECT * FROM `table`";
using (MyWrapper wr = objDB.getRecord(sql))
{
if (wr.Reader.Read())
{
// some code goes here
}
}
You will not save that much work, but one missing using statement in the caller will result in your app not working after some time due to a connection leak.
What you want is possible, but it is not a nice solution, because you have to wrap all the functions of the MySqlDataReader class and forward it to the real MySqlDataReader.
See the ConnectedMySqlDataReader class (hint: it does not implement all functions of MySqlDataReader, if you really want to use it, you have to do it yourself) and how it would fit in your solution:
public ConnectedMySqlDataReader GetRecord(string query)
{
return new ConnectedMySqlDataReader(connectionString, query);
}
// ...
var sql = "SELECT * FROM `table`";
using(var dr = objDB.GetRecord(sql))
{
if (dr.Read())
{
// some code goes hear
}
}
i have not tested this class, it is just for demonstration!
using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using MySql.Data.MySqlClient;
namespace MySqlTest
{
public class ConnectedMySqlDataReader : DbDataReader
{
private readonly MySqlConnection _connection;
private readonly Lazy<MySqlDataReader> _reader;
private MySqlCommand _command;
public ConnectedMySqlDataReader(MySqlConnection connection, string query)
{
if(connection == null)
throw new ArgumentNullException("connection");
_connection = connection;
_reader = new Lazy<MySqlDataReader>(() =>
{
_connection.Open();
_command = new MySqlCommand(query, _connection);
return _command.ExecuteReader();
});
}
public ConnectedMySqlDataReader(string connectionString, string query)
: this(new MySqlConnection(connectionString), query) { }
private MySqlDataReader Reader
{
get { return _reader.Value; }
}
public override void Close()
{
if (_reader.IsValueCreated)
_reader.Value.Close();
if(_command != null)
_command.Dispose();
_connection.Dispose();
}
public override DataTable GetSchemaTable()
{
return this.Reader.GetSchemaTable();
}
public override bool NextResult()
{
return this.Reader.NextResult();
}
public override bool Read()
{
return this.Reader.Read();
}
public override int Depth
{
get { return this.Reader.Depth; }
}
public override bool IsClosed
{
get { return this.Reader.IsClosed; }
}
public override int RecordsAffected
{
get { return this.Reader.RecordsAffected; }
}
public override bool GetBoolean(int ordinal)
{
return this.Reader.GetBoolean(ordinal);
}
public override byte GetByte(int ordinal)
{
return this.Reader.GetByte(ordinal);
}
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
{
return this.Reader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override char GetChar(int ordinal)
{
return this.Reader.GetChar(ordinal);
}
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
{
return this.Reader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override Guid GetGuid(int ordinal)
{
return this.Reader.GetGuid(ordinal);
}
public override short GetInt16(int ordinal)
{
return this.Reader.GetInt16(ordinal);
}
public override int GetInt32(int ordinal)
{
return this.Reader.GetInt32(ordinal);
}
public override long GetInt64(int ordinal)
{
return this.Reader.GetInt64(ordinal);
}
public override DateTime GetDateTime(int ordinal)
{
return this.Reader.GetDateTime(ordinal);
}
public override string GetString(int ordinal)
{
return this.Reader.GetString(ordinal);
}
public override object GetValue(int ordinal)
{
return this.Reader.GetValue(ordinal);
}
public override int GetValues(object[] values)
{
return this.Reader.GetValues(values);
}
public override bool IsDBNull(int ordinal)
{
return this.Reader.IsDBNull(ordinal);
}
public override int FieldCount
{
get { return this.Reader.FieldCount; }
}
public override object this[int ordinal]
{
get { return this.Reader[ordinal]; }
}
public override object this[string name]
{
get { return this.Reader[name]; }
}
public override bool HasRows
{
get { return this.Reader.HasRows; }
}
public override decimal GetDecimal(int ordinal)
{
return this.Reader.GetDecimal(ordinal);
}
public override double GetDouble(int ordinal)
{
return this.Reader.GetDouble(ordinal);
}
public override float GetFloat(int ordinal)
{
return this.Reader.GetFloat(ordinal);
}
public override string GetName(int ordinal)
{
return this.Reader.GetName(ordinal);
}
public override int GetOrdinal(string name)
{
return this.Reader.GetOrdinal(name);
}
public override string GetDataTypeName(int ordinal)
{
return this.Reader.GetDataTypeName(ordinal);
}
public override Type GetFieldType(int ordinal)
{
return this.Reader.GetFieldType(ordinal);
}
public override IEnumerator GetEnumerator()
{
return this.Reader.GetEnumerator();
}
}
}
PS: this is what sealed in c# context means, and yes, MySqlDataReader is sealed.