XML column using Effort - entity-framework

I have my Entity Framework context faked using the Effort framework. I'm using a XML-typed column to store data. Apparently Effort can't handle this. How can I get around this? All suggestions are welcome!

I have come across the same issue and I have written a bit of code that goes through the entity framework model and removes the xml column type from the model.
Here is an example of how to use it. Just create a new wrapper DbContext class for your main code and then use that.
public class EffortDbContext : OriginalContext
{
public EffortDbContext(DbConnection connection) : base(connection, false)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
RemoveXmlColumnTypeFromModelBuilder(modelBuilder);
}
/// <summary>
/// Removes the XML column type from model builder.
/// </summary>
/// <remarks>Beware in using this code, here be dragons. It meddles with the internals of the entity model configuration to strip out the XML column type.</remarks>
/// <param name="modelBuilder">The model builder.</param>
private void RemoveXmlColumnTypeFromModelBuilder(DbModelBuilder modelBuilder)
{
var _modelConfiguration = modelBuilder.GetType()
.GetField("_modelConfiguration", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(modelBuilder);
var _entityConfigurations = (IEnumerable) _modelConfiguration.GetType()
.GetField("_entityConfigurations", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(_modelConfiguration);
foreach (object configuration in _entityConfigurations)
{
var entityConfigurationDictionaryValue = configuration.GetType().GetProperty("Value").GetValue(configuration);
var ppc = (IEnumerable) entityConfigurationDictionaryValue.GetType()
.GetProperty("PrimitivePropertyConfigurations", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(entityConfigurationDictionaryValue);
foreach (var primitivePropertyConfiguration in ppc)
{
var primitivePropertyConfigurationValue = primitivePropertyConfiguration.GetType().GetProperty("Value").GetValue(primitivePropertyConfiguration);
var columnTypeProperty = primitivePropertyConfigurationValue.GetType().GetProperty("ColumnType");
if (columnTypeProperty.GetValue(primitivePropertyConfigurationValue)?.ToString() == "xml")
columnTypeProperty.SetValue(primitivePropertyConfigurationValue, null);
}
}
}
}
Hope this helps.

Related

EF6 preventing not to create Index on Foreign Key

I'm using EF6 code first approach to create database. When i add migration and update database it always create Non-cluster Index for every foreign key in the table by default.
My Question: Is there any global setting for EF6 to not create Non-Cluster indexon foreign key ?
I have search and found the following solutions
Solution 1: Remove index line from migration before updating database
Solution 1 not suits me because i have a lot of tables and my db is already created. Manually remove index creation line takes much much time.
Moreover i'm also using fluent api is there any option related to this issue ?
Well, I think this might have been an 'If all you have is a hammer...' kinda situation.
The answer that I gave before works (and I stand by it, because it is totally fun and awesome), but it's probably not the best way to do it.
Recently I checked all the default conventions EF uses the generate the database, and there is one that's responsible for generating the non-clustered indices on FK-s. Just remove that convention altogether, and the problem is solved:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// using System.Data.Entity.ModelConfiguration.Conventions;
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
}
I don't believe there is a simple solution to this, but I have an idea about what you could do: create a custom migration generator.
Migration generators are the components that are responsible for creating the SQL script that is run on the database from the migration code files. I assume you have SQL Server based on the screenshot. In this case, you can write a custom sql generator that simply overrides the index creation operation so that if the index is non-clustered, nothing is written to the script:
public class NoIndexGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(CreateIndexOperation createIndexOperation)
{
if (!createIndexOperation.IsClustered)
{
return;
}
}
}
Then you have to register this component in the Configuration class of the migration:
internal sealed class Configuration : DbMigrationsConfiguration<MyCtx>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Add this line to register the sql generator
this.SetSqlGenerator("System.Data.SqlClient", new NoIndexGenerator());
}
}
Now if you run Add-Migration, you'll have a normal migration file, with the CreateIndexOperation in it. But if you run Update-Database, the non-clustered indices will not be created. You can also check this if you run Update-Database -Script. The resulting script does not have the non-clustered indices.
If you want, you can go even higher up in the pipeline, and create a custom C# migration scaffolder. It applies the same logic as the sql generator:
internal class NoIndexMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(CreateIndexOperation createIndexOperation, IndentedTextWriter writer)
{
if (!createIndexOperation.IsClustered)
{
return;
}
}
}
Then, you can register it in the Configuration class like this:
internal sealed class Configuration : DbMigrationsConfiguration<MyCtx>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Add this line to register the C# code generator
this.CodeGenerator = new NoIndexMigrationCodeGenerator();
}
}
Now, if you run Add-Migration, the CreateIndex operations will disappear from the generated migration cs files as well.
I'd probably go with the second solution (it can be confusing for others reading your code to see that there are CreateIndex operations in the migration cs file, but not in the SQL scripts), but ultimately it is your choice :)
You can play with other properties of the createIndexOperation parameter of the Generate() methods to implement more sophisticated index filtering, if you have to.
If you want, you can also override the Generate methods that have a parameter of type DropCreateIndexOperation but since indices are dropped with a 'drop-if-exists' pattern, I don't think this is necessary.
EDIT
While the above code samples seem to work, to be fair and follow general best-practices and principles, you should probably include calls to the base methods in both generators after the if statements.
After trying to use CSharpMigrationCodeGenerator I start to think in way to override the ForeignKeyIndexConvention.
So I implemented the check that allows to skip adding index on Primary Key column by assuming that PK naming convention was not overrided.
public class ForeignKeyIndexConventionFix : ForeignKeyIndexConvention
{
private const string Id = "Id";
public override void Apply(AssociationType item, DbModel model)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
if (item.Constraint == null)
{
return;
}
if (item.IsForeignKey)
{
if (IsPrimaryKeyColumn(item.Constraint))
{
return;
}
}
base.Apply(item, model);
}
private static bool IsPrimaryKeyColumn(ReferentialConstraint constraint)
{
IEnumerable<string> dependentColumns = constraint.ToProperties.Select(p => p.Name);
if (dependentColumns.Count() == 1)
{
string dependentColum = dependentColumns.First();
if (dependentColum.Equals(Id, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}
Then override your DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
modelBuilder.Conventions.Add<ForeignKeyIndexConventionFix>();
}
There was problem to debug solution - Console.Write and Debug.Write were not giving output - so I just put tracing to some text file in temp location. Maybe there is a better way..?
And the code source of original implementation helped to figure out how to get dependent column names: https://github.com/dotnet/ef6/blob/master/src/EntityFramework/ModelConfiguration/Conventions/Edm/Db/ForeignKeyIndexConvention.cs
With EF Core 6.0, you need to remove ForeignKeyIndexConvention form all the convention sets that includes it. Here is a working solution:
Create a new ConventionSetBuilder class:
public class CustomSqlServerConventionSetBuilder : SqlServerConventionSetBuilder, IConventionSetBuilder
{
public CustomSqlServerConventionSetBuilder(ProviderConventionSetBuilderDependencies dependencies, RelationalConventionSetBuilderDependencies relationalDependencies,
ISqlGenerationHelper sqlGenerationHelper) : base(dependencies, relationalDependencies, sqlGenerationHelper)
{
}
public override ConventionSet CreateConventionSet()
{
var cs = base.CreateConventionSet();
//ForeignKeyAddedConventions
var foreignKeyAddedConvention = cs.ForeignKeyAddedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (foreignKeyAddedConvention != null)
cs.ForeignKeyAddedConventions.Remove(foreignKeyAddedConvention);
//ForeignKeyRemovedConventions
var foreignKeyRemovedConventions = cs.ForeignKeyRemovedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (foreignKeyRemovedConventions != null)
cs.ForeignKeyRemovedConventions.Remove(foreignKeyRemovedConventions);
//EntityTypeBaseTypeChangedConventions
var entityTypeBaseTypeChangedConventions = cs.EntityTypeBaseTypeChangedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (entityTypeBaseTypeChangedConventions != null)
cs.EntityTypeBaseTypeChangedConventions.Remove(entityTypeBaseTypeChangedConventions);
//KeyAddedConventions
var keyAddedConventions = cs.KeyAddedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (keyAddedConventions != null)
cs.KeyAddedConventions.Remove(keyAddedConventions);
//KeyRemovedConventions
var keyRemovedConventions = cs.KeyRemovedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (keyRemovedConventions != null)
cs.KeyRemovedConventions.Remove(keyRemovedConventions);
//ForeignKeyPropertiesChangedConventions
var foreignKeyPropertiesChangedConventions = cs.ForeignKeyPropertiesChangedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (foreignKeyPropertiesChangedConventions != null)
cs.ForeignKeyPropertiesChangedConventions.Remove(foreignKeyPropertiesChangedConventions);
//ForeignKeyUniquenessChangedConventions
var foreignKeyUniquenessChangedConventions = cs.ForeignKeyUniquenessChangedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (foreignKeyUniquenessChangedConventions != null)
cs.ForeignKeyUniquenessChangedConventions.Remove(foreignKeyUniquenessChangedConventions);
//IndexAddedConventions
var indexAddedConventions = cs.IndexAddedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (indexAddedConventions != null)
cs.IndexAddedConventions.Remove(indexAddedConventions);
//IndexRemovedConventions
var indexRemovedConventions = cs.IndexRemovedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (indexRemovedConventions != null)
cs.IndexRemovedConventions.Remove(indexRemovedConventions);
//IndexUniquenessChangedConventions
var indexUniquenessChangedConventions = cs.IndexUniquenessChangedConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (indexUniquenessChangedConventions != null)
cs.IndexUniquenessChangedConventions.Remove(indexUniquenessChangedConventions);
//ModelFinalizingConventions
var modelFinalizingConventions = cs.ModelFinalizingConventions.FirstOrDefault(f => f is ForeignKeyIndexConvention);
if (modelFinalizingConventions != null)
cs.ModelFinalizingConventions.Remove(modelFinalizingConventions);
return cs;
}
}
And replace the ConventionSetBuilder within your DbContext configuration:
public partial class YourDbContext : DbContext
{
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
...
optionsBuilder.ReplaceService<IConventionSetBuilder, CustomSqlServerConventionSetBuilder>();
}
}

Entity Framework 6 Generic Eager Loading Query Method

I am writing a generic querying method in Entity Framework 6, based off of this helpful article. Here's how it looks:
public static T QueryEagerLoad<T>(Expression<Func<T, bool>> match) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
databaseContext.Configuration.LazyLoadingEnabled = false;
T retrievedObject = databaseContext.Set<T>().SingleOrDefault(match);
return retrievedObject;
}
}
I'm attempting to eagerly load any related entities, so I include disable to configuration variable LazyLoadingEnabled. While it loads the object, it does not load the related entities, per my view in the debugger. Why would this be? Am I missing something? I should note that I'm using Npgsql. Thanks in advance.
See Mikael Östberg's answer to this question. To use a generic method for querying with eager loading, it seems necessary to inject the includes. Here's how the generic method shaped up:
public static T Query<T>(Expression<Func<T, bool>> match, List<Expression<Func<T, object>>> includes) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
var dataSet = databaseContext.Set<T>(); // Get the relevant DataSet
T retrievedObject = includes.Aggregate( // Eagerly load the passed navigation properties
dataSet.AsQueryable(),
(current, include) => current.Include(include)
).SingleOrDefault(match); // Find exactly one or zero matches
return retrievedObject;
}
}
And an example of a call that injects the properties to eagerly load (the includes parameter in the generic method above):
public static Lumber GetLumber(int databaseId)
{
Expression<Func<Lumber, object>> lengthProperty = (lumber => lumber.Length);
Expression<Func<Lumber, object>> thicknessProperty = (lumber => lumber.Thickness);
Expression<Func<Lumber, object>> widthProperty = (lumber => lumber.Width);
List<Expression<Func<Lumber, object>>> lumberNaviationProperties = new List<Expression<Func<Lumber, object>>>() { lengthProperty, thicknessProperty, widthProperty };
Lumber retrievedLumber = DatabaseOperations.Query<Lumber>((lumber => lumber.DatabaseId == databaseId), lumberNaviationProperties);
return retrievedLumber;
}

