EF DbSet.Find throws InvalidOperationException - c#

I have two entities connected by TPT inheritance pattern:
public class User {...}
public class Employee : User {...}
As you can see, base class isn't abstract so both entity types can be added into db-sets. There are two separate sets (I need them both in my model):
public DbSet<User> Users { get; set; }
public DbSet<Employee> Employees { get; set; }
So, basically, Users table contains all entities and Employees holds additional data only for objects that were instantiated as new Employee().
Now, when I try to get entity from Employees set using Find method, I'm expecting that it will only return 'actual' employees. But if I'm specifying Id of the User entity, EF still fetches it from the database and then throws an InvalidOperationException:
"The specified cast from a materialized
'System.Data.Entity.DynamicProxies.User_B2E5EC989E36BE8C53B9285A70C4E879F0B5672E1D141B93FD299D1BA60258EE'
type to the 'Data.Employee' type is not valid."
It can't cast User to Employee, which is understandable.
My question is - is there a way to configure TPT inheritance so Find just returns null in such cases as it does when you pass non-existing Id into it.
My current workaround is this:
public Employee GetEmployeeById(int id)
{
try
{
return Employees.Find(id);
}
catch(InvalidOperationException ex) when (ex.Message.StartsWith("The specified cast from a materialized"))
{
return null;
}
}
But I don't like how it looks - so maybe there is a better (more elegant) solution?

I tend to prefer singleordefault()/firstordefault() over find as it will return null directly if no matches are found, but could you use a predicate with Find like this?
return Employees.Find(em => em.id == id && em is Employee);

Your are missing your DbContext instance. You can't search on the Table Type coz thats declaration.
var checkfind = dbInstance.Employees.Find(searchedID);
If you don't have access directly to your Db you use
using (DBLocal db = new DBLocal())
{
db.Employees.Find(searchedID);
}

Related

Using filtered rows as part of DBContext with derived class

I want to use class inheritance for filtering rows in database which is not absolutery normal and relational. There is table entities mapped to model Entity via EF Core:
[Table("entities")]
public class Entity
{
public int Id { get; set; }
public string Type { get; set; }
}
Type is some string that can be "A" or "B" e.g.
I want to specify class EntityA : Entity for entities with type A and accordingly for B:
public class EntityA : Entity
{
// some unique A properties
}
public class EntityB : Entity
{
// some unique B properties
}
Basically my DBContext looks like
public class ApplicationContext : DbContext
{
public DbSet<Entity> Entities { get; set; }
// ...
}
Can I defined EntitiesA and EntitiesB in my DBContext using filtering by Type?
I wanted to write it at least in stupid way:
public List<EntityA> EntitiesA
{
get
{
return Entity.Where(x => x.Type == "A").ToList();
}
}
But there is class casting problem (because code returns List, not List) and also it's not like ORM-style solution, EntitiesA is not DBSet, load query automatically and so on.
Okay, I found, It's called Discriminator in EF Core, those who interested can read here: https://learn.microsoft.com/en-us/ef/core/modeling/inheritance
I don't think it's a good idea to bring EntitiesA and EntitiesB to DBContext. Because basically, it contains some of the application domain knowledge (Business Layer), which should be completely decoupled with your DBContext (Data Access Layer).
I suggest to have a EntityLoader in Business Layer which is responsible for loading Entities from DB and return list of EntityA or B.
Regarding the class casting problem, you can fix the compile error with
return Entity.Where(x => x.Type == "A").Select(x => (EntityA)x).ToList();
However, you will get a runtime error since the Entity type is less specific than the EntityA type. Which mean you need to convert like this
return Entity.Where(x => x.Type == "A").Select(x => new EntityA(....)).ToList();

c# EF query explosion

