DbSet does not have a search method in EF7

I am trying to create a shared repository to access my database. In EF6, I was able to do this to get a specific object:

protected IDbSet<T> dbset; public T Get(object id) { return this.dbset.Find(id); } 

DbSet in EF7 lacks the Find method. Is there a way to implement the above code snippet?

+45
entity-framework-core
Mar 13 '15 at 10:57
source share
11 answers

Here's a very crude, incomplete, and untested implementation of .Find() as an extension method. If nothing else, this should make you point in the right direction.

Actual implementation tracked # 797 .

 static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class { var context = ((IAccessor<IServiceProvider>)set).Service.GetService<DbContext>(); var entityType = context.Model.GetEntityType(typeof(TEntity)); var key = entityType.GetPrimaryKey(); var entries = context.ChangeTracker.Entries<TEntity>(); var i = 0; foreach (var property in key.Properties) { var keyValue = keyValues[i]; entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValue); i++; } var entry = entries.FirstOrDefault(); if (entry != null) { // Return the local object if it exists. return entry.Entity; } // TODO: Build the real LINQ Expression // set.Where(x => x.Id == keyValues[0]); var parameter = Expression.Parameter(typeof(TEntity), "x"); var query = set.Where((Expression<Func<TEntity, bool>>) Expression.Lambda( Expression.Equal( Expression.Property(parameter, "Id"), Expression.Constant(keyValues[0])), parameter)); // Look in the database return query.FirstOrDefault(); } 
+22
Mar 16 '15 at 16:47
source share
— -

If you are using EF 7.0.0-rc1-final , below you will find a small update for the code provided by @bricelam in the previous answer. By the way, thank you very much @bricelam - your code was very useful for me.

Here are my dependencies in the "project.config" section:

 "dependencies": { "EntityFramework.Commands": "7.0.0-rc1-final", "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", "Microsoft.Framework.Configuration.Json": "1.0.0-beta8", "Microsoft.Framework.ConfigurationModel": "1.0.0-beta4", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", "Microsoft.Framework.DependencyInjection": "1.0.0-beta8" } 

And below is the extension method for DbSet.Find (TEntity) :

 using Microsoft.Data.Entity; using Microsoft.Data.Entity.Infrastructure; using System; using System.Linq; using System.Linq.Expressions; namespace Microsoft.Data.Entity.Extensions { public static class Extensions { public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class { var context = ((IInfrastructure<IServiceProvider>)set).GetService<DbContext>(); var entityType = context.Model.FindEntityType(typeof(TEntity)); var key = entityType.FindPrimaryKey(); var entries = context.ChangeTracker.Entries<TEntity>(); var i = 0; foreach (var property in key.Properties) { entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i]); i++; } var entry = entries.FirstOrDefault(); if (entry != null) { // Return the local object if it exists. return entry.Entity; } // TODO: Build the real LINQ Expression // set.Where(x => x.Id == keyValues[0]); var parameter = Expression.Parameter(typeof(TEntity), "x"); var query = set.Where((Expression<Func<TEntity, bool>>) Expression.Lambda( Expression.Equal( Expression.Property(parameter, "Id"), Expression.Constant(keyValues[0])), parameter)); // Look in the database return query.FirstOrDefault(); } } } 
+18
Dec 10 '15 at 12:48
source share

I took some of the previously provided answers and tweaked them to fix a couple of problems:

  • Implicitly committed close
  • The key must not be hardcoded for "Id"

     public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class { var context = set.GetService<DbContext>(); var entityType = context.Model.FindEntityType(typeof(TEntity)); var key = entityType.FindPrimaryKey(); var entries = context.ChangeTracker.Entries<TEntity>(); var i = 0; foreach (var property in key.Properties) { var i1 = i; entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i1]); i++; } var entry = entries.FirstOrDefault(); if (entry != null) { // Return the local object if it exists. return entry.Entity; } var parameter = Expression.Parameter(typeof(TEntity), "x"); var query = set.AsQueryable(); i = 0; foreach (var property in key.Properties) { var i1 = i; query = query.Where((Expression<Func<TEntity, bool>>) Expression.Lambda( Expression.Equal( Expression.Property(parameter, property.Name), Expression.Constant(keyValues[i1])), parameter)); i++; } // Look in the database return query.FirstOrDefault(); } 
+9
Apr 20 '16 at 11:19
source share

You can't comment because of reputation, but if you use RC2 (or later?), You should use

 var context = set.GetService<ICurrentDbContext>().Context; 

instead

 var context = set.GetService<DbContext>(); 
+9
Jun 07 '16 at 12:31 on
source share

There is not enough reputation for comments, but there is an error in @ Roger-Santana's answer when using it in a console application / separate assembly:

 var i = 0; foreach (var property in key.Properties) { entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i]); i++; } var entry = entries.FirstOrDefault(); 

The value of "i" is fixed in foreach, so when entry.FirstOrDefault () is called, keyValues ​​[i] has a value (at least) of keyValues ​​[i ++], which in my case crashed with the output of the index error. The fix would be to copy the value of "i" through the loop:

 var i = 0; foreach (var property in key.Properties) { var idx =i; entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[idx]); i++; } var entry = entries.FirstOrDefault(); 
+5
Mar 26 '16 at 23:34
source share

Find finally arrives at the core of the Entity Framework.

+5
Jun 17 '16 at 5:53 on
source share

So ... the above search methods did a great job, but if your model does not have a column named "Id", all of this will fail on the next line. I'm not sure why the OP would put a solid value in this place

  Expression.Property(parameter, "Id"), 

Here's a revision that will fix it for those that match our identifier columns. :)

 var keyCompare = key.Properties[0].Name; // TODO: Build the real LINQ Expression // set.Where(x => x.Id == keyValues[0]); var parameter = Expression.Parameter(typeof(TEntity), "x"); var query = set.Where((Expression<Func<TEntity, bool>>) Expression.Lambda( Expression.Equal( Expression.Property(parameter, keyCompare), //Expression.Property(parameter, "Id"), Expression.Constant(keyValues[0])), parameter)); // Look in the database return query.FirstOrDefault(); } 

This STILL can very well fail if you have more than one key setting on the entity object, and the key you are viewing is not the first, but it should be quite a bit btter this way.

+4
Feb 10 '16 at 18:11
source share

I am using linq ; instead of the Find method, you can use:

 var record = dbSet.SingleOrDefault(m => m.Id == id) 
+4
Mar 03 '16 at 16:35
source share

Let me make changes that include creating an expression. I admit, I did not really check this; -)

  public static TEntity Find<TEntity>(this DbSet<TEntity> dbSet, params object[] keyValues) where TEntity : class { // Find DbContext, entity type, and primary key. var context = ((IInfrastructure<IServiceProvider>)dbSet).GetService<DbContext>(); var entityType = context.Model.FindEntityType(typeof(TEntity)); var key = entityType.FindPrimaryKey(); // Build the lambda expression for the query: (TEntity entity) => AND( entity.keyProperty[i] == keyValues[i]) var entityParameter = Expression.Parameter(typeof(TEntity), "entity"); Expression whereClause = Expression.Constant(true, typeof(bool)); uint i = 0; foreach (var keyProperty in key.Properties) { var keyMatch = Expression.Equal( Expression.Property(entityParameter, keyProperty.Name), Expression.Constant(keyValues[i++]) ); whereClause = Expression.And(whereClause, keyMatch); } var lambdaExpression = (Expression<Func<TEntity,bool>>)Expression.Lambda(whereClause, entityParameter); // Execute against the in-memory entities, which we get from ChangeTracker (but not filtering the state of the entities). var entries = context.ChangeTracker.Entries<TEntity>().Select((EntityEntry e) => (TEntity)e.Entity); TEntity entity = entries.AsQueryable().Where(lambdaExpression).First(); // First is what triggers the query execution. // If found in memory then we're done. if (entity != null) { return entity; } // Otherwise execute the query against the database. return dbSet.Where(lambdaExpression).First(); } 
+1
Mar 29 '16 at 2:26
source share

this is what i use. Not a search method, but works like a charm

 var professionalf = from m in _context.Professionals select m; professionalf = professionalf.Where(s => s.ProfessionalId == id); Professional professional = professionalf.First(); 
0
Oct 16 '15 at 18:43
source share

It was suggested to change ".First ()" to ".FirstOrDefault ()" in the very last line of my previous post. Editing was rejected, but I agree with that. I would expect the function to return null if the key was not found. I would not want him to throw an exception. In most cases, I would like to know if a key existed in the set, and exception handling is a very slow way to figure this out.

0
May 17 '16 at 2:07
source share



All Articles