How to write a generic WebAPI Put method against Entity Framework that works with child lists?

I am tinkering with WebAPI to create a generic implementation for entity framework. I am able to implement most of the methods just fine, but am finding PUT to be tricky in non-trivial cases. The implementation most commonly found online works for simple entities:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var entry = db.Entry(entity);
entry.State = EntityState.Modified;
var set = db.Set<T>();
set.Attach(entity);
db.SaveChanges();
return entity;
}
...but does not delete or update child lists:
public class Invoice
{
...
public virtual InvoiceLineItem {get; set;} //Attach method doesn't address these
}
In an MVC Controller, you could simply use "UpdateModel" and it would add/update/delete children as needed, however that method is not available on ApiController. I understand that some code would be necessary to get the original item from the database, and that it would need to use Include to get the child lists, but can't quite figure out the best way to replicate UpdateModel's functionality:
[HttpPut]
[ActionName("Endpoint")]
public virtual T Put(T entity)
{
var db = GetDbContext();
var original = GetOriginalFor(entity);
//TODO: Something similar to UpdateModel(original), such as UpdateModel(original, entity);
db.SaveChanges();
return original;
}
How can I implement UpdateModel OR somehow implement Put in such a way that it will handle child lists?
The routine dont validate entity, but fill the pre-existent entity.
protected virtual void UpdateModel<T>(T original, bool overrideForEmptyList = true)
{
var json = ControllerContext.Request.Content.ReadAsStringAsync().Result;
UpdateModel<T>(json, original, overrideForEmptyList);
}
private void UpdateModel<T>(string json, T original, bool overrideForEmptyList = true)
{
var newValues = JsonConvert.DeserializeObject<Pessoa>(json);
foreach (var property in original.GetType().GetProperties())
{
var isEnumerable = property.PropertyType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (isEnumerable && property.PropertyType != typeof(string))
{
var propertyOriginalValue = property.GetValue(original, null);
if (propertyOriginalValue != null)
{
var propertyNewValue = property.GetValue(newValues, null);
if (propertyNewValue != null && (overrideForEmptyList || ((IEnumerable<object>)propertyNewValue).Any()))
{
property.SetValue(original, null);
}
}
}
}
JsonConvert.PopulateObject(json, original);
}
public void Post()
{
var sample = Pessoa.FindById(12);
UpdateModel(sample);
}

