Make Configuration changes to DbContext - entity-framework-core

I'm using Code First. I've created a DbContext but I'm not able to put the configuration settings in the constructor as I did in the regular Entity Framework. Here's an example in regular EF6:
public AppsDbContext()
: base("name=AppsDbContext")
{
this.Configuration.AutoDetectChangesEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.UseDatabaseNullSemantics = true;
this.Configuration.ValidateOnSaveEnabled = false;
}
I'm not able to do this in an EF-Core DbContext constructor. What I noticed immediately is that I got this functionality from the System.Data.Entity namespace in EF, which apparently no longer exists. I do not see a way I can add this namespace. What am I missing? I'm guessing this is fairly trivial but I have not seen any examples in my search.
NOTE: I'm using EF Core 2.2

you can use ChangeTracker property instead of Configuration in EF-Core.
this.ChangeTracker.LazyLoadingEnabled = true;
this.ChangeTracker.AutoDetectChangesEnabled = false;

Related

EF Core 2.1 In memory DB not updating records

I'm using the in memory database provider for integration tests however I don't seem to be able to update a record. I've run the same code against a real SQL database and everything gets updated fine. Here is my test fixture code.
Test Fixture:
public class TestFixture<TStartup> : IDisposable
{
private readonly TestServer _testServer;
public HttpClient TestClient { get; }
public IDatabaseService DbContext { get { return _testServer.Host.Services.GetService<DatabaseService>(); } }
public TestFixture() : this(Path.Combine("src")) { }
protected TestFixture(string relativeTargetProjectPatentDir)
{
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Testing");
var builder = new WebHostBuilder()
.ConfigureServices(services =>
{
services.AddDbContext<DatabaseService>(options =>
options.UseInMemoryDatabase("TestDB")
.EnableSensitiveDataLogging());
})
.UseEnvironment("Testing")
.UseStartup<Startup>();
_testServer = new TestServer(builder)
{
BaseAddress = new Uri("http://localhost:5010")
};
TestClient = _testServer.CreateClient();
TestClient.BaseAddress = _testServer.BaseAddress;
}
public void Dispose()
{
TestClient.Dispose();
_testServer.Dispose();
}
}
I've spent most of the day googling this and not come across any other people talking about it so I'm assuming its probably my issue rather than a EF bug. I'm sure someone would have noticed a DB that you can't update.
Updating works with Singleton but I have CQRS architecture and to check if the entry was updated in e2e test I have to reload entry
Context.Entry(entity).Reload();
I hope that this can help someone
It turned out that changing the lifetime of my DbContext in my test fixture to singleton solved my issue.
Well it can be that DbContext is used in wrong way. I had the same problem. I used the DbContext in same way as you. I simply returned the instance by .Host.Services.GetService<TContext>. The problem with this approach is that DbContext will never release tracked entities so either you set entity State as EntityState.Detached and you force DbContext to reload it, or you will use scopes.
using (var scope = _testServer.Host.Services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DatabaseService>();
//make any operations on dbContext only in scope
}
Adding to Chris's answer. Here is an example of what I had vs. what fixed the issue:
services.AddDbContext<TestDbContext>(options => {
options.UseInMemoryDatabase("TestDb");
});
to
var options = new DbContextOptionsBuilder<TestDbContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;
services.AddSingleton(x => new TestDbContext(options));
Using AsNoTracking behavior may additionally work below,
services.AddDbContext<TestDbContext>(
a => a.UseInMemoryDatabase(databaseName: "TestDb").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking),
ServiceLifetime.Singleton)
Also, how are you updating record? This seems to track in EFCore InMemory,
_dbContext.Entry(modifyItem).State = EntityState.Modified;
However, this doesn't seem to work as much.
_dbContext.Entry(existingItem).CurrentValues.SetValues(modifyItem);

Entity Framework Core 1.0 CurrentValues.SetValues() does not exist

I'm attempting to update an entity and its related child entities using Entity Framework Core 1.0 RC 1, where the entities are detached from DbContext. I've done this previously using a solution similar to the one described in this answer.
However, it seems that we are no longer able to do the following using Entity Framework 7:
DbContext.Entry(existingPhoneNumber).CurrentValues.SetValues();
Visual Studio complains that:
EntityEntry does not contain a definition for 'CurrentValues'
etc...
I presume this means that this has not (yet?) been implemented for EF Core 1.0? Apart from manually updating the properties, is there any other solution?
As you have noticed, this API is not implemented yet in EF Core. See this work item: https://github.com/aspnet/EntityFramework/issues/1200
I know this is an old question but I ran into this issue today, and it appears it still isn't implemented in EF Core. So I wrote an extension method to use in the meantime that will update any object's properties with the matching values of any other object.
public static class EFUpdateProperties
{
public static TOrig UpdateProperties<TOrig, TDTO>(this TOrig original, TDTO dto)
{
var origProps = typeof(TOrig).GetProperties();
var dtoProps = typeof(TDTO).GetProperties();
foreach(PropertyInfo dtoProp in dtoProps)
{
origProps
.Where(origProp => origProp.Name == dtoProp.Name)
.Single()
.SetMethod.Invoke(original, new Object[]
{
dtoProp.GetMethod.Invoke(dto, null) });
}
);
return original;
}
}
Usage:
public async Task UpdateEntity(EditViewModel editDto)
{
// Get entry from context
var entry = await _context.Items.Where(p => p.ID == editDto.Id).FirstOrDefaultAsync();
// Update properties
entry.UpdateProperties(editDto);
// Save Changes
await _context.SaveChangesAsync();
}

