Connection is closed error. Using ObjectQuery and IContentQueryExecutor

June 11, 2024 1:46 PM

[describe your problem or question]

I am getting Invalid operation. The connection is closed. periodically when trying to run my project. I does not happen every time the project starts. When it does happen, the error occurs at 1 of 2 spots (both within services). Either at an ObjectQuery, or at an IcontentQueryExecutor. Below are code snippets of each.

####IContentQueryExecutor

 var builder = new ContentItemQueryBuilder();
 builder.ForContentType(ResourceStrings.CONTENT_TYPE_NAME);

 var cacheKeyParts = new[] { nameof(ResourceStrings.CONTENT_TYPE_NAME) };
 var queryResult = await cacheService.Get(
     async () => await queryExecutor.GetMappedWebPageResult<ResourceStrings>(builder),
     cacheKeyParts
     );

 var localizationPage = queryResult.FirstOrDefault();

####ObjectQuery

 public IEnumerable<MediaFileInfo> GetAssetsFromRelatedItems(IEnumerable<AssetRelatedItem> items)
 {
     return progressiveCache.Load(
         (cacheSettings) =>
         {
             var results = new ObjectQuery<MediaFileInfo>().ForAssets(items);
             var dependencyKeys = results
                 .Select(result => $"mediafile|{result.FileGUID}")
                 .ToArray();

             cacheSettings.CacheDependency = CacheHelper.GetCacheDependency(dependencyKeys);

             return results;
         },
         new CacheSettings(
             cacheMinutes: CacheConstants.CacheMinutes,
             useSlidingExpiration: true,
             cacheItemNameParts: new[]
             {
                 nameof(MediaLibraryService),
                 nameof(GetAssetsFromRelatedItems)
             }.Concat(items?.OrderBy(item => item.Name).Select(item => item.Name) ?? Enumerable.Empty<string>()).ToArray()
         )
     );
 }

Environment

  • Xperience by Kentico version: [29.1.0]
  • .NET version: [8]
  • Deployment environment: [Azure]
  • Error is happening locally

Answers

A couple things to note in your example code:

  1. It's not clear what cacheService.Get() is doing. That's not a type built into Xperience, so you'll need to provide that code here as well.
  2. I notice you aren't specifying any cache dependencies in your first example - these results will remain cached even if the data in the database changes. Is this intentional?

In regards to the error you're seeing. I'm not positive what the cause is, but I'm thinking that you might have a Task returning operation that is not being awaited correctly. Either the await is missing completely (fire-and-forget) or it is being awaited after the connection has closed.

Your .ForAssets() call returns an ObjectQuery<MediaFileInfo>. Yes, you can implicitly iterate on the result which will force materialization of the SQL query and result set, but I always recommend using one of Xperience's built-in extensions, like .GetEnumerableTypedResultAsync() which performs the query execution and result set generation explicitly.

Your implicit cast of results (which is ObjectQuery<MediaFileInfo>) to IEnumerable<MediaFileInfo> is a big risk, in my opinion. I would refactor the code to the following:

public async Task<ImmutableList<MediaFileInfo>> GetAssetsFromRelatedItems(IEnumerable<AssetRelatedItem> items)
{
    return await cache.LoadAsync(
        async (cacheSettings) =>
        {
            var results = await new ObjectQuery<MediaFileInfo>()
                .ForAssets(items)
                .GetEnumerableTypedResultAsync();

            var dependencyKeys = results
                .Select(result => $"mediafile|{result.FileGUID}")
                .ToArray();

            cacheSettings.CacheDependency = CacheHelper.GetCacheDependency(dependencyKeys);

			// ImmutableList has the immutability benefits of IEnumerable
			// while also clearly showing the result set is finite and already iterated
            return results.ToImmutableList();
        },
        new CacheSettings(
            cacheMinutes: CacheConstants.CacheMinutes,
            useSlidingExpiration: true,
            cacheItemNameParts: new[]
            {
                nameof(MediaLibraryService),
                nameof(GetAssetsFromRelatedItems)
            }.Concat(items?.OrderBy(item => item.Name).Select(item => item.Name) ?? Enumerable.Empty<string>()).ToArray()
        )
    );
}

To answer this question, you have to login first.