Dynamic Data with multiple Entity Framework models - entity-framework

I am using Dynamic Data with Entity framework models.
If I use this with 1 EF model, then this works like a charm.
But now I need to use multiple EF models in my Dynamic Data Project and I'm receiving errors during the registration process.
Code:
public static void RegisterRoutes(RouteCollection routes)
{
var model1 = new MetaModel();
model1.RegisterContext(() =>
{
return ((IObjectContextAdapter)new Model1Entities()).ObjectContext;
}, new ContextConfiguration() { ScaffoldAllTables = true });
routes.Add(new DynamicDataRoute("model1/{table}/{action}.aspx")
{
Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
Model = model1
});
var model2 = new MetaModel();
model2.RegisterContext(() =>
{
return ((IObjectContextAdapter)new Model2Entities()).ObjectContext;
}, new ContextConfiguration() { ScaffoldAllTables = true });
routes.Add(new DynamicDataRoute("model2/{table}/{action}.aspx")
{
Constraints = new RouteValueDictionary(new { action = "List|Details|Edit|Insert" }),
Model = model2
});
}
On runtime I'm receiving an error when he's executing model2.RegisterContext .
Error:
Item has already been added. Key in dictionary:
'System.Data.Objects.ObjectContext' Key being added:
'System.Data.Objects.ObjectContext'
So for model1 he can register the context but for model2 he's blocked on this error.
If you know how to solve this, please advise!

I got it to work in 2 steps:
by removing the '.tt' files in my entity model.
in your edmx model, putting the property 'Code Generation Strategy' to 'Default' of your model. (and rebuild your solution)
After this he accepted the registration of multiple entities.

Hy Tom,
I have the same issue since more than one month. I was investigating many time into this, since it is blocking my project. I started a forum entry here:
http://forums.asp.net/t/1946475.aspx?Duplicated+key+when+try+to+register+multiple+ObjectContexts+in+Dynamic+Data
It worked in previous versions.
There was a workaround (see http://blog.davidebbo.com/2011/01/using-dynamic-data-with-ef-code-first.html) but this does not help anymore for current versions.
In short: it is an ASP.Net bug. Since we cannot call DbContext directly (which would be distinguishable) and we have to call ObjectContext, and the internal dictionary for the MetaModel uses the type without namespace as key (i.e. always "ObjectType"), there is no way around it.
Luckily, the ASP.Net team is now investigating into it now. Keep informed on the mentioned forum page.
UPDATE MARCH 2014: The current version http://blogs.msdn.com/b/webdev/archive/2014/02/28/announcing-the-release-of-dynamic-data-provider-and-entitydatasource-control-for-entity-framework-6.aspx fixes the problem.

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();
}

How can I create a generic update method for One to Many structures in Entity Framework 5?

