using InMemoryDatabase in multiple context - ef-core-3.1

I am using EF Core 3.1 and using in memory db for my xUnit tests. I am trying to use same in memory database into multiple context as below.
DbContextOptionsBuilder _builder = new DbContextOptionsBuilder();
_builder.UseInMemoryDatabase("inMemoryDB");
var guid = Guid.NewGuid();
var parent = Parent(guid);
var child1 = new Child(1, guid);
var child2 = new Child(2, guid);
using (var context = new AdministrationContext(_builder.Options))
{
parent.Child.Add(child1);
parent.Child.Add(child2);
context.Parents.Add(parent);
context.SaveChanges();
}
using (var context = new AdministrationContext(_builder.Options))
{
// I can see saved parent object here as I am using AdministrationContext
var list = context.Parents.ToList();
}
using (var context = new ReferenceContext(_builder.Options))
{
// getting empty list here as I am using ReferenceContext
var list = context.Parents.ToList();
}
I am having same parent and child object in reference context also.
From reference context, I am getting empty parent data.
This is working fine when I use sql server.
Does UseInMemoryDatabase should be used for one context type? Or am I missing anything here.

Related

Add an element to another existing entity using Entity Framework

Let's take this scenario:
I want to add a task to project.
The project exist already.
Task will be added as new entity and attached to the project.
A project can have one or many tasks.
This is the way I do it in now my repository :
public void AddTaskToProject(Project ProjectToUpdate,Task TaskToAdd)
{
using (var context = new myContext())
{
var project= context.Projects.First(x => x.Id == projectToUpdate.Id);
project.Tasks.Add(taskToAdd);
context.Tasks.Add(taskToAdd);
SaveContextChange(context);
}
}
Is there any other way to avoid as much call to database as I did?
The ideal for me, is to update the project, and then call only an update for the project entity ? is there a way to do that ?
This is the generic update I have:
public virtual void Update(params T[] items)
{
using (var context = new MyContext())
{
foreach (T item in items)
{
context.Entry(item).State =EntityState.Modified;
}
context.SaveChanges();
}
}
You can try:
public void AddTaskToProject(Project projectToUpdate,Task taskToAdd)
{
using (var context = new myContext())
{
var project = new Project() { ProjectId = projectToUpdate.Id };
context.Projects.Attach(project);
project.Tasks.Add(taskToAdd);
context.SaveChanges();
}
}

Reuse connection among DbContext instances in unit test

I'm trying to setup some unit tests using EntityFramework 5, SQL Server Compact 4 and Xunit.
I'm using different context instances because I'm testing a ASP MVC app and I need to test the behavior of some update operations over detached entities.
[Fact, AutoRollback]
public void TestConnection()
{
using (var connection = this.GetDbConnection())
{
using (var context = new MyContext(connection, false))
{
// Do database stuff
}
using (var context = new MyContext(connection, false))
{
// Do database stuff
}
}
}
public DbConnection GetDbConnection()
{
string dataSource = "|DataDirectory|\\MyDb.sdf";
var sqlBuilder = new SqlCeConnectionStringBuilder();
sqlBuilder.DataSource = dataSource;
return new SqlCeConnection(sqlBuilder.ToString());
}
This gives me the following error:
System.Data.EntityException : The underlying provider failed on Open.
System.InvalidOperationException : The connection object can not be enlisted in transaction scope.
I know I can't open multiple DbContext instances inside a TransactionScope (that is probably what Xunit does when you put a FallbackAttribute in your method), so that's why I'm creating the connection beforehand.
If I try to open the connection myself, it still does not work:
using (var connection = this.GetDbConnection())
{
connection.Open();
using (var context = new MyContext(connection, false))
{
I get the following exception:
System.ArgumentException : EntityConnection can only be constructed with a closed DbConnection.
Does any one know how to solve that issue?
EDIT
The test classes that deal with the Db extend a "DomainFactsBase" where the database is initialized as the following:
public DomainFactsBase()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
using (var context = new MyContext(GetDbConnection(), true))
context.Database.Initialize(false);
}
EDIT
I can sucessfully run tests with autorollback when I create only one context instance. This was accomplished following the instructions in this article. I have a extension method:
public static void OpenConnection(this DbContext context)
{
((IObjectContextAdapter)context).ObjectContext.Connection.Open();
}
And I call it right after creating the context in the tests:
[Fact, AutoRollback]
public void SomeFact()
{
using (var context = new MyContext())
{
context.OpenConnection();
// Do stuff
}
}
That work with no problems. They arise when I try to open the context more than once in the same fact (with AutoRollback enabled), as I examplified in the beginning.
Initialize the database outside of the test. You can do this inside of the test class's constructor.
public MyTestClass()
{
using (var db = new MyContext(GetDbConnection(), true))
{
db.Database.Initialize(false);
}
}

How to remove the data fetch in this Entity Framework Update?

Can I restructure this query so I don't have to fetch from the database? I have tried various techniques, but none of them work.
public void Update(CartEntryViewModel entry)
using (var context = new MyContext())
{
User user = Auth.GetUser(context);
CartEntry model = context.CartEntries.Find(entry.Id);
// Change the item and update quantity
model.Item = context.Items.Find(entry.Item.Id);
model.Quantity = entry.Quantity;
context.Entries(model).EntityState = EntityState.Modified;
context.SaveChanges();
}
}
The Attach() method takes the model you already have and attaches it to the context as if it were just read from the DB. You just have to change the state so the context knows to update the corresponding row in the DB when SaveChanges() is called.
public void Update(CartEntryViewModel entry)
{
CartEntry model = new CartEntry
{
model.Id = entry.Id,
model.Item = entry.Item,
model.Quantity = entry.Quantity,
// Set other properties.
};
using (MyContext context = new MyContext())
{
context.CartEntries.Attach(model);
context.Entry(model).State = EntityState.Modified;
context.SaveChanges();
}
}

