I have two EF contexts _inventoryContext and _auctionContext.
_inventoryContext has a property called Items and _auctionContext has one called Auctions. Items is a collection of Item objects which each contain a Guid to identify them uniquely. The Auctions property is a collection of Auction objects which each contain a Guid InventoryReference that refers to one of the elements of Items.
What I want to do is get a list of all inventory items that are not part of an auction. How do I do this?
This may be of help to you.
Alternatively, you can do this in 2 steps: First get a collection of GuidReferences from your Auction, and then fetch the Items whose Guid's are included in the collection. There will be a performance hit because of the extra query, and because the framework will need to allocate the Guid collection. But depending on the Item collection size, that may not be a big deal for you.
Another possibility would be to create a view in one database/context that pulls the data from the other. This would be read-only, however.
There is a better solution in EF Core
You can create view as named Auctions one of your context and map the DbSet model in your code. So you can use other context model and table in another context. But you must ensure your db user can access those two contexts.
For example in _inventoryContext you can define like that.
public virtual DbSet<Auction> Auctions { get; set; }
modelBuilder.Entity<Auction>(entity =>
{
entity.ToView("vwAuctions");
}
It's provides you something like that
var result= from x in _inventoryContext.InventoryReference
join y in _inventoryContext.Auctions on x.Id equals y.InvRef
select x;
Related
Is it possible in EF to have a virtual collection that has a custom query to the data it pulls?
For instance, if I have a class person.cs and I have a property in that class: public ICollection<job> jobs, but I want that property to be defined by a custom query that I write, for instance, _context.jobs.where(j => j.backup_person_id == id).select(j); rather than defaulting to look for just person_id on the jobs table.
Is that possible? If so, how is it achieved?
[Edit]
More specifically: let's say the jobs table has the two columns manager and main_lead. I then want my person.cs class to have the properties: ICollection<job> managerJobs which contains all of the <job> entries where the manager field matches the person_code field of that person and ICollection<job> main_leadJobs which contains all of the <job> entries where the main_lead field matches the person_code field of that person,
In my head, it seems pretty simple: ICollection<job> managerJobs = select * from tbl_jobs where manager = person_code and ICollection<job> main_leadJobs = select * from tbl_jobs where main_lead = person_code
I hope that makes more sense than what I had above
No. It is not possible. However, what you want to achieve, more generally, is possible, just in a different way. See the MSDN article: https://msdn.microsoft.com/en-us/data/jj574232.aspx#explicitFilter
In a situation where you a dealing with a specific person, rather than including jobs generally, you can do:
var person = _context.People.Find(personId);
_context.Entry(person)
.Collection(b => b.jobs)
.Query()
.Where(j => j.backup_person_id == backupId)
.Load();
Then, when you accessed the person.jobs collection, it would only be those filtered items. This means you've got two queries, though, whereas if you just eargerly loaded all jobs via Include, it would be done in a single query.
If you have multiple people you're working with though, this explicit load would have to be done multiple times, meaning a query for each person (N+1). In that situation, you're better off querying each entity individually:
var people = db.People.Where(...).ToList();
var jobs = db.Jobs.Where(m => people.Select(m => m.Id).Contains(m.person_id) && m.backup_person_id == backupId).ToList();
In other words, you're querying just jobs belonging to the specific selected people and then further limiting that by your criteria, which in this case is the id of the backup person. Then, as you're iterating through the people, you can filter jobs in memory to just the jobs that belong to the current person in context.
Let's say I have two simple entities:
class Cat
{
int Id;
string Name;
ICollection<CatRelation> ToRelations;
ICollection<CatRelation> FromRelations;
}
class CatRelation
{
int FromCatId;
int ToCatId;
Cat FromCat;
Cat ToCat;
string RelationType;
}
What I would like to do is load all the Cats and their relations, and have the navigation properties work throughout the whole graph. So far I have something like this:
context.Cats.Include(cat => cat.ToRelations)
.Include(cat => cat.FromRelations)
.ToList()
After this the context is disposed of. Further down the line the list is iterated through. This works fine for getting to the relations -entities, but if I, for example, iterate over the Cats and then try to iterate over all their relations, the other end of the CatRelation is there, but its navigation properties won't work (ContextDisposed). As in, given the following cat var cat1 = cats.First().ToRelations.First().ToCat, if I try to access cat1.ToRelations, I get a ContextDisposed -exception.
So is there a way for me to ask the context to fix all these navigation properties (because I know I have loaded all the Cats of all the CatRelations), before disposing of the context?
For a graph I think it would be better to load the entire table, then construct the graph yourself. Even if you could get EF to recursively pull all of the data from the database, it wouldn't reuse the existing objects for relations (if they exist in memory) but rather construct new instances with the same data. That's likely not what you want and it would result in a lot more data being transferred to boot.
In any event I don't think it's possible to get EF to pull data that is nested arbitrarily deep or might have cycles in their relationship graph.
I am using ASP.NET MVC4. I have absolutely no idea where to even start with this - I need to display a hierarchical grid complete with CRUD operations at each level (edit row, create row, delete row). I am using a database-first approach with the Entity Framework (.edmx). The relationships between entities are maintained by a "ParentId" value in the same row in my database. I want to have the "Delete" functionality cascade down to all children, just like in the relationships setting in SQL Server.
QUESTION: Where would I start to begin looking if I want to have a hierarchical grid complete with CRUD operations using the idea of a ParentId to maintain hierarchical relationships? I've looked into ListViews and I don't think that's what I need. I don't want to over complicate this, unless absolutely necessary, and I prefer to not go with a vended solution.
I ended up using MvcTreeView (http://mvctreeview.codeplex.com/). This is also available in Nuget. This Html Helper is designed to work specifically with an adjacency list (a hierarchical table that maintains its hierarchy by a ParentId column, or similar). This helper generates a hierarchical tree view of each record in your table. Additionally, you can further customize is by adding individual CRUD operations on each item, and the CRUD will work just like it normally would in a normal ASP.NET MVC w/Entity Framework project.
To cascade delete all the way down the tree of "n" depth, here's how to do it:
I ended up creating a custom module that works specifically with adjacency lists. An adjacency list in SQL is where you have a single table maintaining an "n" depth hierarchy by a column ParentId that is a foreign key back to the Id column (the primary key) in the same table.
I created a helper class called "Children". The purpose of this class is to return a list of all traversed child Id's for a given parent. So if you pass in the Id for a parent that's say 6 levels up, the below code will traverse down all 6 levels and return the complete list of Id's for all children, grandchildren, great-grandchildren, and so on.
The reason why I am getting a list of all Id's of children instead of a list of objects to delete is because if I get a list of objects to delete I would have to get these objects under a different DbContext, so when I try to actually delete them, I'll get an error saying that the object is detached (or something like that) because it was instantiated under a different context. Getting a list of Id's prevents this from happening and we can use the same context to perform the delete.
So, call this method GetChildrenIds(List<int> immediateChildrenIds) from your code and pass in a list of ints that correspond to the immediate children of a selected node. For example, to get the list of immediate children to pass into this method, use something like the following (this is based on an ASP.NET MVC pattern using WebAPI):
// keep in mind that `id` is the `id` of the clicked on node.
// DELETE api/region/5
public HttpResponseMessage Delete(int id)
{
Region region = db.Regions.Find(id);
List<int> tempRegionIds = new List<int>();
List<int> immediateChildrenIds = (from i in db.Regions where i.ParentId == id select i.Id).ToList();
List<int> regionsToDeleteIds = new List<int>();
// the below is needed because we need to add the Id of the clicked on node to make sure it gets deleted as well
regionsToDeleteIds.Add(region.Id);
// we can't make this a static method because that would require static member
// variables, and static member variables persist throughout each recursion
HelperClasses.HandleChildren.Children GetChildren = new HelperClasses.HandleChildren.Children();
// see below this code block for the GetChildrenIds(...) method
tempRegionIds = GetChildren.GetChildrenIds(immediateChildrenIds);
// here, we're just adding to regionsToDeleteIds the children of the traversed parent
foreach (int tempRegionId in tempRegionIds)
{
regionsToDeleteIds.Add(tempRegionId);
}
// reverse the order so it goes youngest to oldest (child to grandparent)
// is it necessary? I don't know honestly. I just wanted to make sure that
// the lowest level child got deleted first (the one that didn't have any children)
regionsToDeleteIds.Reverse(0, regionsToDeleteIds.Count);
if (regionsToDeleteIds == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
foreach (int regionId in regionsToDeleteIds)
{
// here we're finding the object based on the passed in Id and deleting it
Region deleteRegion = db.Regions.Find(regionId);
db.Regions.Remove(deleteRegion);
}
...
The below code is the class that returns a complete list of children Id's. I put this code in a separate helper class file. The DbContext _db is what I was talking about when I said you don't want to retrieve a list of objects under this context otherwise the Delete wouldn't work when it was actually called in your controller. So instead, as you can see, I get a list of Id's instead and make the actual DbContext call to get the object inside my controller, not this helper class.
public class Children
{
private Entities _db = new Entities(HelperClasses.DBHelper.GetConnectionString());
private List<int> _childrenIds = new List<int>();
private List<int> _childRegionIds = new List<int>();
public List<int> GetChildrenIds(List<int> immediateChildrenIds)
{
// traverse the immediate children
foreach (var i in immediateChildrenIds)
{
_childRegionIds.Add(i);
_childrenIds = (from child in _db.Regions where child.ParentId == i select child.Id).ToList();
if (_childrenIds.Any())
GetChildrenIds(_childrenIds);
else
continue;
}
return _childRegionIds;
}
}
https://datatables.net/ might be a good start. Check for Editor plugin.
I have two IEnumerable collections that I would like to union.
One selects news objects that are associated with a particular category. When a user is filtering by a category I would also like news articles that have been tagged with another category to be displayed.
So I have another query that returns news objects that are tagged with the particular sub category.
Now I would like to union the two collections, removing duplicates (as a news article associated to the main category, may also be tagged with the second category).
var catNews = model.Category.News.SelectMany(n => n.News); //get news article associated to the category
var tagNews = _nr.GetNews(model.Category.relatedCategoryName); //this selects news by tags - which I want as the related category name
model.News = catNews.Union(tagNews).OrderByDescending(p => p.Date); //union the two collections
However, model.News now contains two identical news articles and I am not sure why as union should use the default equality comparer?
Am I doing something wrong here? I am using EF Code First and my primary key is the news id.
The way I have got round this issue is by passing in a list of the catNews id to the GetNews function and excluding them
if (excludeIds != null)
q = q.Where(n => !excludeIds.Contains(n.ID));
But I am not sure why I have to this when I thought union would remove the identical articles?
I guess that you are not loading these two collections from the same instance of the entity framework context. Default equality comparer will compare references and if you use the same context it will indeed return same News instance in both collections when Id is matching but if you use different contexts each collection will contain its own News instances and Union will do the same as Concat. In such case you must override Equals (and GetHaschCode) in your News entity to compare Id or use custom comparer.
I am building a new project from scratch.
I created a db where I have consistently applied a db structure that I explain with a short self-explanatory example:
Table Item -> (Id, Name) -> Contains general information
Table ItemInfo -> (Item_Id, Language, Description) -> Contains the language dependent information.
Id and Item_Id are connected with a foreign key relationship.
My idea was to model it in a way that I would end up using only a single POCO object "Item" populated through Entity Framework. This object would contain only the public properties: Id, Name and Description.
The language will be hidden to the code using this object, the object itself should have the responsibility to give the correct description depending on a global variable that contains the language.
I have tried a few ways to do this and always ended up having problems because Entity Framework wouldn't allow this scenario. I always had to retrieve info for ALL languages and not only the current one or use 2 different queries.
So at the end the solution I started to use was to let a T4 template create both Item and ItemInfo and then I manually added a code similar to this:
public partial class Item
{
private ItemInfo _itemInfo = null;
private ItemInfo itemInfo
{
get
{
if (_itemInfo == null) _itemInfo = ItemInfoes.Single(p => p.Language == GlobalContext.Language);
return _itemInfo;
}
}
public Description
{
get { return itemInfo.Description; }
set { itemInfo.Description = value;}
}
}
With this code I added the additional properties from ItemInfo to Item and selected the correct language as per my requirements.
Do you think this is a good solution? How would you solve this problem instead?
However, running sql profiler I can see that 2 different sql queries are used to populate the Item object, one that queries the Item table and another that queries the ItemInfo.
Can the same scenario be achieved with a single query that does a join between the 2 tables? (I am afraid of the long term performance hit and also this is how I would do it without an ORM).
Any suggestion will be welcome, I have many years of programming experience but I am a newbie with Entity Framework and ORMs in general.
Please help.
You're not showing how you fetch the Item objects, but generally I don't see a problem with fetching everything in one query. You've got several options.
You can do a projection (but not onto a mapped entity - in this example I project onto an anonymous object):
context.
Items.
Select(item => new
{
Id = item.Id,
Name = item.Name,
Description = item.
ItemInfo.
Where(info => info.Language == YourGlobalLang).
Select(info => info.Description).
FirstOrDefault()
};
(This has been edited to use FirstOrDefault instead of Single - see comment discussion with #Craig Stuntz)
This will return a list of all Items - you can add a Where clause to filter.
Or you can fetch it the other way around (starting with ItemInfo):
ItemInfo itemInfo = context.
ItemInfoes.
Include(info => info.Item).
SingleOrDefault(info => info.Language == YourGlobalLang &&
info.Item.Id == itemIdToFetch);
After that you can access the item object itself:
Item item = itemInfo.Item;
I would say it's a reasonable approach. Also, I wouldn't worry about performance issues with two simple selects. If it turns out to be a problem in the future, you might change it to a view, for instance.
You may try to add the where clause dynamically.
Or as it was said use linq to sql directly.
How to append a where clause to an Entity Framework ObjectSet
Add the where clause dynamically in Entity Framework