Entity Framework persist a list of Objects - entity-framework

I am using Service Stack as my system's API and I'm using Entity Framework to get data from my SQL Server DataBase. Although, I cannot retrieve any data from a list of objects generated by entity framework.
[Route("/getInterventions","GET")]
public class GetInterventions
{
}
public class GetInterventionsResponse
{
public List<Intervention> interventions { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
public class GetInterventionsService : Service
{
public object Any(GetInterventions request)
{
using (var dbConnection = new operationsContext())
{
List<Intervention> dbItems = dbConnection.Interventions.ToList();
return new GetInterventionsResponse{
interventions = dbItems
};
}
}
}
From the client side I get:
The ObjectContext instance has been disposed and can no longer be used for "operations"(name of db) that require a connection.
So with this error I can verify that the problem as to do with the list that acts like a "virtual" list, and its objects are not returned to the client side, but are passed as a reference or something like that.
So how can I deep copy this list and retrieve a clone of it?
Thanks anyways

It looks like the list is no longer accessible when the context gets disposed, possibly because the variable was defined within the scope of the context. Try defining dbItems outside of the using statement:
public object Any(GetInterventions request)
{
List<Intervention> dbItems;
using (var dbConnection = new operationsContext())
{
dbItems = dbConnection.Interventions.ToList();
}
return new GetInterventionsResponse{
interventions = dbItems
};
}
Also, you may run into this issue if you are expecting navigation properties of Interventions to be loaded, which they will not be with your code because EF uses lazy loading. For example, if Intervention has a Person navigation property, you would need to include that to have it be available. Like this:
dbItems = dbConnection.Interventions.Include(x => x.Persons).ToList();
Edit based on comment below:
You can also have includes multiple levels deep like this:
dbItems = dbConnection.Interventions.Include(x => x.Persons.Car).ToList();
or for a nested list...
dbItems = dbConnection.Interventions.Include(x => x.Persons.Select(y => y.Cars).ToList();
or for multiple navigation properties...
dbItems = dbConnection.Interventions.Include(x => x.Persons)
.Include(x => x.Cars).ToList();

Related

EFCore - Why do i have to null child objects to stop them inserting? One-to-many

Small amount of context, I have been using NHibernate mapping by code for a few years, the last few months I have started using Entity Framework Core.
I'm trying to understand why I have to null child objects to stop them inserting new records. I'm not sure if its an understanding issue on my part or if this is how Entity Framework works.
I have two classes, Command and CommandCategory. Command has a single CommandCategory and CommandCategory can have many commands. For example, The command "set timeout" would go under the "Configuration" category. Similarly, the "set URL" command would also go under the "Configuration" category.
class Command
{
public Guid Id { get; set; }
public string Name { get; set; }
public string CommandString { get; set; }
public Guid CommandCategoryId { get; set; }
public CommandCategory CommandCategory { get; set; }
}
class CommandCategory
{
public CommandCategory(string id, string name)
{
Id = Guid.Parse(id);
Name = name;
Commands = new List<Command>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Command> Commands { get; set; }
}
My DbContext is setup like so:
class EfContext : DbContext
{
private const string DefaultConnection = "XXXXX";
public virtual DbSet<Command> Command { get; set; }
public virtual DbSet<CommandCategory> CommandCategory { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(DefaultConnection);
optionsBuilder.EnableSensitiveDataLogging();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Command>()
.HasOne(x => x.CommandCategory)
.WithMany(x => x.Commands);
}
}
Then here is the code that actually runs it all. First I call Add(). Add creates a new Command and adds it to the database. It also creates a CommandCategory called "Configuration" and inserts both correctly.
Next I call AddWithExisting(). This will create a new Command but using the existing CommandCategory. When it tries to add to the database, it first inserts the Command and then it tries to insert the CommandCategory. Because the CommandCategory.Id already exists, and its setup as the primary key, this then fails as it's a duplicate key. To get around this I have to make sure the CommandCategory property on the Command object is set to null. This will then only insert the Command to the database and not the CommandCategory object.
I know usually you wouldn't create a new CommandCategory object, but in this instance I am simulating the object coming up from the client via an ApiController. My application sends data back and forth via WebApi so the object is basically being created new when a request is made.
Nulling the property seems like a strange thing to do, I thought the point of Object-relational mapping was to not have to deal with individual properties like this.
Is this how its supposed to function or am I doing something wrong?
class Program
{
static void Main(string[] args)
{
var dbContext = new EfContext();
Add(dbContext);
AddWithExisting(dbContext);
Console.WriteLine("Hello World!");
}
private static void Add(EfContext dbContext)
{
var newCommand = new Command();
newCommand.Id = Guid.NewGuid();
newCommand.Name = "set timeout";
newCommand.CommandString = "timeout:500;";
var newCommandCategory = new CommandCategory("8C0D0E31-950E-4062-B783-6817404417D4", "Configuration");
newCommandCategory.Commands.Add(newCommand);
newCommand.CommandCategory = newCommandCategory;
dbContext.Command.Add(newCommand);
dbContext.SaveChanges();
}
private static void AddWithExisting(EfContext dbContext)
{
var newCommand = new Command();
newCommand.Id = Guid.NewGuid();
newCommand.Name = "set URL";
newCommand.CommandString = "url:www.stackoverflow.com";
// this uses the same Id and Name as the existing command, this is to simulate a rest call coming up with all the data.
var newCommandCategory = new CommandCategory("8C0D0E31-950E-4062-B783-6817404417D4", "Configuration");
newCommandCategory.Commands.Add(newCommand);
// If i don't null the below line, it will insert to the database a second time
newCommand.CommandCategory = newCommandCategory;
newCommand.CommandCategoryId = newCommandCategory.Id;
dbContext.Command.Add(newCommand);
dbContext.SaveChanges();
}
This is by design, you can do two things here:
You can look up the existing command category from the DB and set that as the property (as this object is 'attached' to the DB context, it won't create a new one).
Just set the ID of the command category on the command.
e.g.
newCommand.CommandCategory = dbContext.CommandCategories.Find("8C0D0E31-950E-4062-B783-6817404417D4");
or
newCommand.CommandCategoryId = new Guid("8C0D0E31-950E-4062-B783-6817404417D4");
At the minute, it is seeing a new command category (not attached) so is trying to create it.
EF doesn't perform InsertOrUpdate checks. Entities are tracked by a DbContext as either Added or Updated. If you interact with a tracked entity or "Add" an entity to the DbContext, all untracked related entities will be recognized as Added, resulting in an insert.
The simplest advice I can give is to give EF the benefit of the doubt when it comes to entities and don't try to premature optimize. It can save headaches.
using (var dbContext = new EfContext())
{
var newCommand = Add(dbContext);
AddWithExisting(newCommand, dbContext);
dbContext.SaveChanges();
Console.WriteLine("Hello World!");
}
private static command Add(EfContext dbContext)
{
var newCommand = new Command
{
Id = Guid.NewGuid(), // Should either let DB set this by default, or use a Sequential ID implementation.
Name = "set timeout",
CommandString = "timeout:500;"
};
Guid commandCategoryId = new Guid("8C0D0E31-950E-4062-B783-6817404417D4");
var commandCategory = dbContext.CommandCategory.Where(x => x.CommandCategoryId == commandCategoryId);
if(commandCategory == null)
commandCategory = new CommandCategory
{
Id = commandCategoryId,
Name = "Configuration"
};
newCommand.CommandCategory = commandCategory;
dbContext.Command.Add(command);
return command;
}
private static Command AddWithExisting(Command command, EfContext dbContext)
{
var newCommand = new Command
{
Id = Guid.NewGuid(),
Name = "set URL",
CommandString = "url:www.stackoverflow.com",
CommandCategory = command.CommandCategory
};
dbContext.Commands.Add(newCommand);
return newCommand;
}
So what's changed here?
First the DbContext reference is Disposable, so it should always be wrapped with a using block. Next, we create the initial Command, and as a safety measure to avoid an assumption we search the context for an existing CommandCategory by ID and associate that, otherwise we create the command category and associate it to the Command. 1-to-many relationships do not need to be bi-directional, and even if you do want bi-directional relationships you don't typically need to set both references to each other if the mappings are set up correctly. If it makes sense to ever load a CommandCategory and navigate to all commands using that category then keep it, but even to query all commands for a specific category, that is easy enough to query from the command level. Bi-directional references can cause annoying issues so I don't recommend using them unless they will be really necessary.
We return the new command object back from the first call, and pass it into the second. We really only needed to pass the reference to the commandcategory loaded/created in the first call, but in case it may make sense to check/copy info from the first command, I used this example. We create the new additional command instance and set it's command category reference to the same instance as the first one. I then return the new command as well. We don't use that reference to the second command. The important difference between this and what you had tried is that the CommandCategory here points to the same reference, not two references with the same ID. EF will track this instance as it is associated/added, and wire up the appropriate SQL.
Lastly note that the SaveChanges call is moved outside of the two calls. Contexts generally should only ever save changes once in their lifetime. Everything will get committed together. Having multiple SaveChanges is usually a smell when developers want to manually wire up associations when keys are autogenerated by a DB. (Identity or defaults) Provided relationships are mapped correctly with navigation properties and their FKs, EF is quite capable of managing these automatically. This means that if you set up your DB to default your Command IDs to newsequentialid() for instance and tell EF to treat the PK as an Identity column, EF will handle this all automatically. This goes for associating those new PKs as FKs to related entities as well. No need to save the parent record so the parent ID can be set in the child entities, map it, associate them, and let EF take care of it.

How to query in LINQ & Entity Framework the unmapped property

So I partially followed from an SO answer on how to store a property with array datatype in Entity Framework. What I didn't follow on that answer is setting the string InternalData to be private instead of public as I find it a code smell if it is set to public (not enough reputation to comment there yet).
I also managed to map the private property in entity framework from this blog.
When I perform CR (create, read) from that entity, all goes well. However, when my LINQ query has a where clause using that property with array datatype, it says that "System.NotSupportedException: 'The specified type member is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'".
How to work around on this? Here are the relevant code blocks:
public class ReminderSettings
{
[Key]
public string UserID { get; set; }
[Column("RemindForPaymentStatus")]
private string _remindForPaymentStatusCSV { get; set; }
private Status[] _remindForPaymentStatus;
[NotMapped]
public Status[] RemindForPaymentStatus
{
get
{
return Array.ConvertAll(_remindForPaymentStatusCSV.Split(','), e => (Status)Enum.Parse(typeof(Status), e));
}
set
{
_remindForPaymentStatus = value;
_remindForPaymentStatusCSV = String.Join(",", _remindForPaymentStatus.Select(x => x.ToString()).ToArray());
}
}
public static readonly Expression<Func<ReminderSettings, string>> RemindForPaymentStatusExpression = p => p._remindForPaymentStatusCSV;
}
public enum Status
{
NotPaid = 0,
PartiallyPaid = 1,
FullyPaid = 2,
Overpaid = 3
}
protected override void OnModelCreating(DbModelBuuilder modelBuilder)
{
modelBuilder.Entity<ReminderSettings>().Property(ReminderSettings.RemindForPaymentStatusExpression);
}
//This query will cause the error
public IEnumerable<ReminderSettings> GetReminderSettingsByPaymentStatus(Status[] statusArray)
{
var query = ApplicationDbContext.ReminderSettings.Where(x => x.RemindForPaymentStatus.Intersect(statusArray).Any());
return query.ToList(); //System.NotSupportedException: 'The specified type member 'RemindForPaymentStatus' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
}
Entity Framework can not translate LINQ expressions to SQL if they access a property that is annotated as [NotMapped]. (It also can not translate if the property contains custom C# code in its getter/setter).
As a quick (but potentially low performance) workaround, you can execute the part of the query that does not cause problems, then apply additional filtering in-memory.
// execute query on DB server and fetch items into memory
var reminders = dbContext.ReminderSettings.ToList();
// now that we work in-memory, LINQ does not need to translate our custom code to SQL anymore
var filtered = reminders.Where(r => r.RemindForPaymentStatus.Contains(Status.NotPaid));
If this causes performance problems, you have to make the backing field of your NotMapped property public and work directly with it.
var filtered = dbContext.ReminderSettings
.Where(r => r._remindForPaymentStatusCSV.Contains(Status.NotPaid.ToString("D"));
Edit
To handle multiple Status as query parameters, you can attach Where clauses in a loop (which behaves like an AND). This works as long as your Status enum values are distinguishable (i.e. there is no Status "11" if there is also a Status "1").
var query = dbContext.ReminderSettings.Select(r => r);
foreach(var statusParam in queryParams.Status) {
var statusString = statusParam.ToString("D");
query = query.Where(r => r._remindForPaymentStatusCSV.Contains(statusString));
}
var result = query.ToArray();

Linq Entry( entity.Related ).Query.Load() not writing back to Collection property

Supposing I have three EF entity classes:
public class Person {
...
public ICollection Vehicles { get; set; }
}
public class Vehicle {
...
public Person Owner { get; set; }
public CarModel ModelInfo { get; set; }
}
public class CarModel {
...
// properties for make, model, color, etc
}
The Person.Vehicles property is lazy-loaded.
Supposing I have a Person instance already loaded and I want to load its Vehicle collection property such that it also includes the related ModelInfo property.
So I have this:
void LoadVehiclesAndRelated(MyDbContext dbContext, Person person)
{
dbContext.Entry( person )
.Collection( p => p.Vehicles )
.Query()
.Include( v => v.ModelInfo )
.Load();
}
Used like so:
using( MyDbContext dbContext = ... ) {
Person p = GetPerson( 123 );
LoadVehiclesAndRelated( dbContext, p );
}
foreach(Vehicle v in p.Vehicles) {
Console.WriteLine( v.ModelInfo );
}
However when I do this, I get an exception at runtime when it first evaluates the p.Vehicles expression because the property is actually empty (so it wants to load it) but the DbContext is now disposed.
When the .Load() call was made (inside LoadVehiclesAndRelated() I saw the SQL being executed against the server (in SQL Server Profiler) but the collection property remains empty.
How can I then load the property and with the Included sub-properties?
Annoyingly, this scenario is not mentioned in the MSDN guide for explicit-loading: https://msdn.microsoft.com/en-us/data/jj574232.aspx
Looks like calling .Query().Load() is not the same as calling DbCollectionEntry.Load directly, and the important difference is that the former does not set the IsLoaded property, which then is causing triggering lazy load later. Most likely because as explained in the link, the former is intended to be used for filtered (partial) collection load scenarios.
Shortly, to fix the issue, just set IsLoaded to true after loading the collection:
var entry = dbContext.Entry(person);
var vehicleCollection = entry.Collection(p => p.Vehicles);
vehicleCollection.Query()
.Include( v => v.ModelInfo )
.Load();
vehicleCollection.IsLoaded = true;
P.S. For the sake of correctness, this behavior is sort of mentioned at the end of the Applying filters when explicitly loading related entities section:
When using the Query method it is usually best to turn off lazy loading for the navigation property. This is because otherwise the entire collection may get loaded automatically by the lazy loading mechanism either before or after the filtered query has been executed.

Generic repository to update an entire aggregate

I am using the repository pattern to provide access to and saving of my aggregates.
The problem is the updating of aggregates which consist of a relationship of entities.
For example, take the Order and OrderItem relationship. The aggregate root is Order which manages its own OrderItem collection. An OrderRepository would thus be responsible for updating the whole aggregate (there would be no OrderItemRepository).
Data persistence is handled using Entity Framework 6.
Update repository method (DbContext.SaveChanges() occurs elsewhere):
public void Update(TDataEntity item)
{
var entry = context.Entry<TDataEntity>(item);
if (entry.State == EntityState.Detached)
{
var set = context.Set<TDataEntity>();
TDataEntity attachedEntity = set.Local.SingleOrDefault(e => e.Id.Equals(item.Id));
if (attachedEntity != null)
{
// If the identity is already attached, rather set the state values
var attachedEntry = context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(item);
}
else
{
entry.State = EntityState.Modified;
}
}
}
In my above example, only the Order entity will be updated, not its associated OrderItem collection.
Would I have to attach all the OrderItem entities? How could I do this generically?
Julie Lerman gives a nice way to deal with how to update an entire aggregate in her book Programming Entity Framework: DbContext.
As she writes:
When a disconnected entity graph arrives on the server side, the
server will not know the state of the entities. You need to provide a
way for the state to be discovered so that the context can be made
aware of each entity’s state.
This technique is called painting the state.
There are mainly two ways to do that:
Iterate through the graph using your knowledge of the model and set the state for each entity
Build a generic approach to track state
The second option is really nice and consists in creating an interface that every entity in your model will implement. Julie uses an IObjectWithState interface that tells the current state of the entity:
public interface IObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
First thing you have to do is to automatically set the state to Unchanged for every entity retrieved from the DB, by adding a constructor in your Context class that hooks up an event:
public YourContext()
{
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IObjectWithState;
if (entity != null)
{
entity.State = State.Unchanged;
}
};
}
Then change your Order and OrderItem classes to implement the IObjectWithState interface and call this ApplyChanges method accepting the root entity as parameter:
private static void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new YourContext())
{
context.Set<TEntity>().Add(root);
CheckForEntitiesWithoutStateInterface(context);
foreach (var entry in context.ChangeTracker
.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = ConvertState(stateInfo.State);
}
context.SaveChanges();
}
}
private static void CheckForEntitiesWithoutStateInterface(YourContext context)
{
var entitiesWithoutState =
from e in context.ChangeTracker.Entries()
where !(e.Entity is IObjectWithState)
select e;
if (entitiesWithoutState.Any())
{
throw new NotSupportedException("All entities must implement IObjectWithState");
}
}
Last but not least, do not forget to set the right state of your graph entities before calling ApplyChanges ;-) (You could even mix Modified and Deleted states within the same graph.)
Julie proposes to go even further in her book:
you may find yourself wanting to be more granular with the way
modified properties are tracked. Rather than marking the entire entity
as modified, you might want only the properties that have actually
changed to be marked as modified.
In addition to marking an entity as modified, the client is also
responsible for recording which properties have been modified. One way
to do this would be to add a list of modified property names to the
state tracking interface.
But as my answer is already too long, go read her book if you want to know more ;-)
My opinionated (DDD specific) answer would be:
Cut off the EF entities at the data layer.
Ensure your data layer only returns domain entities (not EF entities).
Forget about the lazy-loading and IQueryable() goodness (read: nightmare) of EF.
Consider using a document database.
Don't use generic repositories.
The only way I've found to do what you ask in EF is to first delete or deactivate all order items in the database that are a child of the order, then add or reactivate all order items in the database that are now part of your newly updated order.
So you have done well on update method for your aggregate root, look at this domain model:
public class ProductCategory : EntityBase<Guid>
{
public virtual string Name { get; set; }
}
public class Product : EntityBase<Guid>, IAggregateRoot
{
private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>();
public void AddProductCategory(ProductCategory productCategory)
{
_productCategories.Add(productCategory);
}
}
it was just a product which has a product category, I've just created the ProductRepository as my aggregateroot is product(not product category) but I want to add the product category when I create or update the product in service layer:
public CreateProductResponse CreateProduct(CreateProductRequest request)
{
var response = new CreateProductResponse();
try
{
var productModel = request.ProductViewModel.ConvertToProductModel();
Product product=new Product();
product.AddProductCategory(productModel.ProductCategory);
_productRepository.Add(productModel);
_unitOfWork.Commit();
}
catch (Exception exception)
{
response.Success = false;
}
return response;
}
I just wanted to show you how to create domain methods for entities in domain and use it in service or application layer. as you can see the code below adds the ProductCategory category via productRepository in database:
product.AddProductCategory(productModel.ProductCategory);
now for updating the same entity you can ask for ProductRepository and fetch the entity and make changes on it.
note that for retrieving entity and value object of and aggregate separately you can write query service or readOnlyRepository:
public class BlogTagReadOnlyRepository : ReadOnlyRepository<BlogTag, string>, IBlogTagReadOnlyRepository
{
public IEnumerable<BlogTag> GetAllBlogTagsQuery(string tagName)
{
throw new NotImplementedException();
}
}
hope it helps

Enumeration Succeeds after DbContext Disposed

Background
I am creating a projection from a parent/child relationship that includes a Name property of the parent and a list of the children's Ids.
Code
private class ParentChildInfo
{
public string Name { get; set; }
public List<int> ChildIds { get; set; }
}
var infos = ctx.Masters.Include(m => m.Children).Select(
m => new ParentChildInfo()
{
Name = m.Name,
ChildIds = m.Children.Where(c => c.SomeProp.StartsWith("SpecialValue"))
.Select(c => c.Id).ToList()
}).ToList();
Unfortunately that produced the error
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[System.Int32] ToList[Int32]
That lead me to this post, which suggested (in the comments) making the following changes:
private class ParentChildInfo
{
public string Name { get; set; }
public IEnumerable<int> ChildIds { get; set; } // No longer List<int>
}
var infos = ctx.Masters.Include(m => m.Children).Select(
m => new ParentChildInfo()
{
Name = m.Name,
ChildIds = m.Children.Where(c => c.SomeProp.StartsWith("SpecialValue"))
.Select(c => c.Id) // Dropped the .ToList()
}).ToList();
I originally wanted to get lists rather than enumerables because the code that uses the result runs for several minutes, and I did not want to tie up the DbContext that long.
I use the code like this:
using (MyContext ctx = new MyContext())
{
// code from above that populates infoes
}
foreach (var info in infoes)
{
// use info.ChildIds
}
I planned to move the foreach into the using so that I can enumerate the ChildIds, but hit F5 instead and was surprised to see that the code works.
Question
Given that the DbContext is disposed at that point and ChildIds is an IEnumerable<int> rather than List<int>, why exactly can I enumerate ChildIds?
It is because the ToList() of the infos query actually executes the query. So the collection ctx.Masters is enumerated and the projections are populated. Even without the Include it would notice that Master.Children is addressed and emit the SQL join. The implementing type of IEnumerable<int> ChildIds is probably List<int>.
You did .ToList() on the query so the query was executed and all the results are materialized and the connection to the database should be closed. I assume it would not work if you did not have .ToList() since (at least in EF5) the results are being processed in streaming fashion and entities are materialized when requested (i.e. on each iteration in the loop).