Ef6: Performance Issue with large model and GetCallingAssembly

Created on 19 Jul 2019  路  3Comments  路  Source: dotnet/ef6

Hi everybody,

We have ASP.NET app running on IIS using EF 6.1.1 and seems to have a performance problem due to EF. We have high CPU problem and when I profile our application with dot Trace we see a lot of CPU time and lock contention is at the method GetCallingAssembly which invoked by EF.
Please see the attachment
GetCallingAssembly

Our model is quite large with many Entities and that method is called quite a lot and it might be the reason.

Look into the source code I see the method with comments regarding ensure Assembly loaded.

CreateQuerySourceCode

I don't have deep knowledge about EF, However I wonder if the red line
"MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(T), Assembly.GetCallingAssembly());"
is needed if we have a simple model and it is loaded one time when the application start?

I comment that out and build the EF from source code and it is working. However I am hesitated to do that since it might lead to some hidden issues that I can't see it yet.

If anybody have some information about this, please give me advises. I am very appreciated that. We are stuck with this High CPU issue

Further technical details

EF version: (6.1.1)
Database Provider: (EntityFramework.SqlServer)
Operating system: (Windows 7)
IDE: (e.g. Visual Studio 2017 15.6)

type-investigation

Most helpful comment

Hi @ajcvickers
I think the problem is calling method GetCallingAssembly (actually GetExecutingAssembly) inside a loop through all items in the entities in the method ForceOSpaceLoadingForKnownEntityTypes.
If we can get the executing Assembly here, outside of the loop, and pass it to the CreateQuery method it would help with the performance issue.

Please give your advise. We are stuck with this issue. I tried to modified the code but have a problem with Strong Name verification. We using some third party assembly which depends on EF

public void ForceOSpaceLoadingForKnownEntityTypes()
{
if (!_oSpaceLoadingForced)
{
// Attempting to get o-space data for types that are not mapped is expensive so
// only try to do it once.
_oSpaceLoadingForced = true;

            Initialize();
            **//Add this line to get calling Assembly
            Assembly assembly = Assembly.GetCallingAssembly();**

            foreach (var set in _genericSets.Values.Union(_nonGenericSets.Values))
            {
                **//comment out the old initialize method call
                //set.InternalSet.TryInitialize(); 
                //pass the calling assembly through the loop with new method TryInitializeWithAssembly
                set.InternalSet.TryInitializeWithAssembly(assembly);**
            }
        }
    }

}

All 3 comments

Hi @ajcvickers
I think the problem is calling method GetCallingAssembly (actually GetExecutingAssembly) inside a loop through all items in the entities in the method ForceOSpaceLoadingForKnownEntityTypes.
If we can get the executing Assembly here, outside of the loop, and pass it to the CreateQuery method it would help with the performance issue.

Please give your advise. We are stuck with this issue. I tried to modified the code but have a problem with Strong Name verification. We using some third party assembly which depends on EF

public void ForceOSpaceLoadingForKnownEntityTypes()
{
if (!_oSpaceLoadingForced)
{
// Attempting to get o-space data for types that are not mapped is expensive so
// only try to do it once.
_oSpaceLoadingForced = true;

            Initialize();
            **//Add this line to get calling Assembly
            Assembly assembly = Assembly.GetCallingAssembly();**

            foreach (var set in _genericSets.Values.Union(_nonGenericSets.Values))
            {
                **//comment out the old initialize method call
                //set.InternalSet.TryInitialize(); 
                //pass the calling assembly through the loop with new method TryInitializeWithAssembly
                set.InternalSet.TryInitializeWithAssembly(assembly);**
            }
        }
    }

}

just looking at this off the cuff.. i tend to agree, there is a ' Assembly.GetCallingAssembly()' inside what looks like a a common code usage (CreateQuery).

https://docs.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.createquery?view=netframework-4.8

Would it not be better cache the result of Assembly.GetCallingAssembly() and then only call if its empty, calling every time CreateQuery is called seem unnecessary.

We have a similar issue with 6.2.0, net472. In a web service environment, application experiences high Lock Contention - the source is in EF ObjectContext / CreateObjectQuery calling GetExecutingAssembly. Happens when a single machine handles multiple requests.

We have multiple databases and can have a read-only connection to replica and another connection to master at the same time, and this is for multiple DBs for multiple requests in parallel.

Was this page helpful?
0 / 5 - 0 ratings