I am reading EF's source code and found this method below. According to the method name, it make sure that the dbcontext is loaded. When I test this with EF Codefirst sample, this method is added the current assembly (my sample console) to "_knownAssemblies"..
I don't see any code of loading the assembly. And I don't see any code that checks whether the assembly is loaded or not.
Is that the naming issue or Did I miss out something? Thanks in advance.
public virtual void EnsureLoadedForContext(Type contextType)
{
DebugCheck.NotNull(contextType);
Debug.Assert(typeof(DbContext).IsAssignableFrom(contextType));
var contextAssembly = contextType.Assembly;
if (contextType == typeof(DbContext)
|| _knownAssemblies.ContainsKey(contextAssembly))
{
return;
}
if (_configurationOverrides.IsValueCreated)
{
lock (_lock)
{
if (_configurationOverrides.Value.Count != 0)
{
return;
}
}
}
if (!ConfigurationSet)
{
var foundConfigurationType =
_loader.TryLoadFromConfig(AppConfig.DefaultInstance) ??
_finder.TryFindConfigurationType(contextType);
if (foundConfigurationType != null)
{
SetConfigurationType(foundConfigurationType);
}
}
else if (!contextAssembly.IsDynamic // Don't throw for proxy contexts created in dynamic assemblies
&& !_loader.AppConfigContainsDbConfigurationType(AppConfig.DefaultInstance))
{
var foundType = _finder.TryFindConfigurationType(contextType);
if (foundType != null)
{
if (_configuration.Value.Owner.GetType() == typeof(DbConfiguration))
{
throw new InvalidOperationException(Strings.ConfigurationNotDiscovered(foundType.Name));
}
if (foundType != _configuration.Value.Owner.GetType())
{
throw new InvalidOperationException(
Strings.SetConfigurationNotDiscovered(_configuration.Value.Owner.GetType().Name, contextType.Name));
}
}
}
_knownAssemblies.TryAdd(contextAssembly, null);
}
The method EnsureLoadedForContext does not load the context but loads a configuration for the context type passed to the method. When you look at the name of the method with the type name on which the method is created (DbConfigurationManager.EnsureLoadedForContext) it is much more clear that the method is related to loading a configuration rather than loading a context. Finally you can look at a comment to one of the bugs which reads:
EnsureLoadedForContext is called from various places as soon as a context type is known to ensure that the correct DbConfiguration is found.
Related
UPDATED: I have a piece of code that creates records when they don't exist, or updates them when they do exist. However, while trying to update the records I get this exception:
Microsoft.EntityFrameworkCore.DbUpdateException The DELETE statement
conflicted with the REFERENCE constraint
public static string AddCurrencies(ApplicationDbContext db)
{
// ...
foreach (Currency c in db.Currency.ToList())
{
try
{
db.Remove(c); // the troublemaker!
db.SaveChanges();
}
catch
{
// probably in use (foreign key)
}
}
// ...
foreach (Currency c in CurrencyList)
{
var c_db = db.Currency.FirstOrDefault(x => x.Code == c.Code);
if (c_db == null)
{
// adding
db.Currency.Add(c);
}
else
{
// updating
c_db.Name = c.Name;
c_db.LocalDisplay = c.LocalDisplay;
}
db.SaveChanges(); // exception fired if updating!
}
// ...
}
After some investigation, and having being able to turn SQL debugging on, I found out that the Remove() "persists" and that it will be retried with the second call to SaveChanges(), hence the exception. Now the question is reformulated: how do I "undo" (in the lack of a better expression) the Remove() commands that failed?
I managed to solve this issue this way:
var entry = context.Entry(entity);
entry.Reload();
for each entry where delete failed.
Inspired by ruby on rails I want to add a delete callback to entity framework. I've started by overriding SaveChanges() to loop over the tracked entities and create an interface which is called whenever an entity gets deleted.
var changedEntities = ChangeTracker.Entries();
foreach (var changedEntity in changedEntities)
{
if (changedEntity.Entity is IBeforeDelete && changedEntity.State == EntityState.Deleted)
{
IBeforeDelete saveChange = (IBeforeDelete)changedEntity.Entity;
saveChange.BeforeDelete(this, changedEntity);
}
}
This works quite well, but I figured one problem and I don't know how to solve that. If an entity gets deleted within the callback, the Changetracker needs to be updated to resprect the newly deleted items. How can I solve that? Or is there another solution? Or do I do it wrong?
Good question. If I understand you correctly, your BeforeDelete implementations might delete a different entry that also needs to have BeforeDelete called on it. This could recursively go forever, so the only thing I could think of would be to recursively check the change tracker entries to see if new ones were added after the last batch was processed.
Untested:
public override int SaveChanges()
{
var processed = new List<DbEntityEntry>();
var entries = ChangeTracker.Entries();
do
{
foreach (var entry in entries)
{
processed.Add(entry);
if (entry.Entity is IBeforeDelete && entry.State == EntityState.Deleted)
{
IBeforeDelete saveChange = (IBeforeDelete)entry.Entity;
saveChange.BeforeDelete(this, entry);
}
}
} while ((entries = ChangeTracker.Entries().Except(processed)).Any());
return base.SaveChanges();
}
you can filter out uncommited entity changes...
var changedEntities = Context.ChangeTracker
.Entries<TEntity>()
.Where(e => e.State != EntityState.Detached)
.Select(e => e.Entity);
and lock on the rest in your "if" block
lock(changedEntity.Entity){ ... interface code }
So, this is the problem I am stuck at for a few weeks.
I am developing an Eclipse plugin which fills in a View with custom values depending on a particular StackFrame selection in the Debug View.
In particular, I want to listen to the stackframe selected and would like to get the underlying IStackFrame object.
However, I have tried more than a dozen things and all of them have failed. So I tried adding DebugContextListener to get the DebugContextEvent and finally the selection. However, the main issue is that ISelection doesn't return the underlying IStackFrame object. It instead returns an object of type AbstractDMVMNode.DMVMContext. I tried getting an adapter but that didn't work out too. I posted this question sometime back also:
Eclipse Plugin Dev- Extracting IStackFrame object from selection in Debug View
Since then, I have tried out many different approaches. I tried adding IDebugEventSetListener (but this failed as it cannot identify stack frame selection in the debug view).
I tried adding an object contribution action but this too was pointless as it ultimately returned me an ISelection which is useless as it only returns me an object of class AbstractDMVMNode.DMVMContext and not IStackframe.
Moreover, I checked out the implementation of the VariablesView source code itself in the org.eclipse.debug.ui plugin. It looks like a few versions back (VariablesView source code in version 3.2), the underlying logic was to use the ISelection and get the IStackFrame. All the other resources on the internet also advocate the same. However, now, this scheme no longer works as ISelection doesn't return you an IStackFrame. Also, the source code for the latest eclipse Debug plugin (which doesn't use this scheme) was not very intuitive for me.
Can anyone tell how I should proceed ? Is hacking the latest Eclipse source code for VariablesView my only option ? This doesn't look like a good design practice and I believe there should be a much more elegant way of doing this.
PS: I have tried all the techniques and all of them return an ISelection. So, if your approach too return the same thing, then it is most likely incorrect.
Edit (Code snippet for trying to adapt the ISelection):
// Following is the listener implemnetation
IDebugContextListener flistener = new IDebugContextListener() {
#Override
public void debugContextChanged(DebugContextEvent event) {
if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
contextActivated(event.getContext());
}
};
};
// Few things I tried in the contextActivated Method
//Attempt 1 (Getting the Adapter):
private void contextActivated(ISelection context) {
if (context instanceof StructuredSelection) {
Object data = ((StructuredSelection) context).getFirstElement();
if( data instanceof IAdaptable){
System.out.println("check1");
IStackFrame model = (IStackFrame)((IAdaptable)data).getAdapter(IStackFrame.class);
if(model != null){
System.out.println("success" + model.getName());
}
}
}
}
// Attemp2 (Directly getting it from ISelection):
private void contextActivated(ISelection context) {
if (context instanceof StructuredSelection) {
System.out.println("a");
Object data = ((StructuredSelection) context).getFirstElement();
if (data instanceof IStackFrame) {
System.out.println("yes");
} else {
System.out.println("no" + data.getClass().getName());
}
}
// This always execute the else and it prints: org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode.DMVMContext
}
// Attemp3 (Trying to obtain it from the viewer (similiar to object action binding in some ways):
private void contextActivated(ISelection context) {
VariablesView variablesView = (VariablesView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(IDebugUIConstants.ID_VARIABLE_VIEW);
if (variablesView != null) {
Object input = ((TreeViewer) variablesView.getViewer()).getInput();
if(input != null) System.out.println(input.getClass().getName());
if (input instanceof IStackFrame) {
System.out.println("success");
} else if (input instanceof IThread) {
System.out.println("success");
try {
IStackFrame[] stackFrames = ((IThread) input).getStackFrames();
for (IStackFrame iStackFrame : stackFrames) {
printVariables(iStackFrame);
}
} catch (DebugException e) {
e.printStackTrace();
}
}
}
}
While I am building this view to work with both JDT & CDT, I am testing it out on the C project. So, this might be the reason why I always get the returned object type as AbstractDMVMNode.DMVMContext. Should my implementation be different to handle both these cases ? I believe I should be building a generic view. Also, if AbstractDMVMNode.DMVMContext is CDT specific, I should I go about implementing it for the CDT case?
I am using EntityFramework Core commands to migration database. The command I am using is like the docs suggests: dnx . ef migration apply. The problem is when specifying AttachDbFileName in connection string, the following error appear: Unable to Attach database file as database xxxxxxx. This is the connection string I am using:
Data Source=(LocalDB)\mssqllocaldb;Integrated Security=True;Initial Catalog=EfGetStarted2;AttachDbFileName=D:\EfGetStarted2.mdf
Please help how to attach the db file to another location.
Thanks
EF core seem to have troubles with AttachDbFileName or doesn't handle it at all.
EnsureDeleted changes the database name to master but keeps any AttachDbFileName value, which leads to an error since we cannot attach the master database to another file.
EnsureCreated opens a connection using the provided AttachDbFileName value, which leads to an error since the file of the database we want to create does not yet exist.
EF6 has some logic to handle these use cases, see SqlProviderServices.DbCreateDatabase, so everything worked quite fine.
As a workaround I wrote some hacky code to handle these scenarios:
public static void EnsureDatabase(this DbContext context, bool reset = false)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (reset)
{
try
{
context.Database.EnsureDeleted();
}
catch (SqlException ex) when (ex.Number == 1801)
{
// HACK: EF doesn't interpret error 1801 as already existing database
ExecuteStatement(context, BuildDropStatement);
}
catch (SqlException ex) when (ex.Number == 1832)
{
// nothing to do here (see below)
}
}
try
{
context.Database.EnsureCreated();
}
catch (SqlException ex) when (ex.Number == 1832)
{
// HACK: EF doesn't interpret error 1832 as non existing database
ExecuteStatement(context, BuildCreateStatement);
// this takes some time (?)
WaitDatabaseCreated(context);
// re-ensure create for tables and stuff
context.Database.EnsureCreated();
}
}
private static void WaitDatabaseCreated(DbContext context)
{
var timeout = DateTime.UtcNow + TimeSpan.FromMinutes(1);
while (true)
{
try
{
context.Database.OpenConnection();
context.Database.CloseConnection();
}
catch (SqlException)
{
if (DateTime.UtcNow > timeout)
throw;
continue;
}
break;
}
}
private static void ExecuteStatement(DbContext context, Func<SqlConnectionStringBuilder, string> statement)
{
var builder = new SqlConnectionStringBuilder(context.Database.GetDbConnection().ConnectionString);
using (var connection = new SqlConnection($"Data Source={builder.DataSource}"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = statement(builder);
command.ExecuteNonQuery();
}
}
}
private static string BuildDropStatement(SqlConnectionStringBuilder builder)
{
var database = builder.InitialCatalog;
return $"drop database [{database}]";
}
private static string BuildCreateStatement(SqlConnectionStringBuilder builder)
{
var database = builder.InitialCatalog;
var datafile = builder.AttachDBFilename;
var dataname = Path.GetFileNameWithoutExtension(datafile);
var logfile = Path.ChangeExtension(datafile, ".ldf");
var logname = dataname + "_log";
return $"create database [{database}] on primary (name = '{dataname}', filename = '{datafile}') log on (name = '{logname}', filename = '{logfile}')";
}
It's far from nice, but I'm using it for integration testing anyway. For "real world" scenarios using EF migrations should be the way to go, but maybe the root cause of this issue is the same...
Update
The next version will include support for AttachDBFilename.
There may be a different *.mdf file already attached to a database named EfGetStarted2... Try dropping/detaching that database then try again.
You might also be running into problems if the user LocalDB is running as doesn't have correct permissions to the path.
I retrieve a stay via a WCF service and pass it to the front end. A user updates the data on that object and then its passed back to the WCF service.
The following code works fine to add new stays, but it silently ignores updates. What am I missing?
public void UpdateStay(ResidentDataTypes.Stay stay)
{
using (ResidentDataTypes.ResidentEntities entity = new ResidentDataTypes.ResidentEntities())
{
if (stay.StayId == Guid.Empty)
{
entity.Stay.AddObject(stay);
}
else
{
entity.Stay.ApplyChanges(stay);
}
entity.SaveChanges();
}
}
When your objects serialized from WCF they are in Detached state and EF will not generate update statement for them. You have 2 options:
1. Get the same object from the DB and then do a ObjectSet.ApplyCurrentValues:
using (ResidentDataTypes.ResidentEntities context = new
ResidentDataTypes.ResidentEntities())
{
if (stay.StayId == Guid.Empty)
{
context.Stay.AddObject(stay);
}
else
{
// Fetch the Stay object into the cache:
context.Stay.First(s => s.StayId == stay.StayId);
// Now ApplyCurrentValues from the stay object coming from WCF:
context.Stay.ApplyCurrentValues(stay);
}
entity.SaveChanges();
}
2.Manually change the Detached state to Modified:
using (ResidentDataTypes.ResidentEntities context = new
ResidentDataTypes.ResidentEntities())
{
if (stay.StayId == Guid.Empty)
{
context.Stay.AddObject(stay);
}
else
{
// Attach the stay object coming from WCF to ObjectContext:
context.Stay.Attach(stay);
// The attached object is going into Unchanged mode after attaching
// so we need to change the state to Modified:
context.ObjectStateManager.ChangeObjectState(stay, EntityState.Modified);
}
entity.SaveChanges();
}
i think you have to attach your entity to your context with something like
entity.Attach(stay)