In EntityFramework, is that possible to query the objects that have just been added to the context using AddObject but before calling the SaveChanges method?
Thanks
To persist an entity you usually add it to it's DbSet in the context.
For example
var bar = new Bar();
bar.Name = "foo";
var context = new Context();
context.Bars.Add(bar);
Surprisingly, querying context.Bars, the just added entity cannot be found
var howMany = context.Bars.Count(b => b.Name == "foo");
// howMany == 0
After context.SaveChanges() the same line will result 1
The DbSet seems unaware to changes until they're persisted on db.
Fortunately, each DbSet has a Local property that acts like the DbSet itself, but it reflect all in-memory operations
var howMany = context.Bars.Local.Count(b => b.Name == "foo");
// howMany == 1
You can also use Local to add entities
context.Bars.Local.Add(bar);
and get rid of the weird behavior of Entity Framework.
you can query objects like this,
context.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Select(obj => obj.Entity).OfType<TheEntityType>()
this will query the objects which are in added state. If you want other states too you can pass all other states to GetObjectStateEntries method like this.
GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Unchanged)
In hibernate transient instances are already attached to context. Just stumbled upon this EF restriction.
I did not managed to intersect/union the ObjectSet with its transient entities ObjectSet.Local but for our usecase the below find method is sufficient.
In our cases we create some entities lazy depending on unique criteria during an iteration
Find method
If you are using an repository pattern you can create a method like:
public interface IRepository<T> where T : class, IEntity
{
/// <summary>
/// Finds the unique Entity with the given predicate.
/// Depending on implementation also checks transient / local (unsaved) Entities.
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IQueryable<T> FindAll(Expression<Func<T, bool>> predicate);
}
public class EfRepository<T> : IRepository<T> where T : class, IEntity
{
protected readonly ObjectContext context;
protected readonly ObjectSet<T> objectSet;
/// <summary>
/// Creates a new repository of the given context.
/// </summary>
/// <param name="context"></param>
public EfRepository(ObjectContext context)
{
if (context == null)
throw new ArgumentException("Context must not be null.");
this.context = context;
this.objectSet = context.CreateObjectSet<T>();
}
/// <summary>
/// Also takes local context into consideration for unsaved changes
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
public T Find(Expression<Func<T, bool>> predicate)
{
T result = this.objectSet.Where(predicate).FirstOrDefault();
if (result == null)
result = this.objectSet.Local().Where(predicate).FirstOrDefault();
return result;
}
}
Related
My application is using EF6 database first approach. All the entities in the database have 2 common properties "CreatedDateTime" and "ModifiedDateTime".
Currently When i do SaveChanges() im explicitly setting these 2 properties based on if i am creating new entity or updating existing entity.
If its a new entity then set both properties else set only ModifiedDateTime property.
I wanted to know if there is a way to implicitly set these 2 properties on Save or update operation?
Update 1
I know i have to override Savechanges() method however the real issue here is SaveChanges needs to have access to these 2 properties. So i only see 2 options here:
1> Use reflection to find if entity has these properties and set it.
2> Modify default T4 generation so that it derives all entities with predefined interface. And this interface will have these 2 properties. SaveChanges() method can check if entity is derived from this interface and set the property.
I defiantly don't want to use option 1 using refection.
Is there any other way or has anyone done this before in DB first approach?
Going with your second approach: Adjust your T4 files to include a reference to an interface (e.g. IChangeTrack):
public interface IChangeTrack
{
/// <summary>
/// When has this entry be created
/// </summary>
[Required]
DateTime CreatedDateTime { get; set; }
/// <summary>
/// When has this entry been modified
/// </summary>
DateTime? ModifiedDateTime { get; set; }
}
Now overwrite your SaveChanges() routine by doing something like this:
/// <summary>
/// Enhance save changes to handle system fields.
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
HandleChanges();
int changes = base.SaveChanges();
return changes;
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
HandleChanges();
int changes = await base.SaveChangesAsync(cancellationToken);
return changes;
}
private void HandleChanges()
{
ChangeTracker.DetectChanges();
var entries = ChangeTracker.Entries<IChangeTrack>();
if (entries != null)
{
foreach (DbEntityEntry<IChangeTrack> entry in entries)
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedDateTime = DateTime.UtcNow
break;
case EntityState.Modified:
entry.Entity.ModifiedDateTime = DateTime.UtcNow;
break;
}
}
}
}
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 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.
I'm using EF4.1 for DAL for an application, and I use DbContext template generator with POCP entities. The model is created from the database, so all the fields / PK's / FK's / relations are already defined in database.
I need to find out in code which are the fields for the table for an entity.
Some tables might have a single field PK, while other might have compound PK. Whan I need is to have a method that will return me a List for an entity, with all field names composing the primary keys. It can be from DbContext, or from entity, doesn't matter.
I could even customize the template to generate a method in POCO entity, as below:
public List<string> PrimaryKey()
{
List<string> pk = new List<string>();
pk.AddRange(
new string[] {"Field1", "Field2"});
return pk;
}
but I don't know how to find the field names composing the PK.
Any suggestions?
Thank you
I did some research and I modified the template to generate a property that returns this for me.
First I customized the template to generate strong typed names for field names (I hate using strings in code which can cause problems when refactoring). Then that is used to generate a property that returns primary key fields as List
Here are the changes to template (I used ADO.NET DbContext Template Generator, but for any other template it should be very similar):
<#=Accessibility.ForType(entity)#>
<#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#>
<#=code.StringBefore(" : ", code.Escape(entity.BaseType))#>
{
<#
WriteStrongTypedPropertyNames(code, entity); // <-- Insert this
WritePrimaryKeyProperty(code, entity); // <-- and this
// .....
And at the end of template file add:
<#+
void WriteStrongTypedPropertyNames(CodeGenerationTools code, EntityType entity)
{
#> /// <summary>
/// Strong typed property names
/// </summary>
public class PropertyNames
{
<#+
foreach (var property in entity.Properties)
{
#>
public const string <#=code.Escape(property)#> = "<#=property#>";
<#+
}
#>
}
<#+
}
void WritePrimaryKeyProperty(CodeGenerationTools code, EntityType entity)
{
#> /// <summary>
/// Returns primary key as List
/// </summary>
public List<string> PrimaryKey
{
get
{
List<string> pk = new List<string>();
pk.AddRange(
new string[] {
<#+
foreach (var member in entity.KeyMembers)
{
string delim = "";
#>
<#=delim#> PropertyNames.<#=code.Escape(member.Name)#>
<#+
delim=",";
}
#> });
return pk;
}
}
<#+
}
#>
It generates a code as below in the entity:
/// <summary>
/// Strong typed property names
/// </summary>
public class PropertyNames
{
public const string AppID = "AppID";
public const string AppName = "AppName";
public const string AppCode = "AppCode";
}
/// <summary>
/// Returns primary key as List
/// </summary>
public List<string> PrimaryKey
{
get
{
List<string> pk = new List<string>();
pk.AddRange(
new string[] {
PropertyNames.AppID
});
return pk;
}
}
Hope this helps someone
I had a tough time trying to do almost the same thing, getting the primary key name and value at runtime when the type is unknown, from a DbContext. I was just get trying to implement an auditing scheme for deletes, and every solution i find involves superfluous code that I dont really understand. The EntityKey is not available from a DbContext, which is also confusing and annoying. The last 5 lines may save you 5 hours and 1 yr of baldness. I am not attempting this for Inserts, so if you do, you need to inspect those values carefully as they may be 0 or null.
foreach(var entry in ChangeTracker.Entries<IAuditable>()) {
...
case EntityState.Deleted:
...
var oc = ((IObjectContextAdapter.this).ObjectContext; //.this is a DbContext EntityKey
ek = oc.ObjectStateManager.GetObjectStateEntry(entry.Entity).EntityKey;
var tablename= ek.EntitySetName;
var primaryKeyField = ek.EntityKeyValues[0].Key; //assumes only 1 primary key var
primaryKeyValue = ek.EntityKeyValues[0].Value;
stumbled across this post after having done something similar to #bzamfir. I thought I would post what I did in hopes that it might alleviate some of the headaches that #Bill mentions that I have also experienced!
Using the DBContext, I also modified the Template.
1) Add Imports System.ComponentModel.DataAnnotations to the list of Imports already in the file
2) Add code to the Primitive Properties For Each Loop which adds the KeyAttribute to the primary key properties. It should look like:
For Each edmProperty As EdmProperty In primitiveProperties
If ef.IsKey(edmProperty) Then
#><#= code.StringBefore("<KeyAttribute()>",Environment.NewLine & CodeRegion.GetIndent(region.CurrentIndentLevel + 2))#><#
End If
WriteProperty(code, edmProperty)
Next
You can add this Extension Method to your code somewhere which will find the Primary Key based off of the KeyAttribute.
<Extension()>
Public Function FindPrimaryKeyProperty(Of T As Class)(context As DbContext, TEntity As T) As PropertyInfo
Return TEntity.GetType().GetProperties().Single(Function(p) p.GetCustomAttributes(GetType(KeyAttribute), True).Count > 0)
End Function
I do recognize the point that this function will fail if you have more than one property flagged with KeyAttribute, however, for my situation this was not the case.
Alternatively, I just came across this solution that appears to work just fine and doesn't require any Template editing (+1).
http://blog.oneunicorn.com/2012/05/03/the-key-to-addorupdate/
public static IEnumerable<string> KeysFor(this DbContext context, Type entityType)
{
Contract.Requires(context != null);
Contract.Requires(entityType != null);
entityType = ObjectContext.GetObjectType(entityType);
var metadataWorkspace =
((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var objectItemCollection =
(ObjectItemCollection)metadataWorkspace.GetItemCollection(DataSpace.OSpace);
var ospaceType = metadataWorkspace
.GetItems<EntityType>(DataSpace.OSpace)
.SingleOrDefault(t => objectItemCollection.GetClrType(t) == entityType);
if (ospaceType == null)
{
throw new ArgumentException(
string.Format(
"The type '{0}' is not mapped as an entity type.",
entityType.Name),
"entityType");
}
return ospaceType.KeyMembers.Select(k => k.Name);
}
I've faced with situation when I need to have EF readonly property in case of 'optimistic update'(you do not load current state of your domain object from database to check what properties are really changed. You just set your object as Modified and update it to database. You avoid redundant select and merge operations in this case).
You can't write something like this : DataContext.Entry(entity).Property(propertyName).IsModified = false;, because setting of 'false' value is not supported and you will get an exception. (in EF 4.1)
I've created a simple structure for registering readonly properties in repository.
So, you can easy Modify just nonreadonly properties.
What do you think about this?
public abstract class RepositoryBase<T> where T : class
{
private const string MethodReferenceErrorFormat = "Expression '{0}' refers to a method, not a property.";
private const string FieldReferenceErrorFormat = "Expression '{0}' refers to a field, not a property.";
protected IList<PropertyInfo> _readOnlyProperties;
/// <summary>
/// This method is used to register readonly property for Entity.
/// </summary>
/// <param name="propertyLambda">Entity property as LambdaExpression</param>
protected void RegisterReadOnlyProperty<TProperty>(Expression<Func<T, TProperty>> propertyLambda)
{
Guard.ArgumentNotNull(propertyLambda, "propertyLambda");
var propertyMember = propertyLambda.Body as MemberExpression;
if (propertyMember == null)
{
var exceptionMessage = string.Format(MethodReferenceErrorFormat, propertyLambda);
throw new ArgumentException(exceptionMessage);
}
var propertyInfo = propertyMember.Member as PropertyInfo;
if (propertyInfo == null)
{
var exceptionMessage = string.Format(FieldReferenceErrorFormat, propertyLambda);
throw new ArgumentException(exceptionMessage);
}
_readOnlyProperties.Add(propertyInfo);
}
/// <summary>
/// This method is used to attach domain object to DbContext and mark it as modified to save changes.
/// </summary>
/// <param name="entity">Detached entity</param>
public void SetModified(T entity)
{
Guard.ArgumentNotNull(entity, "entity");
//Mark whole entity as Modified, when collection of readonly properties is empty.
if(_readOnlyProperties.Count == 0)
{
DataContext.Entry(entity).State = EntityState.Modified;
return;
}
//Attach entity to DbContext.
_dbSet.Attach(entity);
//Mark all properties except readonly as Modified.
var allProperties = entity.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var propertiesForUpdate = allProperties.Except(_readOnlyProperties);
foreach (var propertyInfo in propertiesForUpdate)
{
DataContext.Entry(entity).Property(propertyInfo.Name).IsModified = true;
}
}
This would work but I don't like the need to register modified properties directly in repository. You can forget about registered properties and code will accidentaly not save some changes - that will be bug which will be hard to find when reusing repository in complex scenarios. I like explicit definition of updated properties each time you call something like Update on your repository. Also I don't like reflection in the code. Unless you modify your code to get reflected data about each entity only once for whole application you are doing it wrong.
I wrote the answer for EFv4 but it can be easily modified to EFv4.1:
public void Update(T entity, params Expression<Func<T, object>>[] properties)
{
_dbSet.Attach(entity);
DbEntityEntry<T> entry = _context.Entry(entity);
foreach (var selector in properties)
{
entry.Property(selector).IsModified = true;
}
}
You will call it like:
repo.Update(entity, e => e.Name, e => e.Description);