Verify If Entity Was Modified In Entity Framework Code First

Is there an easy way to verify if an entity was updated instead of keeping tracket of a flag when testing if some properties have changed on a code first entity framework entity?
bool hasChanges = false;
if (existingEvent.Gender != tournament.Gender)
{
hasChanges = true;
existingEvent.Gender = tournament.Gender;
}
I found out I can do it like this.
DatabaseFactory.Get().Entry(existingEvent).State

DbContext AutoDetectChangesEnabled set to false detecting changes

I'm a bit stumped. From what I've read setting the DbContext.AutoDetectChangesEnabled to false should disable change tracking requiring one to call DbContext.DetectChanges in order to identify changes to be sent to the database.
However, it is clear from my logs below that the changes are being registered by dbContexts change tracker, even with the setting set to false.
Am I missing something?
Entity Framework Version: 5.0.0.0
DbContext class
public class ProjectContext : DbContext {
public DbSet<Project> Projects {get;set;}
}
Controller class
private ProjectContext db = new ProjectContext();
public method(){
Project p = new Project("uniqueName");
db.Configuration.AutoDetectChangesEnabled = false;
db.Projects.Add(p);
DebugChangeTracker();
db.SaveChanges();
db.Projects.First().ProjectName = "a differentName!";
DebugChangeTracker();
db.SaveChanges();
}
Logging method
private void DebugChangeTracker()
{
var path = "C:\\mypath\\";
path = path + Util.GetMsSinceEpoch().ToString() + "changeTracker.log";
using (StreamWriter sw = new StreamWriter(path))
{
var changeTracker = db.ChangeTracker;
var entries = changeTracker.Entries();
foreach (var x in entries)
{
var name = x.Entity.ToString();
var state = x.State;
sw.WriteLine("");
sw.WriteLine("***Entity Name: " + name +
"is in a state of " + state);
var currentValues = x.CurrentValues;
sw.WriteLine("***CurrentValues***");
PrintPropertyValues(currentValues,sw);
if (state != EntityState.Added)
{
sw.WriteLine("***Original Values***");
PrintPropertyValues(x.OriginalValues,sw);
}
}
}
}
First log
***Entity Name: Models.Projectis in a state of Added
***CurrentValues***
ProjectId:0
ProjectName:uniqueName
Second Log
***Entity Name: Models.Projectis in a state of Modified
***CurrentValues***
ProjectId:1
ProjectName:uniqueName
***Original Values***
ProjectId:1
ProjectName:a differentName!
Setting AutoDetectChangesEnabled to false doesn't disable change tracking. (That's what the AsNoTracking() extension method would do.) It just disables the automatic call of DetectChanges that would otherwise occur in many DbContext API methods.
But DetectChanges isn't the only method that participates in change tracking. However, if you don't call it manually at the right places where it is needed the tracked entity states are incomplete or wrong leading to incorrectly saved data.
In your case the state Added in the first part of your method is expected, even with AutoDetectChangesEnabled set to false because you only call db.Projects.Add(p). (The line is missing in your code btw, but I guess it's just a copy and paste error.) Calling a method from the DbContext API tracks changes correctly and the states in the tracker will be correct if the state was correct before the call to Add.
Or in other words: Calling an API method doesn't turn a correct state into a wrong state. But: If AutoDetectChangesEnabled is false it also won't turn a wrong state into a correct state which would be the case if AutoDetectChangesEnabled is true.
However, in the second part of your method you are just changing a POCO property value. After this point the change tracker state is wrong (Unchanged) and without a call to DetectChanges (manually or - if AutoDetectChangesEnabled is true - automatically in ChangeTracker.Entries or SaveChanges) it will never be adjusted. The effect is that the changed property value is not saved to the database.
In the last section mentioning the state Unchanged I'm refering to my own test (and also to what I would expect). I don't know and can't reproduce why you have state Modified.
Sorry, if this sounds all a bit confusing. Arthur Vickers can explain it better.
I find automatic change detection and the behaviour when disabling it rather difficult to understand and to master and I usually don't touch the default (AutoDetectChangesEnabled = true) for any tracked changes that are more complex than the simplest things (like bulk adding entities in a loop, etc.).
If someone looking for AutoDetectChangesEnabled in Entity Framework Core you can find it under ChangeTracker insted of Configuration
Usage like:
context.ChangeTracker.AutoDetectChangesEnabled = false;
//Do something here
context.PriceRecords.Add(newPriceRecord);
context.ChangeTracker.AutoDetectChangesEnabled = true;
according to Entity Framework Automatic Detect Changes's Article
they said:
you may get significant performance improvements by turning it off in some cases
look at this example from that article
using (var context = new BloggingContext())
{
try
{
context.Configuration.AutoDetectChangesEnabled = false;
// Make many calls in a loop
foreach (var blog in aLotOfBlogs)
{
context.Blogs.Add(blog);
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = true;
}
}
This code avoids unnecessary calls to DetectChanges that would have occurred while calling the DbSet.Add and SaveChanges methods.

Are EF code-first models intended to fully describe a database's structure?

I'm a little confused as to the purpose of a data model in Entity Framework code-first. Because EF will auto-generate a database from scratch for you if it doesn't already exist using nothing more than the data model (including data annotations and Fluent API stuff in DbContext.OnModelCreating), I was assuming that the data model should fully describe your database's structure, and you wouldn't need to modify anything fundamental after that.
However, I came across this Codeplex issue in which one of the EF Triage Team members suggests that custom indexes be added in data migrations, but not as annotations to your data model fields, or Fluent API code.
But wouldn't that mean that anyone auto-generating the database from scratch would not get those custom indexes added to their DB? The assumption seems to be that once you start using data migrations, you're never going to create the database from scratch again. What if you're working in a team and a new team member comes along with a new SQL Server install? Are you expected to copy over a database from another team member? What if you want to start using a new DBMS, like Postgres? I thought one of the cool things about EF was that it was DBMS-independent, but if you're no longer able to create the database from scratch, you can no longer do things in a DBMS-independent way.
For the reasons I outlined above, wouldn't adding custom indexes in a data migration but not in the data model be a bad idea? For that matter, wouldn't adding any DB structure changes in a migration but not in the data model be a bad idea?
Are EF code-first models intended to fully describe a database's structure?
No, they don't fully describe the database structure or schema.Still there are methods to make the database fully described using EF. They are as below:
You can use the new CTP5’s ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database.
The best place to invoke SqlCommand method for this purpose is inside a Seed method that has been overridden in a custom Initializer class. For example:
protected override void Seed(EntityMappingContext context)
{
context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}
You can even add Unique Constraints this way.
It is not a workaround, but will be enforced as the database will be generated.
OR
If you are badly in need of the attribute, then here it goes
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
public IndexAttribute(string name, bool unique = false)
{
this.Name = name;
this.IsUnique = unique;
}
public string Name { get; private set; }
public bool IsUnique { get; private set; }
}
After this , you will have an initializer which you will call in your OnModelCreating method as below:
public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";
public void InitializeDatabase(T context)
{
const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
string query = string.Empty;
foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(p => p.PropertyType.Name == typeof(DbSet<>).Name))
{
var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();
TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);
indexes.Clear();
string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;
foreach (PropertyInfo property in entityType.GetProperties(PublicInstance))
{
IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
{
ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);
foreach (IndexAttribute indexAttribute in indexAttributes)
{
if (!indexes.ContainsKey(indexAttribute))
{
indexes.Add(indexAttribute, new List<string>());
}
if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
{
string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
indexes[indexAttribute].Add(columnName);
}
else
{
indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
}
}
}
}
foreach (IndexAttribute indexAttribute in indexes.Keys)
{
query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
.Replace("{tableName}", tableName)
.Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
.Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
}
}
if (context.Database.CreateIfNotExists())
{
context.Database.ExecuteSqlCommand(query);
}
}
private string GetKeyName(Type type)
{
PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
return propertyInfo.Name;
}
throw new Exception("No property was found with the attribute Key");
}
}
Then overload OnModelCreating in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer(new IndexInitializer<MyContext>());
base.OnModelCreating(modelBuilder);
}
Apply the index attribute to your Entity type, with this solution you can have multiple fields in the same index just use the same name and unique.
OR
You can do the migrations later on.
Note:
I have taken a lot of this code from here.
The question seems to be if there is value in having migrations added mid-stream, or if those will cause problems for future database initializations on different machines.
The initial migration that is created also contains the entire data model as it exists, so by adding migrations (enable-migrations in the Package Manager Console) you are, in effect, creating the built-in mechanism for your database to be properly created down the road for other developers.
If you're doing this, I do recommend modifying the database initialization strategy to run all your existing migrations, lest EF should start up and get the next dev's database out of sync.
Something like this would work:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourNamespace.YourDataContext, Migrations.Configuration>());
So, no, this won't inherently introduce problems for future work/developers. Remember that migrations are just turned into valid SQL that executes against the database...you can even use script mode to output the TSQL required to make the DB modifications based on anything in the migrations you have created.
Cheers.