Using ASP.NET 4.5 and EF 6, I've put together a multilevel data model that looks like this:
Organization, which has an ICollection of:
_____Workspaces, which has an ICollection of:
__________Projects, which has an ICollection of:
_______________Cards
If I've got a Card Id, how do I find the parents above it? I can't even figure out how to get one level of parent. To find the Project that is the parent of a card with Id of myCardId, I want to write something like:
var project = db.Projects.Where(p => p.Cards.Where(c => c.Id == myCardId));
Any guidance? Good tutorials you can point me to?
Update: Here's my Project model:
public class Project
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<Card> Cards { get; set; }
}
And here's my Card:
public class Card
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public string Notes { get; set; }
}
When I look at the tables in the database, the Card has a Project_Id column, but that happened magically from something Entity Framework did - I believe. Because the Card model has no Parent object on it, I can't programatically ask for things like Card.Project.Workspace.Organization...
Did I set up the whole thing backward?
Hope you have references for each tables.
so you can get the organization by.
var organization = (from o in Organization
from w in Workspaces
from p in Projects
from c in Cards
where c.Id == myCardId && p.projectid == c.projectid && w.workspaceid == p.workspaceid && o.organizationid == w.organizationid select o).FirstOrDefault();
if its different from your code then post your collection structure here, so that anyone can help.
I imagine that find the parents means go up in the Hierarchy so you should be able to write somethin like
'var project = db.Projects.Where(p => p.Cards.Where(c => c.Id == myCardId));'
var t= db.Cards.where(x=> x.id==myCardId).Select(y=> y.Project.Workspace.Organization);
//this should return the organization
Your Model should be like this:
public class Project
{
public int projectId { get; set; }
public int workspaceId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<Card> Cards { get; set; }
}
public class Card
{
public int cardId { get; set; }
public int projectId { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public string Notes { get; set; }
}
Your Card table should have reference to projectid, and project table should have reference to workspaceid, and workspace table should have reference to organisationid.
Then you can use karthikb2win's query to get Organization or any parent tables.
Related
I am using Entity Framework Core 2.0.1 and I have the following models
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Slug { get; set; }
public int Approved { get; set; }
public DateTime ArticleDate { get; set; }
// ... some other fields
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategory
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
//... soem other fields
[ForeignKey("ArticleCategoryParent")]
public int? ArticleCategoryParentID { get; set; }
public virtual ArticleCategory ArticleCategoryParent { get; set; }
public virtual ICollection<ArticleCategory> SubCategories { get; set; }
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategoryRelation
{
[Column(Order = 0)]
public int ArticleId { get; set; }
public Article Article { get; set; }
[Column(Order = 1)]
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory {get; set;}
}
Every article belongs to one or more categories. Categories might have parent category.
I want to get from database last two articles (where Approved = 1) with related category details, for each category that belongs to a parent category which id is given as input.
I have tried but with no success. I can't filter results of an .Include() entity. Is it possible... or I don't know how to do it?
All my data are accessed through entity framework with appContext (the context used to get entities from database). Can I achieve what I want through entity framework core (lambda expression is preferred over Linq if possible), or should I use ADO.NET library (which I know how to execute custom queries).
P.S. I want to get data only to show in the view... no edit is needed.
You don't actually need to include here at all, as far as I can tell. Whenever you use data from a nav property, EF will go get the data from that table, as best it can filter it.
var CategoriesUnderParent = AppContext.ArticleCategories
.Where(c => c.ArticleCategoryParent == {parent});
foreach(var category in CategoriesUnderParent)
{
var ArticlesAllowed = category.ArticleCategoryRelations
.Where(acr => acr.Article.Approved == 1).Select(a => a.Article);
var ArticlesPicked = ArticlesAllowed
.OrderByDescending(ar => ar.ArticleDate)
.Take(2);
// Do something with your data
}
I have three models like followings,
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Application { get; set; }
public ICollection<DocumentResponsible> DocumentResponsibles { get; set; }
public string Pcda { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
public class DocumentResponsible
{
public int Id { get; set; }
public int DocumentId { get; set; }
public int TeamId { get; set; }
}
I want to write an entity framework expression to join three table and select all fields of document table and team names in one row per documents. So basicly I want to join three table use group_concat for team names. Then I want to bind it to a gridview in web form.
What I have tried,
(from dc in DBContext.Document
join dr in DBContext.DocumentResponsible on dc.Id equals dr.DocumentId
join t in DBContext.Team on dr.TeamId equals t.Id
select new
{
Name = dc.Name,
Type = dc.Type,
Application = dc.Application,
Pcda = dc.Pcda,
}).ToList();
and I have just tried it,
var data = DBContext.Dcoument.Include("DocumentResponsibles").Tolist();
It's hard to help without your DbContext and the Entity Mappings, but I'll go out on a limb saying you might just want to mark Document.DocumentResponsibles as virtual.
Also, in DocumentResponsible, maybe you'd want to add a property for Document and one for Team (both marked as virtual too) this way you don't have to do the join keys all the time you want to work with your data, EF would do it for you once properly configured.
If it doesn't work, can you add the following information to your question: First, the context class and the mappings you have. Second, if you do var firstDocument = yoneylemDB.Document.First(), how does firstDocument looks like? Does it have all it's fields and properties filled out? Is there anything weird?
I am using Entity Framework Code First and LINQ to Entities in a Web Api Controller.
I have 3 tables that are related with the following relations/ navigation properties: (the model classes are as follows)
My Reservation class:
[Table("Reservations")]
public class Reservation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ReservationId { get; set; }
public DateTime Date { get; set; }
public int RoomId { get; set; }
public Room Room { get; set; }
public Customer Customer { get; set; }
public int CustomerId { get; set; }
// create association table ReservationMeals
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
}
Association class:
public class ReservationMealItems
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ReservationId { get; set; }
public int MealId { get; set; }
public virtual Reservation Reservation { get; set; }
public virtual Meal Meal { get; set; }
}
Meals class:
[Table("Meals")]
public class Meal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public int MealTypeId { get; set; }
public virtual MealType Type { get; set; }
public virtual ICollection<MenuMealItem> Menus { get; set; }
// create association table ReservationMeal
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
// one-to-one with MealPrice where MealPrice is the dependant
public virtual MealPrice MealPrice { get; set; }
}
And here is my controller to which I am passing a Date and then querying the tables in order to bring back all the meals from all the reservations made on that particular date.
public IHttpActionResult GetReservationMealsForDate(DateDto date)
{
using (var context = new CafeteriaContext())
{
var reservationMeals = from p in context.Reservations
where p.Date == date.Date
join d in context.ReservationMeals
on p.ReservationId equals d.ReservationId
select new SimpleId
{
Id = d.MealId
};
var meals = from p in context.Meals
join d in reservationMeals
on p.Id equals d.Id
select new SimpleMeal
{
Name = p.Name
};
return Ok(meals.ToList());
}
}
The controller works and brings back the list of meals from all reservations made on that date but I am certain that it's not optimal.
I tried not using the JOINs and instead make use of the navigation properties but with no success. I wonder if the relations are set up correctly and if the navigation properties are placed as they should be and if yes, then how can the controller be rewritten to bring back the same result set using navigation properties.
And as a bonus question I would like to know how to write the correct query using LINQ method syntax as I did not succeed in writing it as such.
I am currently learning and any explanations are greatly appreciated. I have read this blog post Coding Abel from where I managed to make it work. But I am still curious to know how correct my solution is and eager to learn better alternatives. Thanks!
You can try use eagerly loading with LINQ and method Include. Your table ReservationMealItems have relationships to Meal and Reservation in this case you need include your entities like :
Lambda
var x = context.ReservationMealItems.Include(res => res.Reservation)
.Where(dt => dt.Reservation.Date == DateTime.Now)
.ThenInclude(meal => meal.Meal)
.Select(d => d.Meal.Name)
.ToList();
Here you find more informations MSDN
Query Syntax
var z = (from resevationMealsItems in context.ReservationMealItems
join reservation in context.Reservation on resevationMealsItems.ReservationId equals
reservation.ReservationId
join meal in context.Meal on resevationMealsItems.MealId equals meal.Id
select meal.Name).ToList();
When I connect LINQPad to my custom assembly using POCO connection, I'm missing some of my entities. This is a project was handed off to me, so I'm not sure what exactly causes this.
This is what I see in LINQPad for my FooContext,
This is my project model,
This is a table that does not show up in LINQPad,
[Table("People")]
public class Person : DomainEntity
{
[Required, StringLength(50)]
public String GivenName { get; set; }
[Required, StringLength(50)]
public String Surname { get; set; }
public virtual ICollection<EmailAddress> EmailAddresses { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
public virtual ICollection<PhysicalAddress> PhysicalAddresses { get; set; }
public virtual ICollection<Login> Logins { get; set; }
public virtual ICollection<CompanyContact> CompanyContacts { get; set; }
}
Here's a table that does,
[Table("Tags")]
public class Tag
{
public int Id { get; set; }
public String Value { get; set; }
public virtual DomainEntity Entity { get; set; }
}
What determines what LINQPad shows for FooContext?
LINQPad uses the following logic to determine which entities to display in the Schema Explorer:
System.Data.Entity.Infrastructure.IObjectContextAdapter adapter = this;
var items = adapter.ObjectContext.MetadataWorkspace.GetItems (DataSpace.CSpace);
var container = (EntityContainer) items.First (i => i.BuiltInTypeKind == BuiltInTypeKind.EntityContainer);
var entities = container.BaseEntitySets.Where (b => b.BuiltInTypeKind == BuiltInTypeKind.EntitySet && b.ElementType != null && b.ElementType is EntityType);
entities.Dump(1);
Does your table show up when you run this code in LINQPad with your custom POCO data context selected?
I have an EF4 Entity Workgroup. Below is the meta-data for that model for reference.
[MetadataType(typeof(WorkgroupMetaData))]
public partial class Workgroup {
public Contact manager { get; set; }
}
[Bind(Exclude = "id")]
public class WorkgroupMetaData
{
[ScaffoldColumn(false)]
public int id { get; set; }
[DisplayName("Org. Number")]
[Required(ErrorMessage = "Org. Number is required.")]
public string org_number { get; set; }
[DisplayName("Workgroup Name")]
[Required(ErrorMessage = "Workgroup name is required.")]
public string name { get; set; }
[DisplayName("Customer Contact")]
public int customer_contact_id { get; set; }
[DisplayName("Manager")]
public int manager_id { get; set; }
[DisplayName("Tech. Lead")]
public int lead_id { get; set; }
[DisplayName("Time Approver")]
public int time_approver { get; set; }
[DisplayName("Description")]
public string description { get; set; }
[ScaffoldColumn(false)]
public object created_at { get; set; }
[ScaffoldColumn(false)]
public object last_modified_at { get; set; }
}
I've got a ViewModel defined as:
public class WorkgroupViewModel
{
public Workgroup Workgroup { get; set; }
public List<Workgroup> Workgroups { get; set; }
}
On the view I have a grid to dump out the workgroups available. This works but I was wondering how to convert the ID fields to the actual strings from another table. Basically the manager, customer_contact, lead are all references to the Contact entity. I would like to show the names from Contacts instead of just the id.
How can this be accomplished? I've looked around a bit but I can't seem to find a suggestion or an answer. Maybe I looking at this from the wrong perspective?
You might consider using a wrapper around Workgroup (decorator pattern) or Tuple or creating a custom class that binds them together.
public class WorkgroupDisplayModel
{
public Workgroup Workgroup { get; set; }
public Manager Manager { get; set; }
// Add additional properties for each related type
}
In your EF query you can do something like:
var query = from w in Context.Workgroups
join m in Context.Managers
on w.manager_id equals m.uid
// Additional joins for each related table
where w.Description == "Project 1" // Whatever criteria
select Tuple.Create(w, m); // Add param for each type
//or
//select new WorkgroupDisplayModel { Workgroup = w, Manager = m, ... };
var list = query.ToList();
var contact = list[0].Item1; // Tuple has strongly typed Item1 thru ItemN
var manager = list[0].Item2;
Then your view model could have:
List<Tuple<Workgroup, Manager, Customer, Lead>> Workgroups { get; set; }
or
List<WorkgroupDisplayModel> Workgroups { get; set; }