From what I have read the code I have should not be throwing an exception when no results are found. I am using a generic repository similar to what is found here:
http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/
The code where the exception is thrown:
List<CADDrawing> projectDrawings = repository.Find<CADDrawing>(x => x.ProjectNumber == result.StringResult)
.Where(y => y.Obsolete == false)
.ToList();
Does anyone have experience with this or know what would cause an exception to be thrown when running a query?
Update:
The find code:
public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> criteria) where TEntity : class
{
return criteria.SatisfyingEntitiesFrom(GetQuery<TEntity>()).AsEnumerable();
}
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
/*
* From CTP4, I could always safely call this to return an IQueryable on DbContext
* then performed any with it without any problem:
*/
// return DbContext.Set<TEntity>();
/*
* but with 4.1 release, when I call GetQuery<TEntity>().AsEnumerable(), there is an exception:
* ... System.ObjectDisposedException : The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
*/
// here is a work around:
// - cast DbContext to IObjectContextAdapter then get ObjectContext from it
// - call CreateQuery<TEntity>(entityName) method on the ObjectContext
// - perform querying on the returning IQueryable, and it works!
var entityName = GetEntityName<TEntity>();
IQueryable<TEntity> ThisQuery = ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
System.Data.Entity.Core.Objects.ObjectQuery objectQuery = (System.Data.Entity.Core.Objects.ObjectQuery)ThisQuery;
objectQuery.MergeOption = System.Data.Entity.Core.Objects.MergeOption.OverwriteChanges;
return ThisQuery;
}
public IQueryable<TEntity> SatisfyingEntitiesFrom(IQueryable<TEntity> query)
{
return query.Where(Predicate);
}
You're calling ToList() on a null object
Please see the following post for handling of null lists
how do I treat null lists like empty lists in linq?
Related
ASP .NET 5 MVC Core application.
Following method is used to find entity by key:
class MyStudents: Students { }
public TEntity FindNormalized<TEntity>(params object[] keyValues)
where TEntity : class
{
return Find<TEntity>(keyValues);
}
void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
where TEntity : class
{
var res = FindNormalized<TEntity>(keyValues);
if (res == null)
throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
CopyPropertiesTo(res, entity);
}
static void CopyPropertiesTo<T, TU>(T source, TU dest)
{ // https://stackoverflow.com/questions/3445784/copy-the-property-values-to-another-object-with-c-sharp
var sourceProps = typeof(T).GetProperties().Where(x => x.CanRead).ToList();
var destProps = typeof(TU).GetProperties()
.Where(x => x.CanWrite)
.ToList();
foreach (var sourceProp in sourceProps)
if (destProps.Any(x => x.Name == sourceProp.Name))
{
var p = destProps.First(x => x.Name == sourceProp.Name);
p.SetValue(dest, sourceProp.GetValue(source, null), null);
}
}
Using it with subclass
FindNormalized<MyStudents>(1);
throws exception
Cannot create a DbSet for 'MyStudents' because this type is not
included in the model for the context.
FindNormalized<Students>(1);
works.
How to fix this so that it can used with subclass type also ?
For getting table attribute from subclass code from Dynamic Linq works:
string GetTableAttrib(Type t)
{
foreach (Type t2 in SelfAndBaseClasses(t))
{
IEntityType entityType = Model.FindEntityType(t2);
if (entityType == null)
continue;
return entityType.GetTableName();
}
throw new ApplicationException(t.FullName + " no table attribute");
}
/// <summary>
/// Enumerate inheritance chain - copied from DynamicLinq
/// </summary>
static IEnumerable<Type> SelfAndBaseClasses(Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
Maybe this can used to implement Find also. Unfortunately Find throws exception. Is it reasonable to wrap Find using try/catch or is there better way ?
The EFCore Find method throws exception because the TEntity is not an entity type but a type deriving from the entity type. So to solve this, we need to first get the most derived type which is the ancestor type of the passed in TEntity type, so when passing in MyStudents you need to get type Students first to use as type argument for Find<> method. Because that type is not known at design time, so we need to use reflection to invoke that Find<> method or better use the other overload of Find that accepts the first argument of entity type (Type). Of course I understand that your Find<> method in your question is from DbContext.Find.
So the solution can be simple like this, firstly we need method to get the most derived entity type from the passed in type (which inherits from the entity type):
public static class DbContextExtensions {
public static Type GetMostDerivedEntityType(this DbContext dbContext, Type type){
while(type != null && dbContext.Model.FindEntityType(type) == null) {
type = type.BaseType;
}
return type;
}
}
Next just use that extension method in your FindNormalized method to find the most derived entity type first before using Find:
//I suppose that this is in the context (class) of your custom DbContext
void FindNormalized<TEntity>(TEntity entity, params object[] keyValues)
where TEntity : class
{
var actualEntityType = this.GetMostDerivedEntityType(typeof(TEntity)) ??
throw new InvalidOperationException($"The type {typeof(TEntity)} is not an entity type or a derive from an entity type");
//use the actualEntityType instead
var res = Find(actualEntityType, keyValues);
if (res == null)
throw new KeyNotFoundException(entity.GetType().Name + " entity key not found " + keyValues[0]);
//copy the properties
CopyPropertiesTo(res, entity);
}
Note that in the above code I use another overload (non-generic version) of Find which accepts the first argument of Type (entity type) directly instead of using your wrapper FindNormalized which is generic only (you can add your one overload of that wrapper which wraps the non-generic Find as used directly in my code above).
I've not tested the code at all. Just try it and let me know if there is any error.
I'm using Specification pattern in EF Code First. When I do order by operation, VS throw a new exception
The specification pattern is copy from eShopOnWeb
I just change a little bit, here is my change code:
public class Specification<T> : ISpecification<T>
{
public Expression<Func<T, object>> OrderBy { get; private set; }
public Specification(Expression<Func<T, bool>> criteria)
{
Criteria = criteria;
}
public Specification<T> OrderByFunc(Expression<Func<T, object>> orderByExpression)
{
OrderBy = orderByExpression;
return this;
}
}
Here is my invoke code, it's very pretty simple:
static void TestSpec()
{
var spec = new Specification<ExcelData>(x => x.RowIndex == 5)
.OrderByFunc(x => x.ColumnIndex);
using (var dbContext = new TechDbContext())
{
var top10Data = dbContext.ExcelData.Take(10).ToList();
var listExcel = dbContext.ApplySpecification(spec).ToList();
Console.WriteLine();
}
}
If I comment OrderByFunc, then everything is fine to me. no error throw from vs.
I had try many times search the error message in google, but none of answer is my case.
So I have to ask a question in here.
When I debug OrderBy property in SpecificationEvaluator.cs, I found there is a Convert method.
So I know the error is about cast error, but how do I fix this cast type error?
Please help me!
The solution is to create new lambda expression with cast (Convert) removed, and then use it to call the Queryable class OrderBy / OrderByDescending method either dynamically (using DLR dispatch or reflection) or by emitting Expression.Call to it.
For the first part, add the following helper method to the SpecificationEvaluator class:
static LambdaExpression RemoveConvert(LambdaExpression source)
{
var body = source.Body;
while (body.NodeType == ExpressionType.Convert)
body = ((UnaryExpression)body).Operand;
return Expression.Lambda(body, source.Parameters);
}
Then replace the code
query = query.OrderBy(specification.OrderBy);
with either
query = Queryable.OrderBy((dynamic)query, (dynamic)RemoveConvert(specification.OrderBy));
or
var keySelector = RemoveConvert(specification.OrderBy);
query = query.Provider.CreateQuery<T>(Expression.Call(
typeof(Queryable), nameof(Queryable.OrderBy),
new[] { typeof(T), keySelector.ReturnType },
query.Expression, keySelector));
Do similar for the specification.OrderByDescending.
I am writing a generic querying method in Entity Framework 6, based off of this helpful article. Here's how it looks:
public static T QueryEagerLoad<T>(Expression<Func<T, bool>> match) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
databaseContext.Configuration.LazyLoadingEnabled = false;
T retrievedObject = databaseContext.Set<T>().SingleOrDefault(match);
return retrievedObject;
}
}
I'm attempting to eagerly load any related entities, so I include disable to configuration variable LazyLoadingEnabled. While it loads the object, it does not load the related entities, per my view in the debugger. Why would this be? Am I missing something? I should note that I'm using Npgsql. Thanks in advance.
See Mikael Östberg's answer to this question. To use a generic method for querying with eager loading, it seems necessary to inject the includes. Here's how the generic method shaped up:
public static T Query<T>(Expression<Func<T, bool>> match, List<Expression<Func<T, object>>> includes) where T : class
{
using (var databaseContext = new ClearspanDatabaseContext())
{
var dataSet = databaseContext.Set<T>(); // Get the relevant DataSet
T retrievedObject = includes.Aggregate( // Eagerly load the passed navigation properties
dataSet.AsQueryable(),
(current, include) => current.Include(include)
).SingleOrDefault(match); // Find exactly one or zero matches
return retrievedObject;
}
}
And an example of a call that injects the properties to eagerly load (the includes parameter in the generic method above):
public static Lumber GetLumber(int databaseId)
{
Expression<Func<Lumber, object>> lengthProperty = (lumber => lumber.Length);
Expression<Func<Lumber, object>> thicknessProperty = (lumber => lumber.Thickness);
Expression<Func<Lumber, object>> widthProperty = (lumber => lumber.Width);
List<Expression<Func<Lumber, object>>> lumberNaviationProperties = new List<Expression<Func<Lumber, object>>>() { lengthProperty, thicknessProperty, widthProperty };
Lumber retrievedLumber = DatabaseOperations.Query<Lumber>((lumber => lumber.DatabaseId == databaseId), lumberNaviationProperties);
return retrievedLumber;
}
I am using Entity Framework and when a DbUpdateException is thrown from dbContext.SaveChanges() how do I create a custom exception and throw that instead?
Would I create a condition on each SQL constraint that can be thrown:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new CustomException("message");
}
EDIT: That approach makes good sense to me. If you know your application/DB is going to have a specific error, and it will help you or your users to have a specific custom exception type that quickly identifies what would otherwise be a somewhat complex or specific scenario, then absolutely yes. It's good practice to use both the exception type and the exception message to make the error as clear as possible. My code below is an even simpler example than what you seem to drilling down into. Rather than letting other code end up with a null reference exception or some other consequence, I beat everything to the punch with throw new DatabaseDataNotFoundException("Cannot find ServerAppConfiguration value for {0}", key);.
Just make your own exception class that inherits from Exception, here's a custom exception I use for exactly that purpose:
public class DatabaseDataNotFoundException : Exception
{
public DatabaseDataNotFoundException() : base() { }
public DatabaseDataNotFoundException(string message) : base(message) { }
public DatabaseDataNotFoundException(string message, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args)) { }
public DatabaseDataNotFoundException(string message, Exception inner)
: base(message, inner) { }
public DatabaseDataNotFoundException(string message, Exception inner, params object[] args)
: base(String.Format(CultureInfo.CurrentCulture, message, args), inner) { }
protected DatabaseDataNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
Then your code becomes:
if (e.InnerException.InnerException.Message.Contains("UNIQUE KEY"))
{
throw new DatabaseDataNotFoundException("message");
}
I try to do the following with entity framework 4 :
public void Update(Site entity)
{
using (db)
{
db.Sites.Attach(db.Sites.Single(s => s.Id == entity.Id));
db.Sites.ApplyCurrentValues(entity);
db.SaveChanges();
}
}
But when i try to update a site through this method i get an error telling me that :
The conversion of a datetime2 data
type to a datetime data type resulted
in an out-of-range value. The
statement has been terminated.
And this is because the original site for some reason is not loaded through the Attach() method.
Can someone help with this ?
/Martin
You don't need to "attach" something you are already retrieving (Ladislav is right). Once you retrieve an entity (e.g SingleOrDefault), it is "in the graph" (EF memory - so it can do optimistic concurrency).
If your trying to do an UPDATE< and the "entity" your passing through is new/detached...
Try the stub technique:
public void Update(Site entity)
{
using (db)
{
var stub = new Site { Id = entity.Id }; // create stub with given key
db.Sites.Attach(stub); // stub is now in graph
db.Sites.ApplyCurrentValues(entity); // override graph (stub) with entity
db.SaveChanges();
}
}
That being said, the error you have provided points to some other issue (data conversion).
Have you checked the "date" values you are passing through with the data type on the model?
public ActionResult Edit(int id, Client collection)
{
try
{
// make sure the rec is in the context
var rec = dbEntities.Clients.First(r => r.ClientID == id);
// update the rec in the context with the parm values
dbEntities.Clients.ApplyCurrentValues(collection);
// make the changes permanent
dbEntities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}