Dealing with three tables - Company, Employee and User.
Company has 0 or Many Employees.
Employee has a nullable int FK to Company. In practice this alway has a value.
Employee has a non nullable int FK to User.
User has a bit field AccountIsDisabled.
In my Data Model I have a partial class extending the EF model class for Company.
On this the call to ActiveEmployees returning all Employees that are active for the company.
My problem is that this code is generating a query explosion.
For a company of 1K employees I am getting 1K calls to the DB. It seems EF is creating a call for each employee when navigating to the User table.
I have tried many methods to force eager loading but to no avail.
Anyone out there see a reason that I am getting this query explosion?
namespace JCS.Data
{
public partial class Company : IIdentifiable
{
public IEnumerable<Employee> ActiveEmployees
{
get
{
return Employees.Where(e => !e.User.AccountIsDisabled);
}
}
}
}
Sorry for the missing info.
The explosion of queries occurs when a bool property on the related Employee class is accessed. Like so
namespace JCS.Data
{
public partial class Employee : IIdentifiable
{
public bool ApprovesTimesheets
{
get
{
return Company.ActiveEmployees.Any(
employee => employee.TimesheetApproverEmployeeID == ID
&& employee.TimesheetsEnabled);
}
}
}
}
So anywhere in the code I go
bool approvesTimesheets = employee.ApprovesTimesheets;
I get the 1K queries.
I have tried adding ToLis() to the Company.ActiveEmployees. No joy.
e.g.
in Employee class
var activeEmployees = Company.ActiveEmployees.ToList();
var approvesTimesheets = activeEmployees .Any(
employee => employee.TimesheetApproverEmployeeID == ID
&& employee.TimesheetsEnabled);
the latest in a long line of failed attempts:
public List<Employee> ActiveEmployees
{
get
{
var employees = Employees.AsQueryable().Include(x => x.User).ToList();
return employees.Where(e => !e.User.AccountIsDisabled).ToList();
//return Employees.Where(e => !e.User.AccountIsDisabled);
}
}
You need to call .ToList() or ToListAsync() to get all the data at once otherwise it will get the data on the fly per record.
This is the problem with deferred execution VS immediate execution. When you don't materialize the list with .Where(foo).ToList() it loads each record whenever you try to access it therefore the 1000 DB calls.
edit: Please note that you are also using a navigational property that points to another object (my guess is that it's object mapped directly to a table ) so, when trying to get that object you also do additional DB calls. to avoid that do something like this :
public partial class Company : IIdentifiable
{
public IEnumerable<Employee> ActiveEmployees
{
get
{
return Employees.Where(e => !e.User.AccountIsDisabled).Include(x=>x.User).ToList();
}
}
}
OK the problem stemmed from using navigation properties in the POCOs.
As pointed out by #IvanStoev I could only enforce Eager Loading at the initial call to the DB.
So when I load the initial Employee object I need to load all related objects. So..
_currentUser = Repository.Context.Employees.Include("User").Include("Company.Employees.User").FirstOrDefault(e => e.User.Person.Email == HttpContext.User.Identity.Name);
Solves the problem. I am worried now that I have a lot of data loaded. Company.Employee is 1K+ object for a big company.
Some more testing needed but the research has greatly increased my understanding of EF. Thanks for the help.

Linq to entities DbQuery object is failing hard

I wonder if anyone can shed some light on what may be happening here. I'm using C#, MVC, with entity framework.
So I run these two lines of code:
var booboo = _context.AppItems.Where(ai => ai.id == 101);
var sql = booboo.ToString();
And I get some strange behavior. The booboo.ToString() method hangs, thus failing. Nothing about the booboo DbQuery object works properly in fact.
I'm having similar problems all over the code with my AppItem entity (AppItems is DbSet as you might guess). Entity Framework appears to be unable to construct a query for the AppItem entity.
Edit:
I wasn't patient enough! After leaving it for a very long time, I do get the following exception:
"Message=Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them."
Interestingly that's a Sql.Client exception, which I wasn't expecting.
Here's what the AppItem class looks like:
public class AppItem : Domain.Item
{
public int? UserProfileId { get; set; }
public virtual UserProfile UpdatedByUser { get; set; }
[MaxLength(50)]
public String Type { get; set;}
public DateTime UpdatedDate { get; set;}
// flags
public virtual ICollection<ItemFlag> Flags { get; set; }
// actions
public virtual ICollection<ItemAction> Actions { get; set; }
// notes
public virtual ICollection<Note> Notes { get; set; }
}
Domain Item contains a primary key field (id) and a few other fields.
The Note / ItemAction / ItemFlag Classes there all inherit from AppItem, so perhaps some sort of circular referencing is to blame?
Other items can be queried just fine. For example, I have numerous classes that inherit from AppItem (like ItemFlag, ItemAction and Note) and I can query all of these just fine.
So, where Members is DbSet and Member inherits from AppItem:
var foofoo = _context.Members.Where(ai => ai.id = 101);
var sql = foofoo.ToString();
This Works fine; foofoo.ToString() returns the constructed SQL and everything appears to be in order.
It seems really bizarre to me, there's no error message or anything, the application just hangs when it tries to query AppItems. The table exists in the database, but that doesn't matter because we aren't getting as far as querying the database, we are failing to construct a query in the first place.
Thanks in advance for any help.
I found what the problem was.
I'm using Table-per-Type for inheritance. With AppItem being a base type, the SQL query it generates for querying it is huge (several thousand lines long in this case) and causes problems.
So basically, you need to avoid querying on base types that have more than a few types inheriting from them when using Table-per-Type.
I am assuming that your query is meant to return 1 item.
Add .FirstOrDefault(); onto the end of your query to only return one item (your current query returns an IQueriable of type AppItems)
The Entity Framework does not execute the query until needed, so in your code it will execute the query when the .ToString() method is called.
Try this:
var booboo = _context.AppItems.Where(ai => ai.id == 101).FirstOrDefault();
if (booboo != null)
{
var sql = booboo.ToString();
//etc
}

How to retrieve data from multiple tables and display in a view using viewmodel

I'm trying to develop a messeging system to my mvc application using mvc 5. I have tables called Event, EventUser, EventObject. Each of those tables have following;
Event
ID
CreatedBy
StartTime
IsShared
Budget
EventUser
EventID
UserID
IsAccepted
EventObject
EventID
ObjectID
in my messageController i have the index method which receive the parameter of the user id.i need to display every event that user has invited using this method..
namespace MvcApp.Controllers
{
public class MessageController : Controller
{
private EPlannerDatabaseEntities db = new EPlannerDatabaseEntities();
// GET: /Message/
public ActionResult Index(int UId)
{
/* linq expressions */
return View();
}
}
}
when the parameter has passed in, i want to;
*Select from EventUser table where UID=UserID and join the result with Event and EventObject tables by using EventID attribute.
*Finally by using the final result i need to display every event's infomation that user has invited; like CreatedBy , StartTime, Budget,other users,objects etc..
i'm new to mvc and viewmodel concept.I heard that viewmodel concept can help with these situations.can i overcome this problem by using viewmodel concept.if yes what are the things i need to add in view model?? otherwise what are the other ways to do this?
one way i can see of doing this is creating a custom return object and using EF to join all the tables together. Example
public class MyObject{
public DateTime DateCreated{get;set}
// add remaining properties here
// properties to get back
}
then in code you would use Entity Framework to create a joined data set into a nice list of objects. Example:
var results = (from b in bla join bla2 in (Some Second Query Here)
from SomeSecondQueryHere
where cond1 and cond2 Select new MyObject{
// add properties in here})
where you would replace the bla and bla2,etc with respective table names needed. Then all you need to do is
return View(results);
And the changes will be accessible in the View
If you question is regarding querying with an ORM like Entity Framework, you need to post your entities, not your table schemas. The whole purpose of an ORM is to abstract away the underlying database structure, so while the schema will often be similar to the entity class, it can also be quite different. As a result, I'll have to make assumptions about your entity classes.
To query everything, you just need something like the following:
var events = db.Events.Where(m =>
m.EventUsers.Any(u => u.UserID == UId && u.IsAccepted)
).Include(m => m.EventObjects);
That assumes entity classes along the lines of:
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public int UserID { get; set; }
public bool IsAccepted { get; set; }
}
You end up with an enumerable of Event. If you need to access the EventObjects for an individual event, you have to use the appropriate collection property. For example:
foreach (var item in events)
{
foreach (var obj in item.EventObjects)
{
// do something with `obj` (an invidual `EventObject` instance)
}
}
If you need the actual User object, you're better object querying that first and including related Events and EventObjects:
var user = db.Users.Include("EventUsers.Event.EventObjects").SingleOrDefault(m => m.UserID == UId);
That assumes entities like:
public class User
{
...
public virtual ICollection<EventUser> EventUsers { get; set; }
}
public class EventUser
{
...
public virtual Event Event { get; set; }
}
public class Event
{
...
public virtual ICollection<EventObject> EventObjects { get; set; }
}
With that method, however, there's no way to filter the included Events by whether they're accepted or not. There's a potential way around that, but it requires disabling lazy-loading of EventUsers entirely and complicates querying the information you need. If you need to go that route, see: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter.
Otherwise, you can just exclude non-accepted events before iterating over the collection:
var events = user.EventUsers.Where(m => m.IsAccepted).Select(m => m.Event);
Really you don't need a view model, per se, for any of this. As you can either pass the lists of events (which will include any related EventObjects) or the the single user instance (which includes related events and related EventObjects) directly to your view.
A very high level description of how to solve your scenario using Entity Framework would be something like this:
First you've got to create a series of entity data objects that will represent your tables in the EF data model using EF Code first techniques.
Then you create DbContext objects with DbSets for your previously created entities.
Then you create at least one Service class that will have a property representing DbContext and a set of methods encapsulating Linq queries to your entities.
In the MVC controller you call an instance of Service that you previously create and assign it to a property ant Controller's construction time. Finally, in the Action method you should call the correct Service method and pass any result to the view.
( I am assuming this is a small Ad-Hoc system with a handful of tables , an elaborate System with production quality would require using IoC techniques).

Updating many-to-many relationships with a generic repository

I have a database context with lazy loading disabled. I am using eager loading to load all of my entities. I cannot update many to many relationships.
Here's the repository.
public class GenericRepository<TEntity> : IGenericRepository<TEntity>
where TEntity : class
{
... other code here...
public virtual void Update(TEntity t)
{
Set.Attach(t);
Context.Entry(t).State = EntityState.Modified;
}
...other code here...
}
Here's the User model.
public partial class User
{
public User()
{
this.Locks = new HashSet<Lock>();
this.BusinessModels = new HashSet<BusinessModel>();
}
public int UserId { get; set; }
public string Username { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public string JobTitle { get; set; }
public string RecoveryEmail { get; set; }
public Nullable<double> Zoom { get; set; }
public virtual ICollection<Lock> Locks { get; set; }
public virtual ICollection<BusinessModel> BusinessModels { get; set; }
}
If I modify the business models collection, it does not save the business models collection although I have attached the entire entity.
Worker.UserRepository.Update(user);
I'm not sure what is going on. I don't want to break my generic repository/unit of work pattern just to update many-to-many relationships.
Edit 2: I've got this working...but it is extremely different from the pattern that I'm going for. Having hard implementations means I will need to create a method for each type that has a many to many relationship. I am investigating now to see if I can make this a generic method.
Edit 3: So the previous implementation I had did not work like I thought it would. But now, I have a slightly working implementation. If someone would please help me so I can move on from this, I will love you forever.
public virtual void Update(TEntity updated,
IEnumerable<object> set,
string navigationProperty,
Expression<Func<TEntity, bool>> filter,
Type propertyType)
{
// Find the existing item
var existing = Context.Set<TEntity>().Include(navigationProperty).FirstOrDefault(filter);
// Iterate through every item in the many-to-many relationship
foreach (var o in set)
{
// Attach it if its unattached
if (Context.Entry(o).State == EntityState.Detached)
// Exception "an object with the same key already exists"
// This is due to the include statement up above. That statement
// is necessary in order to edit the entity's navigation
// property.
Context.Set(propertyType).Attach(o);
}
// Set the new value on the navigation property.
Context.Entry(existing).Collection(navigationProperty).CurrentValue = set;
// Set new primitive property values.
Context.Entry(existing).CurrentValues.SetValues(updated);
Context.Entry(existing).State = EntityState.Modified;
}
I then call it like this:
Worker.UserRepository.Update(user, user.BusinessModels, "BusinessModels", i => i.UserId == user.UserId, typeof (BusinessModel));
Extremely messy, but it lets me update many-to-many relationships with generics. My big problem is the exception when I go to attach new values that already exist. They're already loaded because of the include statement.
This works:
This doesn't:
After many painful hours, I have finally found a way to update many-to-many relationships with a completely generic repository. This will allow me to create (and save) many different types of entities without creating boilerplate code for each one.
This method assumes that:
Your entity already exists
Your many to many relationship is stored in a table with a composite key
You are using eager loading to load your relationships into context
You are using a unit-of-work/generic repository pattern to save your entities.
Here's the Update generic method.
public virtual void Update(Expression<Func<TEntity, bool>> filter,
IEnumerable<object> updatedSet, // Updated many-to-many relationships
IEnumerable<object> availableSet, // Lookup collection
string propertyName) // The name of the navigation property
{
// Get the generic type of the set
var type = updatedSet.GetType().GetGenericArguments()[0];
// Get the previous entity from the database based on repository type
var previous = Context
.Set<TEntity>()
.Include(propertyName)
.FirstOrDefault(filter);
/* Create a container that will hold the values of
* the generic many-to-many relationships we are updating.
*/
var values = CreateList(type);
/* For each object in the updated set find the existing
* entity in the database. This is to avoid Entity Framework
* from creating new objects or throwing an
* error because the object is already attached.
*/
foreach (var entry in updatedSet
.Select(obj => (int)obj
.GetType()
.GetProperty("Id")
.GetValue(obj, null))
.Select(value => Context.Set(type).Find(value)))
{
values.Add(entry);
}
/* Get the collection where the previous many to many relationships
* are stored and assign the new ones.
*/
Context.Entry(previous).Collection(propertyName).CurrentValue = values;
}
Here's a helper method I found online which allows me to create generic lists based on whatever type I give it.
public IList CreateList(Type type)
{
var genericList = typeof(List<>).MakeGenericType(type);
return (IList)Activator.CreateInstance(genericList);
}
And from now on, this is what calls to update many-to-many relationships look like:
Worker.UserRepository.Update(u => u.UserId == user.UserId,
user.BusinessModels, // Many-to-many relationship to update
Worker.BusinessModelRepository.Get(), // Full set
"BusinessModels"); // Property name
Of course, in the end you will need to somewhere call:
Context.SaveChanges();
I hope this helps anyone who never truly found how to use many-to-many relationships with generic repositories and unit-of-work classes in Entity Framework.
#dimgl Your solution worked for me. What I've done in addition was to replace the hard-coded type and name of the primaryKey with dynamically retrieved ones:
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
ObjectSet<TEntity> set = objectContext.CreateObjectSet<TEntity>();
IEnumerable<string> keyNames = set.EntitySet.ElementType.KeyMembers.Select(k => k.Name);
var keyName = keyNames.FirstOrDefault();
var keyType = typeof(TEntity).GetProperty(keyName).PropertyType
foreach (var entry in updatedSet
.Select(obj =>
Convert.ChangeType(obj.GetType()
.GetProperty(keyName)
.GetValue(obj, null), keyType))
.Select(value => context.Set<TEntity>().Find(value)))
{
values.Add(entry);
}
Like this your code won't depend on the Entity key's name and type.

Categories

Resources