I am writing a web application, such that I get different objects back from the web that need to be either updated or added to the database. On top of this, I need to check that the owner is not modified. Since a hacker could potentially get an account and send an update to modify the foreign key to the user model. I don't want to have to manually code all of these methods, instead I want to make a simple generic call.
Maybe something as simple as this
ctx.OrderLines.AddOrUpdateSet(order.OrderLines, a => a.Order)
Based on old persisted records that have a foreign key to Order, and on the new incoming records.
Delete old records that are not on the new records list.
Add new records that are not on the old records list.
Update new records that exist on both lists.
ctx.Entry(orderLine).State=EntityState.Deleted;
...
ctx.Entry(orderLine).State=EntityState.Added;
...
ctx.Entry(orderLine).State=EntityState.Modified;
This gets a bit complicated when the old record is loaded to verify that ownership did not change. I get an error if I don't do.
oldorder.OrderLines.remove(oldOrderLine); //for deletes
oldorder.OrderLines.add(oldOrderLine); //for adds
ctx.Entry(header).CurrentValues.SetValues(header); //for modifications
With Entity Framework 5 there is a new extension function called AddOrUpdate. And there was a very interesting (please read) blog entry on how to create this method before it was added.
I'm not sure if this is too much to ask as a question in StackOverflow, any clues on how to approach the problem may be sufficient. Here are my thoughts so far:
a) leverage AddOrUpdate for some of the functionality.
b) create a secondary context hoping to avoid loading order into the context and avoid extra calls.
c) Set the state of all the saved objects to initially deleted.
Since you have linked to this question from my own question, I thought I'd throw in some newly-aquired experience with Entity Framework for me.
To achieve a common save method in my generic repository with Entity Framework, I do this. (Please note that the Context is a member of my repository, as I am implementing the Unit of Work pattern as well)
public class EFRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
internal readonly AwesomeContext Context;
internal readonly DbSet<TEntity> DbSet;
public EFRepository(AwesomeContext context)
{
if (context == null) throw new ArgumentNullException("context");
Context = context;
DbSet = context.Set<TEntity>();
}
// Rest of implementation removed for brevity
public void Save(TEntity entity)
{
var entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
DbSet.Add(entity);
else entry.State = EntityState.Modified;
}
}
Honestly, I can't tell you why this works, because I just kept changing the state conditions - however I do have unit (integration) tests to prove that it works. Hopefully someone more into EF than myself can shed some light on this.
Regarding the "cascading updates", I was curious myself as if it would work using the Unit of Work pattern (my question I linked to was when I did not know it existed, and my repositories would basically create a unit of work whenever I wanted to save/get/delete, which is bad), so I threw in a test case in a simple relational DB. Here is a diagram to give you an idea.
IMPORTANT In order for test case number 2 to work, you need to make your POCO reference properties virtual, in order for EF to provide lazy loading.
The repository implementation is just derived from the generic EFRepository<TEntity> as shown above, so I'll leave out that implementation.
These are my test cases, both pass.
public class EFResourceGroupFacts
{
[Fact]
public void Saving_new_resource_will_cascade_properly()
{
// Recreate a fresh database and add some dummy data.
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
var existingCulture = cultureRepo.Get(1); // First and only culture.
var groupToAdd = new ResourceGroup("Added Group");
var resourceToAdd = new Resource(existingCulture,"New Resource", "Resource to add to existing group.",groupToAdd);
// Verify we got a single resource group.
Assert.Equal(1,ctx.ResourceGroups.Count());
// Saving the resource should also add the group.
resourceRepo.Save(resourceToAdd);
ctx.SaveChanges();
// Verify the group was added without explicitly saving it.
Assert.Equal(2, ctx.ResourceGroups.Count());
}
// try creating a new Unit of Work to really verify it has been persisted..
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
Assert.DoesNotThrow(() => ctx.ResourceGroups.First(rg => rg.Name == "Added Group"));
}
}
[Fact]
public void Changing_existing_resources_group_saves_properly()
{
SetupTestCase();
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Configuration.LazyLoadingEnabled = true;
var cultureRepo = new EFCultureRepository(ctx);
var resourceRepo = new EFResourceRepository(cultureRepo, ctx);
// This resource already has a group.
var existingResource = resourceRepo.Get(2);
Assert.NotNull(existingResource.ResourceGroup); // IMPORTANT: Property must be virtual!
// Verify there is only one resource group in the datastore.
Assert.Equal(1,ctx.ResourceGroups.Count());
existingResource.ResourceGroup = new ResourceGroup("I am implicitly added to the database. How cool is that?");
// Make sure there are 2 resources in the datastore before saving.
Assert.Equal(2, ctx.Resources.Count());
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Make sure there are STILL only 2 resources in the datastore AFTER saving.
Assert.Equal(2, ctx.Resources.Count());
// Make sure the new group was added.
Assert.Equal(2,ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(2,existingResource.ResourceGroup.Id);
// let's change the group to an existing group
existingResource.ResourceGroup = ctx.ResourceGroups.First();
resourceRepo.Save(existingResource);
ctx.SaveChanges();
// Assert no change in groups.
Assert.Equal(2, ctx.ResourceGroups.Count());
// Refetch from store, verify relationship.
existingResource = resourceRepo.Get(2);
Assert.Equal(1, existingResource.ResourceGroup.Id);
}
}
private void SetupTestCase()
{
// Delete everything first. Database.SetInitializer does not work very well for me.
using (var ctx = new LocalizationContext("Localization.CascadeTest"))
{
ctx.Database.Delete();
ctx.Database.Create();
var culture = new Culture("en-US", "English");
var resourceGroup = new ResourceGroup("Existing Group");
var resource = new Resource(culture, "Existing Resource 1",
"This resource will already exist when starting the test. Initially it has no group.");
var resourceWithGroup = new Resource(culture, "Exising Resource 2",
"Same for this resource, except it has a group.",resourceGroup);
ctx.Cultures.Add(culture);
ctx.ResourceGroups.Add(resourceGroup);
ctx.Resources.Add(resource);
ctx.Resources.Add(resourceWithGroup);
ctx.SaveChanges();
}
}
}
It was interesting to learn this, as I was not sure if it would work.
After working on this for a while I found an opensource project called GraphDiff here is it's blog entry 'introducing graphdiff for entity framework code first – allowing automated updates of a graph of detached entities'. I only began using it but it looks impressive. And it does solve the problem of issuing update/delete/insert for Many to One relationships. It actually generalizes the problem to graphs and allows arbitrary nesting.
Here is the generic method I concocted. It does use AddOrUpdate from the System.Data.Entity.Migrations namespace. Which may be reloading records from the db, I'll be checking on that later. The usage is
ctx.OrderLines.AddOrUpdateSet(l => l.orderId == neworder.Id,
l => l.Id, order.orderLines);
Here is the code:
public static class UpdateExtensions
{
public static void AddOrUpdateSet<TEntity>(this IDbSet<TEntity> set, Expression<Func<TEntity, bool>> predicate,
Func<TEntity, int> selector, IEnumerable<TEntity> newRecords) where TEntity : class
{
List<TEntity> oldRecords = set.Where(predicate).ToList();
IEnumerable<int> keys = newRecords.Select(selector);
foreach (TEntity newRec in newRecords)
set.AddOrUpdate(newRec);
oldRecords.FindAll(old => !keys.Contains(selector(old))).ForEach(detail => set.Remove(detail));
}
}

