Can you see anything wrong with this ASP.NET snippet?

It doesn’t happen often but from time to time I’ll get an exception report emailed to me pointing to this bit of code. I’m wondering if you see anything wrong with it:

namespace DomainModel.Concrete
{
	public class ConfigRepository : IConfigRepository
	{
		static mvCmsContext context { get; set; }
		public ConfigRepository() { context = new mvCmsContext(); }

		private static Func<mvCmsContext, string, Configuration> _byName =
			CompiledQuery.Compile((mvCmsContext context, string configName) =>
				(from c in context.Configs
				 where c.configName == configName
				 select c).SingleOrDefault());
		static public Configuration ByName(string configName)
		{
			var result = (Configuration)HttpContext.Current.Cache.Get(configName);
			if (result == null)
			{
				using (new mvCmsContext())
				{
					HttpContext.Current.Cache.Insert(configName, _byName(context, configName));
					result = (Configuration)HttpContext.Current.Cache.Get(configName);
				}
			}
			return result;
		}

	}
}

Here is the service calling that method:

public class ConfigService
{
	public static string siteName
	{
		get { return ConfigRepository.ByName("Site_Name").configValue; }
	}
	public static string copyright
	{
		get { return ConfigRepository.ByName("Copyright").configValue; }
	}
	public static string companyName
	{
		get { return ConfigRepository.ByName("Company_Name").configValue; }
	}
	public static string homeTitle
	{
		get { return ConfigRepository.ByName("Home_Title").configValue; }
	}
	public static string contactEmail
	{
		get { return ConfigRepository.ByName("Contact_Email").configValue; }
	}
	public static string physicalAddress
	{
		get { return ConfigRepository.ByName("Physical_Address").configValue; }
	}
	public static string phoneNumber
	{
		get { return ConfigRepository.ByName("Phone_Number").configValue; }
	}
}

Here is the report received:

** Summary **
---------------
This message contains events 1 to 1 from the total of 1 events scheduled for this notification.  There were 0 events left in the buffer at the beginning of this notification.


** Application Information **
---------------
Application domain: /LM/W3SVC/66/ROOT-7-129384226573152341
Trust level: Full
Application Virtual Path: /
Application Path: D:\\***\\***\\***.com\\
Machine name: WIN11


** Events **
---------------
Event code: 3005
Event message: An unhandled exception has occurred.
Event time: 1/2/2011 12:17:44 AM
Event time (UTC): 1/2/2011 6:17:44 AM
Event ID: f909c5c676bd4ca1ba21512c678ac502 Event sequence: 6 Event occurrence: 1 Event detail code: 0

Process information:
    Process ID: 26904
    Process name: w3wp.exe
    Account name: NT AUTHORITY\\NETWORK SERVICE

Exception information:
    Exception type: System.InvalidOperationException
    Exception message: Invalid operation. The connection is closed.

Request information:
    Request URL: http://***.com/article/***-ALERT
    Request path: /article/III-ALERT
    User host address: 68.230.129.53
    User: 
    Is authenticated: False
    Authentication Type: 
    Thread account name: NT AUTHORITY\\NETWORK SERVICE

Thread information:
    Thread ID: 6
    Thread account name: NT AUTHORITY\\NETWORK SERVICE
    Is impersonating: False
    Stack trace:    at System.Data.SqlClient.SqlConnection.GetOpenConnection()
   at System.Data.SqlClient.SqlConnection.get_HasLocalTransactionFromAPI()
   at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult)
   at System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries)
   at System.Data.Linq.SqlClient.SqlProvider.CompiledQuery.Execute(IProvider provider, Object[] arguments)
   at System.Data.Linq.CompiledQuery.ExecuteQuery(DataContext context, Object[] args)
   at System.Data.Linq.CompiledQuery.Invoke[TArg0,TArg1,TResult](TArg0 arg0, TArg1 arg1)
   at DomainModel.Concrete.ConfigRepository.ByName(String configName)
   at DomainModel.Services.ConfigService.get_companyName()
   at ASP.views_shared_site_master.__Render__control1(HtmlTextWriter __w, Control parameterContainer)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children)
   at System.Web.UI.Page.Render(HtmlTextWriter writer)
   at System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

If I were closing the datacontext at the wrong place it would fail all the time, wouldn’t it?

I think I may be going about this the wrong way though. That’s not surprising. :wink:

If you’re going for a single project solution, it really doesn’t matter. Most people break a solution into several projects though, giving each layer it’s own project.

I’ve been tracing the data locally using breakpoints and I just can’t get it to fail. Hmmm.

I’ve gotten the occasional connection closed error before. It happens, especially on hosted environments. Maybe the app pool was recycling at the time. Who knows.

The only thing I see “wrong” (a relative term) though is that your concrete repository implementation seems to be in your domain model library. Only the interface should reside there. But I’m nitpicking. =)

I was thinking the same thing and someone mentioned it may have something to do with the static on the datacontext. I changed it to this:

public class ConfigRepository : IConfigRepository
{
	private mvCmsContext context { get; set; }
	public ConfigRepository() { context = new mvCmsContext(); }

