I have Menu table in database which have self referencing foreign key i.e. ParentID. Below is my Menu class (DB First Approach)
public partial class Menu
{
public Menu()
{
this.Menu1 = new HashSet<Menu>();
this.Products = new HashSet<Product>();
}
public int MenuID { get; set; }
public string Name { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<Menu> Menu1 { get; set; }
public virtual Menu Menu2 { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
I want to implement following things,
I want entire hierarchy using menu id e.g "if I pass 7 then result should be all the child and sub-child of menu id 7"
If I pass 7 then I want all parent and super parent of menu id 7.
I found several article on StackOverflow before posting this question but they were asking to implement Code First Approach. Here are the questions posted on Stackoverflow before Entity Framework Self Join, Most efficient method of self referencing tree using Entity Framework
I am not sure if you realize this but Menu1 is your parent Menu and Menu2 are your children menus. (I would recommending renaming both Menu1 and Menu2 properties to parent and children).
I believe all of the solutions you have linked have a solution you can use to solve your problem.
Code Sample:
void GetParents(Menu current) {
dbContext.Entry(current).Reference(m => m.Menu2).Load();
while (current.Menu2 != null) {
current = current.Menu2;
dbContext.Entry(current).Reference(m => m.Menu2).Load();
}
}
void GetChildren(Menu current) {
if (current == null) {
return;
} else {
dbContext.Entry(current).Collection(m => m.Menu1).Load();
foreach (var menu in m.Menu1) {
GetChildren(menu);
}
}
}
Something like this should help you get all parents and all children of a Menu instance called current. Note the efficiency is terrible. But that is a different problem. I don't optimize code until my performance tests indicate the bottlenecks in my application.
Fun quote: "Premature optimization is the root of all evil." - Donald Knuth
Related
I'm re-asking this from a question a couple of days ago now I've whittled the problem down.
Two simple objects:
public class Parent
{
public int Id { get; set; }
public virtual Child Child { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}
I find a Parent object using a DbContext method e.g.
Parent parentToUpdate = _context.Parent.Find(1);
This object comes equipped with a populated child already, say with Id 22, generated as a System.Data.Entity.DynamicProxy
I then have a new child object which becomes a null because it wasn't found in the database, using the same DbContext:
Child newChild = _context.Child.Find(999); // returns null
I then try to overwrite the parentToUpdate object's child with the newChild object:
parentToUpdate.Child = newChild;
I expect .Child to become null - This doesn't work unless I step through the code - The parentToUpdate.Child doesn't become null!
WHY? and How can I nullify my parentToUpdate.Child object? before I do _context.SaveChanges()
Ok so thanks to the breadcrumb of lazy loading I ended up circling back on Include which I'd glossed over earlier looking for a solution.
It's as simple as changing the Find in context statement from
Parent parentToUpdate = _context.Parent.Find(1);
To
Parent parentToUpdate = _context.Parent.Include(x => x.Child).Where(x => x.Id == 1);
I am not sure if I am doing this the right way or not, so need advice.
I have an entity, this entity has a child collection, and each child entity has another child collection. Something like this (simplified example)
public MyEntity() {
public long Id { get; set; }
public ICollection<MyChild> Children { get; set; }
}
public MyChild() {
public long Id { get; set; }
public long MyEntityId { get; set; }
public MyEntity MyEntity { get; set; }
public ICollection<MyGrandChild> Children { get; set; }
}
public MyGrandChild() {
public long Id { get; set; }
public long MyChildId { get; set; }
public MyChild MyChild { get; set; }
public string Name { get; set; }
}
Now in our application, the user retrieves this entity from our webApi into an angularJs application. The user then updates the entity (and sub entities) and passes the entity back to the webApi. I am using models to pass the objects from my webApi to the angularJs application, and they look something like this.
public MyEntityModel() {
public long Id { get; set; }
public ICollection<MyChildModel> Children { get; set; }
}
public MyChildModel() {
public long Id { get; set; }
public ICollection<MyGrandChildModel> Children { get; set; }
}
public MyGrandChildModel() {
public long Id { get; set; }
public string Name { get; set; }
}
Once the models are passed back to the webApi, I use Auto Mapper to convert them back to entity objects.
Now the bit I am confused about, i now pass the object to my service layer, my method looks similar to this
public Task<int> UpdateAsync(MyEntity updated) {
_context.Entry(updated).State = EntityState.Modified;
return _context.SaveChangesAsync();
}
If I add a new MyChild or MyGrandChild object to MyEntity after MyEntity exists or update MyChild or MyGrandChild object then the changes are not committed to the database? I changed my UpdateAsync method to this, but is this really needed?
public Task<int> UpdateAsync(MyEntity updated) {
_context.Entry(updated).State = EntityState.Modified;
foreach (var child in updated.Children) {
if (child.Id == 0) {
_context.Entry(child).State = EntityState.Added;
} else {
_context.Entry(child).State = EntityState.Modified;
}
foreach (var grand in child.Children) {
if (grand.Id == 0) {
_context.Entry(grand).State = EntityState.Added;
} else {
_context.Entry(grand).State = EntityState.Modified;
}
}
}
return _context.SaveChangesAsync();
}
Do I really have to loop through each collection, and sub collection, check if the id equals 0 and set its state accordingly?
Yes, you have to do that.
When you do all the work inside a DbContext scope it takes care to track al the changes happening in the entities, and you have to do nothing to let the DbContext know whathas changed.
But, in multi-layered applications, when you move the entities between layers they cannot be kept inside a DbContext scope, so you're responsible for tracking the cahnges.
Julie Lerman recommends implementing an interface to track the status of each entity. This interface has a property that keeps the entity status. This is modified on the client side and visited on the server to set each entity status: get the entities on the server side, attach them to the context, and modify its status according to the tracking interface property. (I can't find the reference, but it's covered in her Programming Entity Framework book, and in one of her Pluralsight courses).
Trackable Entities can also be of your interest.
If you want this to happen "automagically" you can use Breeze. This let you easyly expose an EF model on the client side, using JavaScript code. This code is able to track the changes (and do many other things like validating) in the client side, and send them back to the server to update the database. It's quite easy to get started. Basically you need to install the NuGet package for the server to implement a Breeze controllers, which is done with very little lines of code, and the NuGet package for the client, that implements the JavaScript code. It's advisable to use some MVVM JavaScript library, like Knockout or AngularJS, because the changes will be automatically tracked by subscribing to the observables created by these libraries.
Ok I'm not even sure where to begin on this. I have four main tables.
IPACS_Departments (one to many) -> IPACS_Functions (one to many) -> IPACS_Processes (one to many) -> IPACS_Procedures
I have an IPACS_Documents table with a primary key for docID.
I have 4 look up tables.
IPACS_DepartmentDocs -> IPACS_FunctionDocs -> IPACS_ProcesseDocs -> IPACS_ProcedureDocs
Each of those tables have a FK to the IPACS_Document table docID
They also have a FK to my first four tables mentioned on departmentID, functionID, processID, procedureID.
I need to somehow wire these together though a LINQ statement.
For my department view page. I need to show every single document that is in the current department.
For example we have a computer department. That has 2 functions within that department, that has 13 processes within those functions and 41 procedures within those processes.
So on my department view page I need to show all of the documents for that department and it's functions and it's processes and it's procedures.
On my department view page I have access to the departmentID.
Where I am 100% confused is how do I get all of the associated documents using these 9 different tables?
I hope that made sense because my brain is friend trying to think through this.
I'm not sure if I have your model down right, but I think it follows this pattern (assuming Entity Framework, with the descendant entities having mapping properties to allow the heirarchy to be walked):
public class Department
{
[Key]
public int Id { get; set; }
public virtual ICollection<Function> Functions { get; set; }
public virtual ICollection<DepartmentDocument> DepartmentDocuments { get; set; }
}
public class DepartmentDocument
{
[Key]
public int Id { get; set; }
[ForeignKey("Department")]
public int DeptId { get; set; }
[ForeignKey("Document")]
public int DocId { get; set; }
public virtual Department Department { get; set; }
public virtual Document Document { get; set; }
}
public class Document
{
[Key]
public int Id { get; set; }
public virtual DepartmentDocument DepartmentDocument { get; set; }
public virtual FunctionDocument FunctionDocument { get; set; }
}
Assuming a model like this, then you can write the following - I've only included traversing two levels, but the extras just need some extra lines with SelectMany() for the child elements:
public List<Document> GetDocumentsForDepartment(List<Department> departments)
{
var docs = new List<Document>();
foreach (var department in departments)
{
foreach (var ddoc in department.DepartmentDocuments)
{
docs.Add(ddoc.Document);
}
foreach (var fx in department.Functions)
{
foreach (var fdoc in fx.FunctionDocuments)
{
docs.Add(fdoc.Document);
}
}
}
return docs;
}
Which simplifies to:
public List<Document> GetDocumentsForDepartment2(List<Department> departments)
{
var docs = new List<Document>();
foreach (var department in departments)
{
docs.AddRange(department.DepartmentDocuments.Select(ddoc => ddoc.Document));
docs.AddRange(department.Functions.SelectMany(fx => fx.FunctionDocuments, (fx, fdoc) => fdoc.Document));
}
return docs;
}
This might be OK, the scheme uses more than one DB call (if you are using EF and not Linq to Objects). If that sucks, then maybe you need to put a view in the DB.
I couldn't think how to write this as a single linq query, so maybe this is just a starting point for further work.
This is a pretty simple follow-up to your earlier question though. When you're looking to aggregate child data, you need SelectMany().
I'm having a bit of performance problem with an EF query.
We basically have this:
public class Article
{
public int ID { get; set; }
public virtual List<Visit> Visits { get; set; }
}
public class Visit
{
public int? ArticleID { get; set; }
public DateTime Date { get; set; }
}
Now, I would like to do:
Article a = ...;
vm.Count = a.Visits.Count;
The problem is that, from what I can gather, this first causes the entire list being fetched, and then the count of it. When doing this in a loop this creates a performance problem.
I assumed that it was due to the object being "too concrete", so I've tried to move the Visits.Count call as far back in repository as I can (so that we're sort of working directly with the DbContext). That didn't help.
Any suggestions?
Assuming your data context has a Visits property:
public class MyDbContext: DbContext
{
public IDbSet<Article> Articles { get; set; }
public IDbSet<Visit> Visits { get; set; }
}
you could do that:
using (var ctx = new MyDbContext())
{
var count = ctx.Visits.Where(x => x.ArticleID == 123).Count();
}
Also if the Visits collection is not always required when dealing with an article you could declare it as IEnumerable<T>:
public class Article
{
public int ID { get; set; }
public virtual IEnumerable<Visit> Visits { get; set; }
}
and then rely on the lazy loading.
I think the performance issue might be in the lazy loading. (But need to see more code for that).
Try an include(a => a.Visits) on the moment you retrieve articles from the dbcontext.
for more inforamtion on EF performance: http://www.asp.net/web-forms/tutorials/continuing-with-ef/maximizing-performance-with-the-entity-framework-in-an-asp-net-web-application
In the end I did it another way.
I found that this was hit over and over in different ways, and due to the way the rest of the domain model is set up, I made a bit of a hack:
In my VisitRepository I created a new function GetArticleIDsWithVisit(), which makes a direct sql call via db.SqlQuery, returning a Dictionary. The dictionary is cached and used in all places where visit counts are needed.
Not very pretty, but I have wrapped it inside the repository so I think it's ok.
I've got three classes.
Event > Workshop > Workshop Times
I'm currently looking for best way of inserting records into the Workshop Times, this is running through code first using ICollections.
Looking for something along the lines of this, but I know it doesn't work:
//Create connection
var db = new Context();
var Event = db.Events
.Include("Workshops")
.Include("Workshops.Times")
.Where(ev => ev.GUID == EventGUID).FirstOrDefault();
Event.Workshops.Add(new Workshop
{
Name = tbWorkshopName.Text,
Description = tbWorkshopDescription.Text,
Times.Add(new WorkshopTime{
//Information for times
})
});
db.SaveChanges();
Chopped down classes:
public class Workshops{
public int id { get; set; }
public string name { get; set; }
public ICollection<WorkshopTimes> Times{get;set;}
}
public class Events {
public int id { get; set; }
public string name { get; set; }
public ICollection<Workshops> WorkShops { get; set; }
}
public class WorkshopTimes {
public int id { get; set; }
public DateTime time { get; set; }
}
You are definitely on the right track with your query, however your include statements appear incorrect. From your model I would expect:
var Event = db.Events
.Include("WorkShops")
.Include("WorkShops.events")
.Where(ev => ev.GUID == EventGUID).FirstOrDefault();
Note this uses the property names not the types. This will ensure that the entities in the listed nav properties will be included in the result.
In addition you can use a lambda to do the same thing (but its typesafe)
Check out here for how to do a very similar scenario to yours:
EF Code First - Include(x => x.Properties.Entity) a 1 : Many association
or from rowan miller (from EF team)
http://romiller.com/2010/07/14/ef-ctp4-tips-tricks-include-with-lambda/
And make sure you are using System.Data.Entities for lambda based includes ( Where did the overload of DbQuery.Include() go that takes a lambda? )