I've read some tutorials with entity framework 6...
The basics are easy.
using (var context = new MyContext())
{
User u = context.Users.Find(1);
}
But how to use "Where" or something else on the "DbSet" with the users?
public class MyContext : DbContext
{
public MyContext()
: base("name=MyContext")
{
//this.Database.Log = Console.Write;
}
public virtual DbSet<User> Users { get; set; }
}
Users
[Table("User")]
public class User : Base
{
public Guid Id { get; set; }
[StringLength(100)]
public string Username { get; set; }
}
And thats the problem which doesnt work.
string username = "Test";
using (var context = new MyContext())
{
User u = from user in context.Users where user.Username == username select user;
}
Error: There was no implementation of the query pattern for source type 'DbSet'. 'Where' is not found. Maybe a reference or a using directive for 'System.Link' is missing.
If i try to autocomplete the methods there are none.
Why it doesnt works? :(
// Edit:
Adding System.Linq to the top of the file changes the functions of the problem above so that i havent a problem anymore.
But why the where is wrong now?
The type "System.Linq.IQueryable<User>" cant converted into "User" explicit. There already exists an explicit conversion. (Possibly a cast is missing)
Thanks to #Grant Winney and #Joe.
Adding using System.Linq; to the namespace/top of the document where i'm tring the code above fixed the problem.
And using the line above it works for the first item of the list.
User user = (select user from context.Users where user.Username == username select user).First();
The (second) problem is in what you expect:
User u = from user in context.Users where user.Username == username select user;
You're expecting a single element. But a Where clause returns a list (IEnumerable) of items. Even if there's only one entity that fits the where clause, it will still return a list (with a single item in it).
If you want a single item, you need to either take the .First() or the .Single() item of that list.
Some considerations:
Either method I just mentioned can take a clause similar to how the where clause works. This means you can skip the Where and put the clause straight in this method.
Single only works if only one element exists (fitting the clause). If two elements occur, an exception will be thrown.
First works similar to Single, but it will not throw an exception if multiple items exist (fitting the clause). It will simply return the first element it finds (fitting the clause).
Related
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);
}
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
}
I'm trying get all records from a table that have a specific foreign key but I'm struggling to get linq to return anything useful.
Post Model
public class Post
{
//... irrelevant properties
[ForeignKey("Category")]
public int CategoryId;
public virtual Category Category { get; set; }
}
my dbo.Posts table
What I've tried
I've tried several variations of the following:
//id = 7
using (UnitOfWork uwork = new UnitOfWork())
{
var post = uwork.PostRepository.GetAll().Where(c => c.CategoryId == id);
}
This only returns "Non-Public members", which doesn't contain anything useful.
Question
How could I modify my linq query to return all posts that have a specific Foreign Key id?
updates
here's my repository
It seems that you are basically looking at DbQuery<T> object, which is an implementation of IQueryable<T>. Basically LINQ did not make a query yet, because no one asked it for data. So instead it collects all info about the query in an object, to execute it later when needed.
To force it to give you the actual data, simply do ToList, or iterate over posts, or anything:
var post = uwork.PostRepository.GetAll().Where(c => c.CategoryId == id).ToList();
Just make sure to do so before you expose the db context object.
I am using EF 6.1 with EF.Extended and I am trying to execute the following:
if (allRevisions != null && allRevisions.Any(r => r.Item.Id == itemId))
allRevisions.Where(r => r.Item.Id == itemId).Delete();
allRevisions is a DbSet<Revision> from my current DbContext (this code is inside a generic helper method).
When I execute this I get the following exception:
Sequence contains no matching element.
Which is not true as there is a matching revision and the Any is also true.
Furthermore if I execute the following it works fine:
if (allRevisions != null && allRevisions.Any(r => r.Item.Id == itemId))
{
foreach (var revision in allRevisions.Where(r => r.Item.Id == itemId))
allRevisions.Remove(revision);
}
But that is exactly the way you should be able to avoid with EF.Extended.
Am I doing something wrong or is this a bug in EF.Extended?
P.S.: I know that the Any is pointless - I added that to make shure there are revisions to delete after I got the error the first time. There is also no race-condition as on my dev-machine no one else is hitting the DB.
Better to materialize the query then check if it has items and delete those you need to delete all in memory. => but thats exactly what I want to avoid (and what EF.Extened is good for). I actually don't care if something has changed - I would expect it to simply execute a query like DELETE from Revisions WHERE Item_Id = #Id; in the DB.
UPDATE:
I created a small demo-project to reproduce the problem: HERE
It seems to be connected to inheritance. If I try the same thing with the ContentRevision it works, but with MyRevision, which inherits from it, it does not.
I faced the same problem. So I used your example to locate the issue. It seems to be in inheritance. In class MetadataMappingProvider is following code
// Get the entity set that uses this entity type
var entitySet = metadata
.GetItems<EntityContainer>(DataSpace.CSpace)
.Single()
.EntitySets
.Single(s => s.ElementType.Name == entityType.Name);
and the second Single seems to be the trouble, because in EntitySets property are just entity sets for base classes. There is a simple solution to this problem. Always use the base class (from EF point of view) in query.
For example if we have following mapping:
public class Item
{
public long Id { get; set; }
}
public class ItemWithContent : Item
{
public string Content { get; set; }
}
public class TestContext : DbContext
{
public IDbSet<Item> Items { get; set; }
}
this code will throw an error:
using (var context = new TestContext())
{
context.Items.OfType<ItemWithContent>()
.Where(o => string.IsNullOrWhiteSpace(o.Content)).Delete();
}
but this code will work correctly:
using (var context = new TestContext())
{
context.Items
.Where(o => o is ItemWithContent &&
string.IsNullOrWhiteSpace((o as ItemWithContent).Content)).Delete();
}
I was writing a web app and learning entity framework along the way. I am curios if I have done something wrong though. I haven't been using dispose or using statements when querying.
Example of a Repository of mine:
public User GetUserById(int sessionId)
{
var user = (from u in db.Users where u.Id == sessionId select u).SingleOrDefault();
return user;
}
My User table I selected from would have foreign keys which would be associated with other tables. The cool thing, or so I thought, about entity is that I could return the entire User object (built by entity) which then would allow me to do something like this User.MyOtherTable.SomeColumn in my controller.
The problem is I relied on this and went about my merry way in my controller of grabbing the user information as well as utilizing information from the other table. I am now realizing that if I close that connection like the code below:
public User GetUserById(int sessionId)
{ using(db)
{
var user = (from u in db.Users where u.Id == sessionId select u).SingleOrDefault();
return user;
}
}
My controller no long has access to User.MyOtherTable.SomeColumn as this will be null. My true question is how important is it for me use dispose in my entity application?
I would strongly recommend that you use using statements, and also that you stop relying on lazy-loading.
Instead of selecting User objects with lazy-load properties, work out the full data you need and project that to a Model class, e.g.
public class UserWithOtherStuffModel
{
public int Id { get; set; }
public string Name { get; set; }
public string OtherStuff { get; set; }
}
Then in your method:
public UserWithOtherStuffModel GetUserById(int sessionId)
{
using(db)
{
var user = (from u in db.Users
where u.Id == sessionId
select new UserWithOtherStuffModel{
Id = u.Id,
Name = u.Name,
OtherStuff = u.MyOtherTable.SomeColumn
}).SingleOrDefault();
return user;
}
}
Not only does this mean you limit the number of database calls, but you have a data contract that isn't tied to your database model. If you move that column, you simply update the query to point to the new spot/name, the view can remain unchanged.
So when you dispose your context, you aren't going to be able to reference properties that are lazy loaded. You'll need to make your context live longer either by having the context disposed when the controller is disposed (I don't remember if MVC does this per request or not) or by controlling the lifetime of the context using some attributes on the Action.
Some examples on how to resolve this can be found in this question, which is pretty much the same as what you are asking here.