	private static Func<mvCmsContext, string, Configuration> _byName =
		CompiledQuery.Compile((mvCmsContext context, string configName) =>
			(from c in context.Configs
			 where c.configName == configName
			 select c).SingleOrDefault());
	static public Configuration ByName(string configName)
	{
		var result = (Configuration)HttpContext.Current.Cache.Get(configName);
		if (result == null)
		{
			using (var context = new mvCmsContext())
			{
				HttpContext.Current.Cache.Insert(configName, _byName(context, configName));
				result = (Configuration)HttpContext.Current.Cache.Get(configName);
			}
		}
		return result;
	}

I’ll see if that has any effect. :slight_smile:

Wait, what? Where should it be? :slight_smile:

Honestly, that’s up to you. Typically speaking though, the domain model is your entities, and repository interfaces (though some might argue even those don’t belong there, as they typically just define crud ops). Most will agree though your concrete repos should go in a separate persistence layer. The domain layer should be persistence ignorant.

I see what you mean but isn’t it just a name? The concept is still the same without moving it to a separate folder, right? :slight_smile: The folders are really just imaginary borders.

My bet is whatever connection you inserted into the cache with the query is closed/timed out/whatever so you get the exception you are seeing. Not sure why you are not caching the results anyhow – no need to execute the query every time.

Wait, I thought I was caching the results so that the query isn’t executed except to fill the cache. What am I doing wrong here?

First, I should say that that was the way I read the code – does not appear to be getting executed necessarily, at least the way I am reading it. That and your exception indicates something like this is the case.

In any case, I would use a tool like the sql trace utility that comes with sql server to see what is actually happening – you can pretty easily filter for the sorts of queries this would create and you should not see any of them.

Other angle to watch out for here is RAM contention – ASP.NET cache will eject stuff pretty brutally to keep itself running fast. What you can do here is use one of the overloads to register a handler for the cache removal event.

If you haven’t figured out, caching is a pretty tricky beast . . .

I’ve been using a trace log in the datacontext:

base(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString,XmlMappingSource.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("DomainModel.mvCmsMapping.map"))){
			Log = (StringWriter)HttpContext.Current.Items["linqToSqlLog"]; // <-- THIS PART
		}

That gives me a list of executed queries. It shows 12 at first load and then 2 afterward due to the items being cached (the 2 queries left are the page content which isn’t cached).

first load:

Executed 12 SQL queries
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE REPLACE([t0].[pageTitle], @p0, @p1) = @p2 
-- @p0: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [ ] 
-- @p1: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [-] 
-- @p2: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [Home] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT ( SELECT [t0].[masterPage] 
FROM [dbo].[pages] AS [t0] 
WHERE REPLACE([t0].[pageTitle], @p0, @p1) = @p2 ) AS [value] 
-- @p0: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [ ] 
-- @p1: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [-] 
-- @p2: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [Home] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[configName], [t0].[configValue], [t0].[description] 
FROM [dbo].[config] AS [t0] 
WHERE [t0].[configName] = @p0 
-- @p0: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [Site_Name] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[configName], [t0].[configValue], [t0].[description] 
FROM [dbo].[config] AS [t0] 
WHERE [t0].[configName] = @p0 
-- @p0: Input NVarChar (Size = 12; Prec = 0; Scale = 0) [Company_Name] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE ([t0].[parent] = @p0) AND ([t0].[visible] = 1) 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [0] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE [t0].[parent] = @p0 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE [t0].[parent] = @p0 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE [t0].[parent] = @p0 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [3] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE [t0].[parent] = @p0 
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [4] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[configName], [t0].[configValue], [t0].[description] 
FROM [dbo].[config] AS [t0] 
WHERE [t0].[configName] = @p0 
-- @p0: Input NVarChar (Size = 10; Prec = 0; Scale = 0) [Home_Title] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[tipsText] 
FROM [dbo].[tips] AS [t0] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT [t0].[ID], [t0].[configName], [t0].[configValue], [t0].[description] 
FROM [dbo].[config] AS [t0] 
WHERE [t0].[configName] = @p0 
-- @p0: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [Copyright] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1

subsequent loads:

Executed 2 SQL queries
SELECT [t0].[ID], [t0].[pageTitle], [t0].[menuTitle], [t0].[content], [t0].[masterPage], [t0].[listOrder], [t0].[visible], [t0].[parent], [t0].[created], [t0].[edited], [t0].[createdBy], [t0].[lastEditBy] 
FROM [dbo].[pages] AS [t0] 
WHERE REPLACE([t0].[pageTitle], @p0, @p1) = @p2 
-- @p0: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [ ] 
-- @p1: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [-] 
-- @p2: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [Home] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1
SELECT ( SELECT [t0].[masterPage] 
FROM [dbo].[pages] AS [t0] 
WHERE REPLACE([t0].[pageTitle], @p0, @p1) = @p2 ) AS [value] 
-- @p0: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [ ] 
-- @p1: Input NVarChar (Size = 1; Prec = 0; Scale = 0) [-] 
-- @p2: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [Home] 
-- Context: SqlProvider(Sql2008) Model: MappedMetaModel Build: 3.5.30729.1