Entity Framework Detached Object Merging

I have a scenario where I am using Entity Framework in a WCF service, and changes happen on a non-tracked instance of a type that is mapped back to the database via code-first (non-trivial updates and deletes throughout the instance's object tree). When I try to attach the non-tracked instance into the context, EF is only recognizing changes to the simple value types on the root object.
Does anyone know of an elegant solution for this scenario? I am looking for a way to do this by using a generic repository, and avoiding having to run through the instance's entire object tree managing the "attach/detach" state of every object. I have considered possibly using ValueInjecter or AutoMapper to run the changes on a fully hydrated and tracked instance of the "old" state in order for the context to pickup the changes. Also, how would Nhibernate handle this situation?
Thanks in advance for your input!
UPDATE (7/31/2012): I have updated the code to handle genericly-typed keys, and some typing issues with EF Proxies. Also added some helper extensions when dealing with IEntity types. This implementation isn't perfect, but it is very functional.
UPDATE (3/13/2012): I have added a feature request for cleaner merging in EF. The request is located here: http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/2679160-better-merging-change-tracking
UPDATE (3/12/2012): I have posted my solution below. It uses FubuCore, ValueInjecter, and requires entities to be marked with one of two interfaces, either IEntity, or IRecursiveEntity for recursive classes. The solution will handle recursive, self-linked entities.
Also, I am referencing a generic repository (Repository) that allows me to get a reference to the IDbSet that EF exposes. This could be substituded with any other generic or specific repository. Lastly, the IEntity interface uses an int? id, however you could define that however you want (Guid/Guid?). The solution itself isn't quite as elegant as I would like, however it allows for much more elegant data access code when behind a physical WCF service boundary.
public class DomainMergeInjection : ConventionInjection
{
private readonly Repository _repository;
private readonly Dictionary<string, object> _potentialParentObjectDump;
private readonly Cache<Type, Type> _entityTypesAndKeysCache;
public DomainMergeInjection(Repository repository)
{
_repository = repository;
_potentialParentObjectDump = new Dictionary<string, object>();
_entityTypesAndKeysCache = new Cache<Type, Type>();
}
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override object SetValue(ConventionInfo c)
{
if(c.SourceProp.Value == null)
return null;
//for value types and string just return the value as is
if(c.SourceProp.Type.IsSimple())
return c.SourceProp.Value;
//TODO: Expand on this to handle IList/IEnumerable (i.e. the non-generic collections and arrays).
//handle arrays
if(c.SourceProp.Type.IsArray)
{
var sourceArray = c.SourceProp.Value as Array;
// ReSharper disable PossibleNullReferenceException
var clonedArray = sourceArray.Clone() as Array;
// ReSharper restore PossibleNullReferenceException
for(int index = 0; index < sourceArray.Length; index++)
{
var sourceValueAtIndex = sourceArray.GetValue(index);
//Skip null and simple values that would have already been moved in the clone.
if(sourceValueAtIndex == null || sourceValueAtIndex.GetType().IsSimple())
continue;
// ReSharper disable PossibleNullReferenceException
clonedArray.SetValue(RetrieveComplexSourceValue(sourceValueAtIndex), index);
// ReSharper restore PossibleNullReferenceException
}
return clonedArray;
}
//handle IEnumerable<> also ICollection<> IList<> List<>
if(c.SourceProp.Type.IsGenericEnumerable())
{
var t = c.SourceProp.Type.GetGenericArguments()[0];
if(t.IsSimple())
return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
dynamic list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach(var sourceItem in (IEnumerable)c.SourceProp.Value)
{
addMethod.Invoke(list, new[] { RetrieveComplexSourceValue(sourceItem) });
}
return list;
}
//Get a source value that is in the right state and is tracked if needed.
var itemStateToInject = RetrieveComplexSourceValue(c.SourceProp.Value);
return itemStateToInject;
}
private object RetrieveComplexSourceValue(object source)
{
//If the source is a non-tracked type, or the source is a new value, then return its value.
if(!source.ImplementsIEntity(_entityTypesAndKeysCache) || source.IsEntityIdNull(_entityTypesAndKeysCache))
return source;
object sourceItemFromContext;
//Handle recursive entities, this could probably be cleaned up.
if(source.ImplementsIRecursiveEntity())
{
var itemKey = source.GetEntityIdString(_entityTypesAndKeysCache) + " " + ObjectContext.GetObjectType(source.GetType());
//If we have a context item for this key already, just return it. This solves a recursion problem with self-linking items.
if(_potentialParentObjectDump.ContainsKey(itemKey))
return _potentialParentObjectDump[itemKey];
//Get the source from the context to ensure it is tracked.
sourceItemFromContext = GetSourceItemFromContext(source);
//Add the class into the object dump in order to avoid any infinite recursion issues with self-linked objects
_potentialParentObjectDump.Add(itemKey, sourceItemFromContext);
}
else
//Get the source from the context to ensure it is tracked.
sourceItemFromContext = GetSourceItemFromContext(source);
//Recursively use this injection class instance to inject the source state on to the context source state.
var itemStateToInject = sourceItemFromContext.InjectFrom(this, source);
return itemStateToInject;
}
private object GetSourceItemFromContext(object source)
{
if(source == null)
return null;
//Using dynamic here to "AutoCast" to an IEntity<>. We should have one, but it's important to note just in case.
dynamic sourceEntityValue = source;
var sourceEntityType = ObjectContext.GetObjectType(source.GetType());
var sourceKeyType = sourceEntityType.GetEntityKeyType();
var method = typeof(DomainMergeInjection).GetMethod("GetFromContext", BindingFlags.Instance | BindingFlags.NonPublic);
var generic = method.MakeGenericMethod(sourceEntityType, sourceKeyType);
var sourceItemFromContext = generic.Invoke(this, new object[] { new object[] { sourceEntityValue.Id } });
return sourceItemFromContext;
}
// ReSharper disable UnusedMember.Local
private TItem GetFromContext<TItem, TKey>(object[] keys) where TItem : class, IEntity<TKey>
// ReSharper restore UnusedMember.Local
{
var foundItem = _repository.GetDbSet<TItem>().Find(keys);
return foundItem;
}
}
public static class EntityTypeExtensions
{
/// <summary>
/// Determines if an object instance implements IEntity.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache">A cache to hold types that do implement IEntity. If the cache does not have the Type and the Type does implement IEntity, it will add the type to the cache along with the </param>
/// <returns></returns>
public static bool ImplementsIEntity(this object entity, Cache<Type, Type> entityCache = null)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
if(entityCache != null && entityCache.Has(entityType))
return true;
var implementationOfIEntity = entityType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof (IEntity<>));
if(implementationOfIEntity == null)
return false;
if(entityCache != null)
{
var keyType = implementationOfIEntity.GetGenericArguments()[0];
entityCache.Fill(entityType, keyType);
}
return true;
}
/// <summary>
/// Determines if an object instances implements IRecurisveEntity
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool ImplementsIRecursiveEntity(this object entity)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
var implementsIRecursiveEntity = entityType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRecursiveEntity<>));
return implementsIRecursiveEntity;
}
/// <summary>
/// Determines whether or not an Entity's Id is null. Will throw an exception if a type that does not implement IEntity is passed through.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache"></param>
/// <returns></returns>
public static bool IsEntityIdNull(this object entity, Cache<Type, Type> entityCache = null)
{
bool isEntityIdNull = ExecuteEntityIdMethod<bool>("IsEntityIdNull", entity, entityCache);
return isEntityIdNull;
}
/// <summary>
/// Determines whether or not an Entity's Id is null. Will throw an exception if a type that does not implement IEntity is passed through.
/// </summary>
/// <param name="entity"></param>
/// <param name="entityCache"></param>
/// <returns></returns>
public static string GetEntityIdString(this object entity, Cache<Type, Type> entityCache = null)
{
string entityIdString = ExecuteEntityIdMethod<string>("GetEntityIdString", entity, entityCache);
return entityIdString;
}
private static T ExecuteEntityIdMethod<T>(string methodName, object entityInstance, Cache<Type, Type> entityCache = null)
{
if(!entityInstance.ImplementsIEntity(entityCache))
throw new ArgumentException(string.Format("Parameter entity of type {0} does not implement IEntity<>, and so ist not executable for {1}!", entityInstance.GetType(), methodName));
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entityInstance.GetType());
var keyType = entityCache != null ? entityCache[entityType] : entityType.GetEntityKeyType();
var method = typeof(EntityTypeExtensions).GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic);
var generic = method.MakeGenericMethod(keyType);
T returnValue = (T)generic.Invoke(null, new[] { entityInstance });
return returnValue;
}
private static string GetEntityIdString<TKey>(IEntity<TKey> entity)
{
var entityIdString = entity.Id.ToString();
return entityIdString;
}
private static bool IsEntityIdNull<TKey>(IEntity<TKey> entity)
{
//We need to handle getting the proxy type if this is an EF Code-First proxy.
//Please see for more info: http://msdn.microsoft.com/en-us/library/dd456853.aspx
var entityType = ObjectContext.GetObjectType(entity.GetType());
if(entityType.IsPrimitive)
return false;
//NOTE: We know that this entity's type is NOT primitive, therefore we can cleanly test for null, and return properly.
// ReSharper disable CompareNonConstrainedGenericWithNull
var entityIdIsNull = entity.Id == null;
// ReSharper restore CompareNonConstrainedGenericWithNull
return entityIdIsNull;
}
public static Type GetEntityKeyType(this Type typeImplementingIEntity)
{
var implementationOfIEntity = typeImplementingIEntity.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEntity<>));
if(implementationOfIEntity == null)
throw new ArgumentException(string.Format("Type {0} does not implement IEntity<>", typeImplementingIEntity));
var keyType = implementationOfIEntity.GetGenericArguments()[0];
return keyType;
}
}
public interface IEntity<TKey>
{
TKey Id { get; set; }
}
public interface IRecursiveEntity<TKey> : IEntity<TKey>
{
IRecursiveEntity<TKey> Parent { get; }
IEnumerable<IRecursiveEntity<TKey>> Children { get; }
}
you could use the detached object only as a DTO,
and after refill the object from context with values from the DTO
with ValueInjecter this would be:
//manually
conObj.InjectFrom(dto);
conObj.RefTypeProp.InjectFrom(dto.RefTypeProp);
...
//or by writing a custom injection:
conObj.InjectFrom<ApplyChangesInjection>(dto);
here's the Injection that will do that automatically, (I did it by modifying a bit the DeepClone Injection from VI's home page)
the trick here is that the Injection uses itself in the SetValue method
public class ApplyChangesInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
protected override object SetValue(ConventionInfo c)
{
if (c.SourceProp.Value == null) return null;
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays - not impl
//handle IEnumerable<> also ICollection<> IList<> List<> - not impl
//for simple object types apply the inject using the corresponding source
return c.TargetProp.Value
.InjectFrom<ApplyChangesInjection>(c.SourceProp.Value);
}
}
//Note: I'm not handling collections in this injection, I just wanted you to understand the principle,
you can look at the original http://valueinjecter.codeplex.com/wikipage?title=Deep%20Cloning&referringTitle=Home