MVC 2 and EF4 Self-tracking entities models have bad state on post back

I've got standard Create() Edit() and Delete() methods on my controllers, and I am using the EF4 Self-tracking entities.
When the edit is posted back, the model.ChangeTracker.ChangeTracking = false, and model.ChangeTracker.State = ObjectState.Added, even though I made sure those are set when retrieving the record initially.
Are the self-tracking entities not persisting the ChangeTracker class when the form is submitted? If so, how do I fix that?
public virtual ActionResult Edit(int personId)
{
IContext context = ContextFactory.GetContext();
EntityRepo Repo = new EntityRepo(context);
Person d = Repo.Person.GetById(PersonId);
d.ChangeTracker.ChangeTrackingEnabled = true;
return View(d);
}
[HttpPost]
public virtual ActionResult Edit(int personId, Person item)
{
try
{
if (ModelState.IsValid)
{
IContext context = ContextFactory.GetContext();
EntityRepo Repo = new EntityRepo(context);
// the item is returning these properties that are wrong
//item.ChangeTracker.ChangeTrackingEnabled = false;
//item.ChangeTracker.State = ObjectState.Added;
Repo.Person.Update(item);
Repo.Person.SaveChanges();
return RedirectToAction("Index");
}
}
catch
{
}
return View();
}
Let's start at the beginning.
What are Self-Tracking Entities, exactly?
A Self-Tracking Entity is an entity which can do change tracking even when it is not connected to a ObjectContext. They are useful in times when you must change the entity, but cannot have it connected to an ObjectContext.
So when would I want one, really?
Mostly, when you must have distributed objects. For example, one use case is when you are making a web service which talks to a Silverlight client. However, other tools, like RIA Services may be a better fit here. Another possible use case is for a long-running task. Since an ObjectContext is intended to be a unit of work and should typically not be long-lived, having a disconnected entity might make sense here.
Do they make any sense for MVC?
Not really, no.
Let's look at this a little deeper, and examine what happens when you update an entity in MVC. The general process is like this:
The browser issues a GET request for an update page.
The MVC app fetches an entity, and uses it to build an update HTML page. The page is served to the browser, and most C# objects, including your entity, are disposed. At this point, you can restart the Web server, and the browser will never know the difference.
The browser issues a POST request to update the entity.
The MVC framework uses the data in the POST in order to materialize an instance of an edit model which is passed to the update action. This might happen to be the same type as the entity, but it is a new instance.
The MVC app can update the entity and pass those changes back to the database.
Now, you could make self-tracking entities work by also including the full state of the STE in the HTML form and POSTing that back to the MVC app along with the scalar values on the entity. Then the Self-Tracking Entity might at least work.
But what benefit does this give you? The browser obviously cannot deal with your entity as a C# object. So it cannot make any changes to the entity worth tracking in terms that a Self-Tracking Entity would understand.
U should keep original STE in some hidden field. It's like your custom ViewState. In submit method u must merge original STE and new values.
Use ActionFilterAttribute for it.
Like
public class SerializeOriginalModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult == null)
return;
var viewModel = viewResult.ViewData.Model as ViewModel;
if (viewModel == null || viewModel.SteObject == null)
return;
byte[] bytes;
using (var stream = new MemoryStream())
{
var serializer = new DataContractSerializer(viewModel.SteObject.GetType());
serializer.WriteObject(stream, viewModel.SteObject);
bytes = stream.ToArray();
}
var compressed = GZipHelper.Compress(bytes);
viewModel.SerializedSteObject = Convert.ToBase64String(compressed);
}
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters == null || filterContext.ActionParameters.Count == 0)
return;
var viewModel = filterContext.ActionParameters.First().Value as ViewModel;
var serialized = filterContext.HttpContext.Request.Form["SerializedSteObject"];
if (viewModel == null || String.IsNullOrEmpty(serialized))
return;
var type = filterContext.ActionParameters.First().Value.GetType().BaseType.GetGenericArguments()[0];
var bytes = GZipHelper.Decompress(Convert.FromBase64String(serialized));
using (var stream = new MemoryStream(bytes))
{
var serializer = new DataContractSerializer(type);
viewModel.SteObject = serializer.ReadObject(stream);
}
}
}
STE has one very big drawback. You have to store them in session or view state (WebForms). So it is nothing more than "new version of dataset". If you don't store STE you will have one instance for getting data and different for posting = no change tracking.
I think you are missing the idea of Repository. You should not have an Update method in the Repository. After submitting, you should get the item again, apply the modifications and then Save.
I prefer having a service layer between client and Repository. We can always change the strategy with which we merge.
And yes, if you need to persist your STE's between requests, use session or viewstate.
It should be
Repo.Person.ApplyChanges(item);
Repo.Person.SaveChanges();
instead of
Repo.Person.Update(item);
Repo.Person.SaveChanges();
Self Tracking works with ApplyChanges extention method.