How can I update my DTO's ID when inserting multiple new entities

I'm using EF4. I'm adding a series of new entities from a list of DTOs, and I'm not saving changes until after all of them are added. I'm wanting to set the IDs of the DTOs to what the new entities' IDs are. How on earth do I do this? Does EF provide a mechanism for this?
With a single entity I would do this:
public void InsertMyDto(MyDto a_dto)
{
var newEntity = new MyEntity
{
Name = a_dto.Name,
Type = a_dto.Type.ToString(),
Price = a_dto.Price
};
_dataContext.MyEntities.AddObject(newEntity);
_dataContext.SaveChanges();
a_dto.ID = newEntity.ID;
}
This works fine, but what do I do in this case?
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
// ???
}
I want to save all at once, because I have validation work (not shown above) that is done against the database and fails before it gets to SaveChanges, and if it fails I want it to fail as a whole transaction (i.e. rollback).
I don't think that EF can help you here. It even can't help you for a single instance which forces you to write a_dto.ID = newEntity.ID. The counterpart of this code for multiple entites is to keep track of the pairs of dtos and new entities:
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
Dictionary<MyDto, MyEntity> dict = new Dictionary<MyDto, MyEntity>();
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
dict.Add(myDto, newEntity);
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
foreach (var item in dict)
item.Key.ID = item.Value.ID; // Key is MyDto, Value is MyEntity
}

Exception when saving an object with related object EF 3.5

I get an error telling me that: "The EntityKey property can only be set when the current value of the property is null." when I try to save an object with related object.
Here's my code:
public partial class Cat{
public bool Save()
{
try
{
using (var context = new PhonebookEntities())
{
if (this.ParentCat != null)
{
if (this.ParentCat.CategoryID == 0)
context.AddToCats(this.ParentCat);
}
context.AddToCats(this);
context.SaveChanges();
}
return true;
}
catch (System.Exception)
{
return false;
}
}
And here I create a Cat object and connect it to a relatet parent Cat object and then call the save method:
var cat = new Cat()
{
CatName = "Test",
ParentCat = Cat.GetById(1)
};
cat.Save();
Let me guess - the Cat.GetById(1) looks like:
public static Cat GetById(int id)
{
using (var context = new PhonebookEntities())
{
return context.Cats.Single(c => c.Id == id);
}
}
You are using two different contexts - that is the source of the issue. The first context loads the Cat and fills it EntityKey but the second context doesn't know this instance so once you call AddToCats it will add both new Cat and ParentCat as well but it fails because new entity cannot have filled EntityKey (I know that it is not a new entity but for the new instance of the context it is!).
Add based operations always add all unknown entities in the object graph. The entity is known by the context only if the same context loaded the entity or if you called .Attach for that entity.
Because of that, this is also incorrect:
if (this.ParentCat != null)
{
if (this.ParentCat.CategoryID == 0)
context.AddToCats(this.ParentCat);
}
Unknown ParentCat will be added automatically together with the current Cat. If you call this it will also add the current Cat but the next call will try to add it again => you will probably get an exception.
This whole can be solved by two ways:
Load the ParentCat on the same context instance as you save Cat
Don't load the ParentCat and either use dummy class or try to set EntityKey
Dummy class approach:
var parentCat = new Cat() { Id = 1 };
context.Cats.Attach(parentCat); // Use correct entity set name
var cat = new Cat()
{
CatName = "Test",
ParentCat = parentCat
};
cat.Save();
EntityKey aproach (this is more like a guess):
var cat = new Cat()
{
CatName = "Test",
// I hope ParentCatReference will be initialized
ParentCatReference.EntityKey = new EntityKey("Cats", "Id", 1) // Use correct entity set name
};
cat.Save();