How do I view the SQL generated by the Entity Framework?

How do I view the SQL generated by entity framework ?
(In my particular case I'm using the mysql provider - if it matters)
For those using Entity Framework 6 and up, if you want to view the output SQL in Visual Studio (like I did) you have to use the new logging/interception functionality.
Adding the following line will spit out the generated SQL (along with additional execution-related details) in the Visual Studio output panel:
using (MyDatabaseEntities context = new MyDatabaseEntities())
{
context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
// query the database using EF here.
}
More information about logging in EF6 in this nifty blog series: http://blog.oneunicorn.com/2013/05/08/ef6-sql-logging-part-1-simple-logging/
Note: Make sure you are running your project in DEBUG mode.
You can do the following:
IQueryable query = from x in appEntities
where x.id == 32
select x;
var sql = ((System.Data.Objects.ObjectQuery)query).ToTraceString();
or in EF6:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query)
.ToTraceString();
or in EF6.3+:
var sql = ((dynamic)flooringStoresProducts).Sql;
That will give you the SQL that was generated.
Starting with EF6.1 you can use Interceptors to register a database logger.
See chapters "Interceptors" and "Logging Database Operations" to a File here
<configuration>
<entityFramework>
<interceptors>
<interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
<parameters>
<parameter value="C:\Temp\LogOutput.txt"/>
<parameter value="true" type="System.Boolean"/>
</parameters>
</interceptor>
</interceptors>
</entityFramework>
</configuration>
If you are using a DbContext, you can do the following to get the SQL:
var result = from i in myContext.appEntities
select new Model
{
field = i.stuff,
};
var sql = result.ToString();
EF Core 5.0+
This loooong-awaited feature is available in EF Core 5.0! This is from the weekly status updates:
var query = context.Set<Customer>().Where(c => c.City == city);
Console.WriteLine(query.ToQueryString())
results in this output when using the SQL Server database provider:
DECLARE p0 nvarchar(4000) = N'London';
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName],
[c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone],
[c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE [c].[City] = #__city_0
Notice that declarations for parameters of the correct type are also
included in the output. This allows copy/pasting to SQL Server
Management Studio, or similar tools, such that the query can be
executed for debugging/analysis.
woohoo!!!
(Note: You will need using Microsoft.EntityFrameworkCore;)
Applicable for EF 6.0 and above:
For those of you wanting to know more about the logging functionality and adding to the some of the answers already given.
Any command sent from the EF to the database can now be logged. To view the generated queries from EF 6.x, use the DBContext.Database.Log property
What Gets Logged
- SQL for all different kinds of commands. For example:
- Queries, including normal LINQ queries, eSQL queries, and raw queries from methods such as SqlQuery.
- Inserts, updates, and deletes generated as part of SaveChanges
- Relationship loading queries such as those generated by lazy loading
- Parameters
- Whether or not the command is being executed asynchronously
- A timestamp indicating when the command started executing
- Whether or not the command completed successfully, failed by throwing an exception, or, for async, was canceled
- Some indication of the result value
- The approximate amount of time it took to execute the command. Note that this is the time from sending the command to getting the result object back. It does not include time to read the results.
Example:
using (var context = new BlogContext())
{
context.Database.Log = Console.Write;
var blog = context.Blogs.First(b => b.Title == "One Unicorn");
blog.Posts.First().Title = "Green Eggs and Ham";
blog.Posts.Add(new Post { Title = "I do not like them!" });
context.SaveChangesAsync().Wait();
}
Output:
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 4 ms with result: SqlDataReader
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
[Extent1].[BlogId] AS [BlogId]
FROM [dbo].[Posts] AS [Extent1]
WHERE [Extent1].[BlogId] = #EntityKeyValue1
-- EntityKeyValue1: '1' (Type = Int32)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader
UPDATE [dbo].[Posts]
SET [Title] = #0
WHERE ([Id] = #1)
-- #0: 'Green Eggs and Ham' (Type = String, Size = -1)
-- #1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 12 ms with result: 1
INSERT [dbo].[Posts]([Title], [BlogId])
VALUES (#0, #1)
SELECT [Id]
FROM [dbo].[Posts]
WHERE ##ROWCOUNT > 0 AND [Id] = scope_identity()
-- #0: 'I do not like them!' (Type = String, Size = -1)
-- #1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader
To log to an external file:
using (var context = new BlogContext())
{
using (var sqlLogFile = new StreamWriter("C:\\temp\\LogFile.txt"))
{
context.Database.Log = sqlLogFile.Write;
var blog = context.Blogs.First(b => b.Title == "One Unicorn");
blog.Posts.First().Title = "Green Eggs and Ham";
context.SaveChanges();
}
}
More info here: Logging and Intercepting Database Operations
You can do the following in EF 4.1:
var result = from x in appEntities
where x.id = 32
select x;
System.Diagnostics.Trace.WriteLine(result .ToString());
That will give you the SQL that was generated.
My answer addresses EF core. I reference this github issue, and the docs on configuring DbContext:
Simple
Override the OnConfiguring method of your DbContext class (YourCustomDbContext) as shown here to use a ConsoleLoggerProvider; your queries should log to the console:
public class YourCustomDbContext : DbContext
{
#region DefineLoggerFactory
public static readonly LoggerFactory MyLoggerFactory
= new LoggerFactory(new[] {new ConsoleLoggerProvider((_, __) => true, true)});
#endregion
#region RegisterLoggerFactory
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLoggerFactory(MyLoggerFactory); // Warning: Do not create a new ILoggerFactory instance each time
#endregion
}
Complex
This Complex case avoids overriding the DbContext OnConfiguring method. , which is discouraged in the docs: "This approach does not lend itself to testing, unless the tests target the full database."
This Complex case uses:
The IServiceCollection in Startup class ConfigureServices method
(instead of overriding the OnConfiguring method; the benefit is a looser coupling between the DbContext and the ILoggerProvider you want to use)
An implementation of ILoggerProvider (instead of using the ConsoleLoggerProvider implementation shown above; benefit is our implementation shows how we would log to File (I don't see a File Logging Provider shipped with EF Core))
Like this:
public class Startup
public void ConfigureServices(IServiceCollection services)
{
...
var lf = new LoggerFactory();
lf.AddProvider(new MyLoggerProvider());
services.AddDbContext<YOUR_DB_CONTEXT>(optionsBuilder => optionsBuilder
.UseSqlServer(connection_string)
//Using the LoggerFactory
.UseLoggerFactory(lf));
...
}
}
Here's the implementation of a MyLoggerProvider (and its MyLogger which appends its logs to a File you can configure; your EF Core queries will appear in the file.)
public class MyLoggerProvider : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new MyLogger();
}
public void Dispose()
{ }
private class MyLogger : ILogger
{
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
File.AppendAllText(#"C:\temp\log.txt", formatter(state, exception));
Console.WriteLine(formatter(state, exception));
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
}
}
To have the query always handy, without changing code
add this to your DbContext and check it on the output window in visual studio.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = (query)=> Debug.Write(query);
}
Similar to #Matt Nibecker answer, but with this you do not have to add it in your current code, every time you need the query.
There are two ways:
To view the SQL that will be generated, simply call ToTraceString(). You can add it into your watch window and set a breakpoint to see what the query would be at any given point for any LINQ query.
You can attach a tracer to your SQL server of choice, which will show you the final query in all its gory detail. In the case of MySQL, the easiest way to trace the queries is simply to tail the query log with tail -f. You can learn more about MySQL's logging facilities in the official documentation. For SQL Server, the easiest way is to use the included SQL Server profiler.
I am doing integration test, and needed this to debug the generated SQL statement in Entity Framework Core 2.1, so I use DebugLoggerProvider or ConsoleLoggerProvider like so:
[Fact]
public async Task MyAwesomeTest
{
//setup log to debug sql queries
var loggerFactory = new LoggerFactory();
loggerFactory.AddProvider(new DebugLoggerProvider());
loggerFactory.AddProvider(new ConsoleLoggerProvider(new ConsoleLoggerSettings()));
var builder = new DbContextOptionsBuilder<DbContext>();
builder
.UseSqlServer("my connection string") //"Server=.;Initial Catalog=TestDb;Integrated Security=True"
.UseLoggerFactory(loggerFactory);
var dbContext = new DbContext(builder.Options);
........
Here is a sample output from Visual Studio console:
SQL Management Studio => Tools => SQL Server profiler
File => New Trace...
Use the Template => Blank
Event selection => T-SQL
Lefthandside check for: SP.StmtComplete
Column filters can be used to select a specific ApplicationName or DatabaseName
Start that profile running then trigger the query.
Click here for Source information
Use Logging with Entity Framework Core 3.x
Entity Framework Core emits SQL via the logging system. There are only a couple of small tricks. You must specify an ILoggerFactory and you must specify a filter. Here is an example from this article
Create the factory:
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole((options) => { })
.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information);
});
Tell the DbContext to use the factory in the OnConfiguring method:
optionsBuilder.UseLoggerFactory(_loggerFactory);
From here, you can get a lot more sophisticated and hook into the Log method to extract details about the executed SQL. See the article for a full discussion.
public class EntityFrameworkSqlLogger : ILogger
{
#region Fields
Action<EntityFrameworkSqlLogMessage> _logMessage;
#endregion
#region Constructor
public EntityFrameworkSqlLogger(Action<EntityFrameworkSqlLogMessage> logMessage)
{
_logMessage = logMessage;
}
#endregion
#region Implementation
public IDisposable BeginScope<TState>(TState state)
{
return default;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (eventId.Id != 20101)
{
//Filter messages that aren't relevant.
//There may be other types of messages that are relevant for other database platforms...
return;
}
if (state is IReadOnlyList<KeyValuePair<string, object>> keyValuePairList)
{
var entityFrameworkSqlLogMessage = new EntityFrameworkSqlLogMessage
(
eventId,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "commandText").Value,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "parameters").Value,
(CommandType)keyValuePairList.FirstOrDefault(k => k.Key == "commandType").Value,
(int)keyValuePairList.FirstOrDefault(k => k.Key == "commandTimeout").Value,
(string)keyValuePairList.FirstOrDefault(k => k.Key == "elapsed").Value
);
_logMessage(entityFrameworkSqlLogMessage);
}
}
#endregion
}
IQueryable query = from x in appEntities
where x.id = 32
select x;
var queryString = query.ToString();
Will return the sql query. Working using datacontext of EntityFramework 6
Necromancing.
This page is the first search result when searching for a solution for any .NET Framework, so here as a public service, how it's done in EntityFrameworkCore (for .NET Core 1 & 2):
var someQuery = (
from projects in _context.projects
join issues in _context.issues on projects.Id equals issues.ProjectId into tmpMapp
from issues in tmpMapp.DefaultIfEmpty()
select issues
) //.ToList()
;
// string sql = someQuery.ToString();
// string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions.ToSql(someQuery);
// string sql = Microsoft.EntityFrameworkCore.IQueryableExtensions1.ToSql(someQuery);
// using Microsoft.EntityFrameworkCore;
string sql = someQuery.ToSql();
System.Console.WriteLine(sql);
And then these extension methods (IQueryableExtensions1 for .NET Core 1.0, IQueryableExtensions for .NET Core 2.0) :
using System;
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Remotion.Linq.Parsing.Structure;
namespace Microsoft.EntityFrameworkCore
{
// https://stackoverflow.com/questions/1412863/how-do-i-view-the-sql-generated-by-the-entity-framework
// http://rion.io/2016/10/19/accessing-entity-framework-core-queries-behind-the-scenes-in-asp-net-core/
public static class IQueryableExtensions
{
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields
.First(x => x.Name == "_queryCompiler");
private static readonly PropertyInfo NodeTypeProviderField =
QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name == "NodeTypeProvider");
private static readonly MethodInfo CreateQueryParserMethod =
QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name == "CreateQueryParser");
private static readonly FieldInfo DataBaseField =
QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
private static readonly PropertyInfo DatabaseDependenciesField =
typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
{
if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
{
throw new ArgumentException("Invalid query");
}
var queryCompiler = (QueryCompiler) QueryCompilerField.GetValue(query.Provider);
var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
var parser = (IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
var queryModel = parser.GetParsedQuery(query.Expression);
var database = DataBaseField.GetValue(queryCompiler);
var databaseDependencies = (DatabaseDependencies) DatabaseDependenciesField.GetValue(database);
var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
var sql = modelVisitor.Queries.First().ToString();
return sql;
}
}
public class IQueryableExtensions1
{
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo()
.DeclaredFields
.First(x => x.Name == "_queryCompiler");
private static readonly PropertyInfo NodeTypeProviderField =
QueryCompilerTypeInfo.DeclaredProperties.Single(x => x.Name == "NodeTypeProvider");
private static readonly MethodInfo CreateQueryParserMethod =
QueryCompilerTypeInfo.DeclaredMethods.First(x => x.Name == "CreateQueryParser");
private static readonly FieldInfo DataBaseField =
QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
private static readonly FieldInfo QueryCompilationContextFactoryField = typeof(Database).GetTypeInfo()
.DeclaredFields.Single(x => x.Name == "_queryCompilationContextFactory");
public static string ToSql<TEntity>(IQueryable<TEntity> query) where TEntity : class
{
if (!(query is EntityQueryable<TEntity>) && !(query is InternalDbSet<TEntity>))
{
throw new ArgumentException("Invalid query");
}
var queryCompiler = (IQueryCompiler) QueryCompilerField.GetValue(query.Provider);
var nodeTypeProvider = (INodeTypeProvider) NodeTypeProviderField.GetValue(queryCompiler);
var parser =
(IQueryParser) CreateQueryParserMethod.Invoke(queryCompiler, new object[] {nodeTypeProvider});
var queryModel = parser.GetParsedQuery(query.Expression);
var database = DataBaseField.GetValue(queryCompiler);
var queryCompilationContextFactory =
(IQueryCompilationContextFactory) QueryCompilationContextFactoryField.GetValue(database);
var queryCompilationContext = queryCompilationContextFactory.Create(false);
var modelVisitor = (RelationalQueryModelVisitor) queryCompilationContext.CreateQueryModelVisitor();
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
var sql = modelVisitor.Queries.First().ToString();
return sql;
}
}
}
Well, I am using Express profiler for that purpose at the moment, the drawback is that it only works for MS SQL Server. You can find this tool here: https://expressprofiler.codeplex.com/
While there are good answers here, none solved my problem completely (I wished to get the entire SQL statement, including Parameters, from the DbContext from any IQueryable. The following code does just that. It is a combination of code snippets from Google. I have only tested it with EF6+.
Just an aside, this task took me way longer than I thought it would. Abstraction in Entity Framework is a bit much, IMHO.
First the using. You will need an explicit reference to 'System.Data.Entity.dll'.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data.Common;
using System.Data.Entity.Core.Objects;
using System.Data.Entity;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Reflection;
The following class converts an IQueryable into a DataTable. Modify as your need may be:
public class EntityFrameworkCommand
{
DbContext Context;
string SQL;
ObjectParameter[] Parameters;
public EntityFrameworkCommand Initialize<T>(DbContext context, IQueryable<T> query)
{
Context = context;
var dbQuery = query as DbQuery<T>;
// get the IInternalQuery internal variable from the DbQuery object
var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var iq = iqProp.GetValue(dbQuery, null);
// get the ObjectQuery internal variable from the IInternalQuery object
var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
var objectQuery = oqProp.GetValue(iq, null) as ObjectQuery<T>;
SQL = objectQuery.ToTraceString();
Parameters = objectQuery.Parameters.ToArray();
return this;
}
public DataTable GetData()
{
DataTable dt = new DataTable();
var connection = Context.Database.Connection;
var state = connection.State;
if (!(state == ConnectionState.Open))
connection.Open();
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = SQL;
foreach (var p in Parameters)
{
var param = cmd.CreateParameter();
param.Name = "#" + p.Name;
param.Value = p.Value;
cmd.Parameters.Add(param);
}
using (var da = DbProviderFactories.GetFactory(connection).CreateDataAdapter())
{
da.SelectCommand = cmd;
da.Fill(dt);
}
}
if (!(state == ConnectionState.Open))
connection.Close();
return dt;
}
}
To use, simply call it as below:
var context = new MyContext();
var data = ....//Query, return type can be anonymous
.AsQueryable();
var dt = new EntityFrameworkCommand()
.Initialize(context, data)
.GetData();
For me, using EF6 and Visual Studio 2015 I entered query in the immediate window and it gave me the generated SQL Statement
In my case for EF 6+, instead of using this in the Immediate Window to find the query string:
var sql = ((System.Data.Entity.Core.Objects.ObjectQuery)query).ToTraceString();
I ended up having to use this to get the generated SQL command:
var sql = ((System.Data.Entity.Infrastructure.DbQuery<<>f__AnonymousType3<string,string,string,short,string>>)query).ToString();
Of course your anonymous type signature might be different.
HTH.
I've just done this:
IQueryable<Product> query = EntitySet.Where(p => p.Id == id);
Debug.WriteLine(query);
And the result shown in the Output:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Code] AS [Code],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[FileName] AS [FileName],
FROM [dbo].[Products] AS [Extent1]
INNER JOIN [dbo].[PersistedFiles] AS [Extent2] ON [Extent1].[PersistedFileId] = [Extent2].[Id]
WHERE [Extent1].[Id] = #p__linq__0
Entity Framework 4 Solution
Most of the answers here were EF6-specific. Here's one for those of you still using EF4.
This method replaces the #p__linq__0/etc. parameters with their actual values, so you can just copy and paste the output into SSMS and run it or debug it.
/// <summary>
/// Temporary debug function that spits out the actual SQL query LINQ is generating (with parameters)
/// </summary>
/// <param name="q">IQueryable object</param>
private string Debug_GetSQLFromIQueryable<T>(IQueryable<T> q)
{
System.Data.Objects.ObjectQuery oq = (System.Data.Objects.ObjectQuery)q;
var result = oq.ToTraceString();
List<string> paramNames = new List<string>();
List<string> paramVals = new List<string>();
foreach (var parameter in oq.Parameters)
{
paramNames.Add(parameter.Name);
paramVals.Add(parameter.Value == null ? "NULL" : ("'" + parameter.Value.ToString() + "'"));
}
//replace params in reverse order, otherwise #p__linq__1 incorrectly replaces #p__linq__10 for instance
for (var i = paramNames.Count - 1; i >= 0; i--)
{
result = result.Replace("#" + paramNames[i], paramVals[i]);
}
return result;
}
Starting from Entity Framework Core 5.0+ one can simply override the OnConfiguring method in the DbContext once for logging. This works also for Single() or Any() queries.
For logging to debug window:
public class ExampleDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// using System.Diagnostics;
optionsBuilder.LogTo(message => Debug.WriteLine(message));
}
}
For logging to console:
public class ExampleDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.LogTo(Console.WriteLine);
}
}
See here for more details including log levels and filtering: https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/simple-logging
If you want to have parameter values (not only #p_linq_0 but also their values) too, you can use IDbCommandInterceptor and add some logging to ReaderExecuted method.