Is there a way to automatically enforce parent entity to be timestamped as having been modified, if any of its dependent child items are added/deleted/modified? The key word is automatically. I know this can be done by manipulating the DbEntry's EntityState or by manually setting the timestamp field in the parent, but I need this done on a number of parent-child entities in a system, so the desire is to have EF (or a related component) automatically do this somehow.
More Background and Examples
Let's say we have an Order and Order Items (1-many). When order items are added/removed from an order, the parent order itself needs to be updated to store the last modified timestamp.
public interface IModifiableEntity
{
DateTime LastModifiedOn { get; set; }
}
public class Order : IModifiableEntity
{
// some Order fields here...
// timestamp for tracking when the order was changed
public DateTime LastModifiedOn { get; set; }
// list of order items in a child collection
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderId { get; set; }
// other order item fields...
}
Somewhere in application logic:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
_myDb.SaveChanges();
}
I already have a pattern in place to detect modified entities and set timestamps automatically via EF's SaveChanges, like this:
public override int SaveChanges()
{
var timestamp = DateTime.Now;
foreach (var modifiableEntity in ChangeTracker.Entries<IModifiableEntity>())
{
if (modifiableEntity.State == EntityState.Modified)
{
modifiableEntity.Entity.UpdatedOn = timestamp;
}
}
return base.SaveChanges();
}
That works great if any direct fields on an IModifiableEntity are updated. That entity's state will then be marked as Modified by EF, and my custom SaveChanges() above will catch it and set the timestamp field correctly.
The problem is, if you only interact with a child collection property, the parent entity is not marked as modified by EF. I know I can manually force that via context.Entry(myEntity).State or just by manually setting the LastModifiedOn field when adding child items in application logic, but that wouldn't be done centrally, and is easy to forget.
I DO NOT want to do this:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
// this works but is very manual and EF infrastructure specific
_myDb.Entry(order).State = EntityState.Modified;
// this also works but is very manual and easy to forget
order.LastModifiedOn = DateTime.Now;
_myDb.SaveChanges();
}
Any way I can do this centrally and inform EF that a "root" entity of a parent-child relationship needs to be marked as having been updated?
My app allows users to assign categories to their items. The classes look like this.
class Item
{
public string Id { get; set; }
/* ... */
public virtual IEnumerable<Category> Categories { get; set; }
}
class Category
{
public string Id { get; set; }
public virtual Category Parent { get; set; }
public virtual IEnumerable<Category> Subcategories { get; set; }
}
As you can see from the above there are are a hierarchy between categories.
If I have the following category tree:
|-- Item 1
|---- Child 1
|---- Child 2
|---- Child 3
|-- Item 2
And the user wants to display Item1, I want to include all the categories for Child 1, 2 and 3 in the result, i.e. four categories should be included (Item 1, Child 1, Child 2, Child 3) in the query.
How can I do this with EntityFrameworkCore. I'm using SQLite as a backend but would prefer to do this without SQL if possible.
Have you tried using the ToString() method of DbFunction
ToString() will print the current object. So, It's children will also be printed.
you need to override this method in Item class.
You may fetch grandchildren like this:
Considering eager load
public List<Category> GetCategories(int itemId)
{
Category categoryChildren = _context.Set<Category>()
.Include(i => i.Subcategories)
.ThenInclude(i => i.Category)
.FirstOrDefault(w => w.ItemId == itemId);
var categories = new List<Category>();
if (categoryChildren == null)
return categories;
// get children
categories.AddRange(categoryChildren.Subcategories.Select(s => s.Category));
// get grandchildren
foreach (var subCategory in categoryChildren.Subcategories.Select(s => s.Category))
{
_context.Entry(subCategory).Collection(b => b.Subcategories).Load();
foreach (var categoryGrandChildren in subCategory.Subcategories)
{
_context.Entry(categoryGrandChildren).Reference(b => b.Category).Load();
// check if not adding repeatables
if (!categories.Any(a => a.Id == categoryGrandChildren.Id))
categories.Add(categoryGrandChildren.Category);
}
}
return categories;
}
If you're using lazy load you don't even need .Include and .Load methods.
public void PrintAllItems() //Use Take or where to fetch you specfic data
{
var allItems = context.Items
.Include(item=> item.Categories)
.ThenInclude(cat=>cat.Subcategories)
.ToList();
foreach(var item in allItems)
{
Console.WriteLine(item.Id);
foreach(var category in item.Categoires)
{
Console.WriteLine(category.Id);
foreach(var sub in category.Subcategories)
{
Console.WriteLine(sub.Id);
}
}
}
}
public void FirstItem(string Id) //Use Take or where to fetch you specfic data
{
var allItems = context.Items
.Include(item=> item.Categories)
.ThenInclude(cat=>cat.Subcategories)
.FirstOrDefault(g=>g.Id==Id);
foreach(var item in allItems)
{
Console.WriteLine(item.Id);
foreach(var category in item.Categoires)
{
Console.WriteLine(category.Id);
foreach(var sub in category.Subcategories)
{
Console.WriteLine(sub.Id);
}
}
}
}
Entity Framework is quite convenient with all it's automation, but unfortunately, like most things in life, it has yet to master every tricky situation out there, this being one of them. (Although to be fair, the problem pretty much lies within storing hierarchical data in a relational database).
I tend to solve similar situations by "cheating" a bit, at least whenever possible/suitable, by introducing some kind of additional property/column to group them, and then simply load them all, and do the relational mapping by hand, which is usually quite simple.
Loading additional data in one database call is often to prefer before making multiple calls. (You might still have to sneak around any lurking db-admin though).
Assuming you're planning for a situation with potentially N amount in breadth and M amount in depth (if not, the other answers should suffice), it's a quick and dirty solution which in worst case at least gets the job done.
To stick with EF, the idea is essentially to first decouple the relationships that EF might have mapped and use simple value types as reference: (It's not really a necessity, but something I tend to prefer)
class Item
{
public string Id { get; set; }
public virtual IEnumerable<Category> Categories { get; set; }
}
class Category
{
public string Id { get; set; }
// We drop the parent reference property and add a simple ParentId property instead,
// hopefully saving us some future headache.
//
public string ParentId { get; set; }
//public virtual Category Parent { get; set; } // Goodbye dear friend, you have served us well.
// Depending on how you're configuring, we might have to "loose" some EF-mapped relationships,
// [NotMapped] is merely an example of that here, it's not neccessarily required.
[NotMapped]
public virtual IEnumerable<Category> Subcategories { get; set; }
// As an example, I've just added the item id as our category scope/discriminator,
// allowing us to limit our full query at least somewhat.
//
public string ItemId { get; set; }
}
Now we're ready to do what EF does best. Load and map data! We will load a plan list of the category-entities all by themselves, without any direct relationships to anything else, and then map them ourselves.
To make it maintainable, let's create a neat little static class and add some useful extensions methods that will assist us, starting with the initial DbContext-load.
public static class CategoryExtensions
{
/// <summary>
/// Extension method to find and load all <see cref="Category"/> per <see cref="Category.ItemId"/>
/// </summary>
public static List<Category> FindCategoriesForItemId(this DbContext dbContext, string itemId)
=> dbContext.Set<Category>()
.Where(c => c.ItemId == itemId)
.ToList();
}
Once we're able to easily load categories, it would be useful to be able to map the children and possibly flatten them/any subcategory if necessary, so we throw two more methods in there, one to map child categories to all the categories we've found, and one to flatten a hierarchically structure we might have in the future (or just for fun).
/// <summary>
/// Flattens the IEnumerable by selecting and concatenating all children recursively
/// </summary>
/// <param name="predicate">Predicate to select the child collection to flatten</param>
/// <returns>Flat list of all items in the hierarchically constructed source</returns>
public static IEnumerable<TSource> Flatten<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> predicate)
=> source.Concat(source.SelectMany(s => predicate(s).Flatten(predicate)));
/// <summary>
/// "Overload" for above but to use with a single root category or sub category...
/// </summary>
public static IEnumerable<TSource> Flatten<TSource>(this TSource source, Func<TSource, IEnumerable<TSource>> predicate)
=> predicate(source).Flatten(predicate);
/// <summary>
/// For each entry in the <paramref name="flatSources"/>,
/// finds all other entries in the <paramref name="flatSources"/> which has
/// a <paramref name="parentRefPropSelector"/> value matching initial entries
/// <paramref name="identityPropSelector"/>
/// </summary>
/// <param name="flatSources">Flat collection of entities that can have children</param>
/// <param name="identityPropSelector">Selector Func to select the identity property of an entry</param>
/// <param name="parentRefPropSelector">Selector Func to select the parent reference property of an entry</param>
/// <param name="addChildren">Action that is called once any children are found and added to a parent entry</param>
public static IEnumerable<TSource> MapChildren<TSource, TKey>(
this IEnumerable<TSource> flatSources,
Func<TSource, TKey> identityPropSelector,
Func<TSource, TKey> parentRefPropSelector,
Action<TSource, IEnumerable<TSource>> addChildren)
=> flatSources.GroupJoin( // Join all entityes...
flatSources, // ... with themselves.
parent => identityPropSelector(parent), // On identity property for one...
child => parentRefPropSelector(child), // ... And parent ref property for another.
(parent, children) => // Which gives us a list with each parent, and the children to it...
{
addChildren(parent, children); // ... Which we use to call the addChildren action, leaving adding up to the caller
return parent;
});
That's it. It's not perfect, but, in my opinion, it's a decent enough starter solution that still takes advantage of EF and doesn't complicate it too much. Only worry is if the amount of categories loaded becomes too large, but at that point, it will be well worth spending some actual time on a more "proper" solution. (I haven't actually tested that MapChildren-extension, and there's a lot of room for improvements to it, but I hope it helps to illustrate the idea.)
To eventually actually use it, it ends up looking something like this:
/// <summary>
/// Loads and structures all categories related to <see cref="itemId"/>
/// and returns first <see cref="Category"/> where <see cref="Category.ParentId"/>
/// is null.
/// </summary>
public Category GetMeRootCategorylore(string itemId)
{
using (var dbContext = new DbContext())
{
var mappedAndArmedCategories
= dbContext // Use our db context...
.FindCategoriesForItemId(itemId) // To find categories..
.MapChildren( // And then immediately map them, which comes close to what we're used with when using EF.
parent => parent.Id, // Set the identity property to map children against
child => child.ParentId, // Set the parent references to map with
(parent, children) => parent.Subcategories = children); // This is called when children have been found and should be mapped to the parent.
// Oh noes, what if I need a flattened category list later for whatever reason! (Or to do some real lazy loading when looking a single one up!)
// ... Aha! I almost forgot about our nifty extension method to flatten hierarchical structures!
//
var flattenedList = mappedAndArmedCategories.Flatten(c => c.Subcategories);
// Maybe we'll pick up a root category at some point
var rootCategory = mappedAndArmedCategories.FirstOrDefault(c => c.ParentId == null);
// And perhaps even flatten it's children from the single category node:
var subFlattenedList = rootCategory?.Flatten(c => c.Subcategories);
// But now we've had enough fun for today, so we return our new category friend.
return rootCategory;
}
}
Finally, here's a quite informative and helpful question about hierarchical data in relational databases if you'd like to dig in deeper or get some other ideas: What are the options for storing hierarchical data in a relational database?
I am working on upgrading a WPF application from using .Net4/EF 4.4 to .Net4.5/EF 6.1. After the upgrade I will use DbContext (since there was no POCO-generator for ObjectContext).
The application use a Repository/UnitOfWork-pattern to access Entity Framework, and before the upgrade I could set the ObjectSet.MergeOption to OverwriteChanges (in the repository-class), but the DbSet-class does not have this feature. However, I know that I can get to a ObjectSet from the DbContext by using the IObjectContextAdapter. (See code below). But it seems that setting the MergeOption on the created ObjectSet will not reflect back to the DbSet.
So my question is this: is there any way to convert the ObjectSet back to a DbSet (conserving the MergeOption-setting)?
This is some of the repository class:
public class SqlRepository<T> : IRepository<T> where T : class, IEntity
{
protected DbSet<T> dbSet;
public SqlRepository(DbContext context)
{
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var set = objectContext.CreateObjectSet<T>();
set.MergeOption = MergeOption.OverwriteChanges;
dbSet = context.Set<T>();
//I would like to do something like this: dbSet = (DbSet)set;
}
}
Although not a direct answer to your question, I have come up with a T4 based solution to the EF oversight around MergeOption not being accessible in DbSet. It seems from your question this is what you are looking for?
In the default Context you have something generated by the T4 generator like:
public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }
etc.
My approach is to edit the T4 to add Getters for each Entity that provide direct access the ObjectSet as an IQueryable:
public IQueryable<Person> GetPersons(MergeOption mergeOption = MergeOption.AppendOnly, bool useQueryImplentation = true)
{
return useQueryImplementation ? GetSet<Person>(mergeOption).QueryImplementation() : GetSet<Person>(mergeOption);
}
Then in a base DataContext
public class DataContextBase
{
/// <summary>
/// Gets or sets the forced MergeOption. When this is set all queries
/// generated using GetObjectSet will use this value
/// </summary>
public MergeOption? MergeOption { get; set; }
/// <summary>
/// Gets an ObjectSet of type T optionally providing a MergeOption.
/// <remarks>Warning: if a DataContext.MergeOption is specified it will take precedence over the passed value</remarks>
/// </summary>
/// <typeparam name="TEntity">ObjectSet entity Type</typeparam>
/// <param name="mergeOption">The MergeOption for the query (overriden by DataContext.MergeOption)</param>
protected IQueryable<TEntity> GetObjectSet<TEntity>(MergeOption? mergeOption = null) where TEntity : class
{
var set = Context.CreateObjectSet<TEntity>();
set.MergeOption = MergeOption ?? mergeOption ?? MergeOption.AppendOnly;
return set;
}
By creating a default Extension method for an IQueryable as below you can optionally add your own implenations of QueryImplementation for each table/type so that all users of your table get sorting or includes etc. (this part is not required to answer the question but its useful anyway)
So for example you could add the following to always Include Addresses when calling GetPersons()
public static class CustomQueryImplentations
{
public static IQueryable<Person> QueryImplementation(this IQueryable<Person> source)
{
return source
.Include(r => r.Addresses)
.OrderByDescending(c => c.Name);
}
}
Finally:
//just load a simple list with no tracking (Fast!)
var people = Database.GetPersons(MergeOption.NoTracking);
//user wants to edit Person so now need Attached Tracked Person (Slow)
var peson = Database.GetPersons(MergeOption.OverwriteChanges).FirstOrDefault(p => p.PersonID = 1);
//user makes changes and on another machine sometime later user clicks refresh
var people = Database.GetPersons(MergeOption.OverwriteChanges);
Or you can (as I have) write something like
Database.MergeOption = MergeOption.OverwriteChanges;
refresh loads of entities using existing Get methods but will now ALL overwrite Attached entities
Database.MergeOption = null;
Something to note is that if you load AsNoTracking before you make changes you need to either Re-Attach or probably better reload with OverwriteChanges to ensure you have the latest Entity.
I am using EF 5.0 and the model first approach. I have build a GenericRepository that has the basic get, insert, delete etc statements. Like:
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
My EF entities all have the attributes Modified and ModifiedBy. Now I want to change this values everytime I save an entity.
Is it possible to modify this two attributes (set the value) without writing an specific implementation all the time?
Thank you
I see two options for you to do this, but they both entail either introducing a base type or an interface for all of your entities to cover them in a generic function. I would prefer an interface, although each entity would have to implement it again and again.
Let's say you create
interface IAuditable
{
DateTime Modified { get; set; }
string ModifiedBy {get; set; } // User id?
}
Now you can do:
public virtual void Insert(TEntity entity)
where TEntity : IAuditable
{
entity.Modified = DateTime.Now;
entity.ModifiedBy = ???? // Whatever you get the name from
...
}
(Same for edit)
You can also subscribe to the context's SavingChanges event:
// In the constructor:
context.SavingChanges += this.context_SavingChanges;
private void context_SavingChanges(object sender, EventArgs e)
{
foreach (var auditable in context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Select(entry => entry.Entity)
.OfType<IAuditable>)
{
auditable.Modified = DateTime.Now;
auditable.ModifiedBy = ????;
}
}
If you work with DbContext you can get to the event by
((IObjectContextAdapter)this).ObjectContext.SavingChanges
I'd like to add that more reliable time tracking can (and maybe should) be achieved by database triggers. Now you depend on a client's clock.
You can do this using the following code in your all methods of repository where you want to.
public virtual void Edit(TEntity entity)
{
entity.Modified=DateTime.Now;
entity.ModifiedBy=User.Identity.Name;
//Other saving to repository code
}
I am moving from CodeFirst to DatabaseFirst to map my views. In my CodeFirst approach I had a base entity like this:
public abstract class BaseEntity
{
/// <summary>
/// Gets or sets the entity identifier
/// </summary>
public virtual int Id { get; set; }
... // Some more methods here for equality checking
}
I derived all my classes from this base class, since every one of them would have an Id. So I used this BaseClass to create a generic repository. My repository looks like this:
public partial class EfRepository<T> where T : BaseEntity
{
public readonly DemirbasContext context;
private DbSet<T> _entities;
/// <summary>
/// Ctor
/// </summary>
/// <param name="context">Object context</param>
public EfRepository(DemirbasContext context)
{
this.context = context;
}
public T GetById(object id)
{
return this.Entities.Find(id);
}
public void Insert(T entity)
{
try
{
if (entity == null)
throw new ArgumentNullException("entity");
this.Entities.Add(entity);
this.context.SaveChanges();
}
catch (Exception e)
{
...
}
}
// Other methods here Update, Delete etc
So I was able to create repositories just by specifying the generic type paremeter like this
EfRepository<Car> carRepo = new EfRepository<Car>();
In DatabaseFirst, I cannot derive the entity classes from a base class. Is there a way to do it or what would be your suggestions?
Oops, I missed code generators.
Right click on your data model (.EDM file) and click Add Code Generation Item. Choose DbContext (for simplified DbContext API).
This creates two files with .tt extensions: One for context and one for entities. If you expand the file, you will see .tt file holds all your classes there.
You can modify it as you need.