I am updating some code to allow us to implement unit tests. What I have so far is a Business Layer which calls the repository to get data from the database.
Business Layer example:
public class ConversationLogic
{
private IConversationData _conversationData { get; set; }
public ConversationLogic(IConversationData conversationData)
{
_conversationData = conversationData;
}
public ConversationListModel GetConversations(int loginID)
{
ConversationListModel model = new ConversationListModel();
dynamic list = _conversationData.GetConversations(loginID);
foreach (var item in list)
{
ConversationModel conversation = ConvertConversation(item);
model.Conversations.Add(conversation);
}
return model;
}
}
This allows an Interface of ConversationData to be passed in, this ConversationData is the repository layer which calls the database.
Conversation Data repository example:
public class ConversationData : BaseMassiveTable, IConversationData
{
public dynamic GetConversations(int loginID)
{
string sql = GetResourceFile("Project.SQL.GetConversations.sql", Assembly.GetExecutingAssembly());
dynamic result = Query(sql, loginID).ToList();
return result;
}
}
The ConversationData inherits from the class BaseMassive, which implements IDisposable:
public class BaseMassiveTable : DynamicModel, IDisposable
{
string connectionName;
public BaseMassiveTable(string connectionStringName, string tableName, string primaryKeyColumn) :
base(connectionStringName, tableName, primaryKeyColumn)
{
connectionName = connectionStringName;
}
public void Dispose()
{
}
protected void WriteErrorToFile(int userId, string url, Exception ex)
{
try
{
string path = "~/Error/" + DateTime.Today.ToString("yyyy-MM-dd") + ".txt";
if (!File.Exists(System.Web.HttpContext.Current.Server.MapPath(path)))
{
File.Create(System.Web.HttpContext.Current.Server.MapPath(path)).Close();
}
using (StreamWriter w = File.AppendText(System.Web.HttpContext.Current.Server.MapPath(path)))
{
w.WriteLine("\r\nLog Entry : ");
w.WriteLine("{0}", DateTime.Now.ToString(CultureInfo.InvariantCulture));
string err = "Error in: " + url +
". Error Message:" + ex.Message;
w.WriteLine(err);
w.WriteLine(ex.StackTrace.ToString());
w.WriteLine("__________________________");
w.Flush();
w.Close();
}
}
catch
{
//What do we do here ????
}
}
public string Truncate( string stringForTuncation, int maxLength)
{
string result = "";
if (stringForTuncation == null)
{
result = null;
}
else if (stringForTuncation.Length <= maxLength)
{
result = stringForTuncation;
}
else
{
result = stringForTuncation.Substring(0, maxLength);
}
return result;
}
public static string GetResourceFile(string name, Assembly assembly)
{
using (Stream stream = assembly.GetManifestResourceStream(name))
{
using (StreamReader reader = new StreamReader(stream))
{
string result = reader.ReadToEnd();
return result;
}
}
}
}
As you may seen in my implementation of ConversationLogic I cannot wrap the IConversationData interface in a using as this does not work with the current implementation.
What you will also see that in ConversationData.GetConversations(), this calls the Massive ORM Query(), which looks like this:
public virtual IEnumerable<dynamic> Query(string sql, params object[] args)
{
using (var conn = OpenConnection())
{
var rdr = CreateCommand(sql, conn, args).ExecuteReader();
while (rdr.Read())
{
yield return rdr.RecordToExpando(); ;
}
}
}
What I am looking for is that, even though ConversationData inherits from BaseMassive, which implements IDisposable, do I actually need to wrap any calls using IConversationData in using statements, as every call to the repository calls the Massive Query() statement, which manages its own resources by creating a using() statement on any query?
Apologies for the long-winded question, if you need any clarifications, just let me know.
Best Answer
The caller should use
using
; you need to implementDispose
in order for him to be able to. Then theDispose
calls will cascade and everything will get released in due course.The class into which the disposable resource is injected should have its own
Dispose
method and should release the resources there. Then you need to make sure that whoever is using theConversationLogic
class is also disposing it, perhaps with ausing
clause of their own.For example, if your
ConversationLogic
class is injected into an MVC controller, you can take advantage of the fact that the base Controller class already has a Dispose method. You just need to override it to Dispose your ConversationLogic, which will in turn release your resource-intensive service. The .NET pipeline will call the controller'sDispose
method at the right time.Sometimes you're not sure if the injected resource implements IDisposable. It is easy to check.
If this pattern bothers you (because you're a purist, and you noticed that the code that created the resource didn't dispose it) you have two options:
Get over it, because the code that created the resource was the IoC container, and it would require something really weird for the IoC container to deal with the disposal, or
Don't inject the disposable instance; inject a factory instead. That way your class can both create and dispose of the object and make everyone happy.
Either way, you don't use
using
, you use theDispose
override per the conventional pattern, and ensure that your controller (or other object) is itself enclosed in ausing
clause so thatDispose
gets called.