RIA Services EntitySet does not support 'Edit' operation

Making my first steps in RIA Services (VS2010Beta2) and i encountered this problem:
created an EF Model (no POCOs), generic repository on top of it and a RIA Service(hosted in an ASP.NET MVC application) and tried to get data from within the ASP.NET MVC application: worked well.
Next step: Silverlight client. Got a reference to the RIAService (through its context), queried for all the records of the repository and got them into the SL application as well (using this code sample):
private ObservableCollection<Culture> _cultures = new ObservableCollection<Culture>();
public ObservableCollection<Culture> cultures
{
get { return _cultures; }
set
{
_cultures = value;
RaisePropertyChanged("cultures");
}
}
....
//Get cultures
EntityQuery<Culture> queryCultures = from cu in dsCtxt.GetAllCulturesQuery()
select cu;
loCultures = dsCtxt.Load(queryCultures);
loCultures.Completed += new EventHandler(lo_Completed);
....
void loAnyCulture_Completed(object sender, EventArgs e)
{
ObservableCollection<Culture> temp=
new ObservableCollection<Culture>loAnyCulture.Entities);
AnyCulture = temp[0];
}
The problem is this: whenever i try to edit some data of a record (in this example the first record) i get this error:
This EntitySet of type 'Culture' does not support the 'Edit' operation.
I thought that i did something weird and tried to create an object of type Culture and assign a value to it: it worked well!
What am i missing? Do i have to declare an EntitySet? Do i have to mark it? Do i have to...what?
Thanks in advance
It turns out that in the DomainService class one has to implement (or at least to mark "placeholder methods") as "Edit", "Delete",... eg
[Delete]
public void DeleteCulture(Culture currentCulture)
{
throw new NotImplementedException("UpdateCulture not Implemented yet");
}
[Insert]
public void InsertCulture(Culture newCulture)
{
throw new NotImplementedException("InsertCulture not Implemented yet");
}
This way the OrganizationDomainContextEntityContainer class creates an EntitySet with parameter EntitySetOperations.All (meaning that all the CUD operations are available).
Hope it's useful for someone in the future!