Entity Framework - How to Include parent self referencing - entity-framework

Okay - So I have an object called Employee.
The employee contains many HolidayYears and each of those HolidayYears has a one to many relationship with the Employee.
When I try to do the following line of code:
public Employee ReadById(int id)
{
using (var dbContext = GetContext())
{
return dbContext.Employees
.Include(e => e.HolidayYears.Select(h => h.Employee))
.Include(e => e.Department)
.Include(e => e.HolidayYears.Select(h => h.Months.Select(m => m.AbsencesInMonth)))
.FirstOrDefault(x => x.Id == id);
}
}
The first include holds a reference to the parent - "Employee" - and is a self-reference. The HolidayYears loads just fine, but it doesn't include the HolidayYear's employee.
Is there a way to run this, or is self-referencing like this a no-no in the entity framework?

Related

Why is Automapper.ProjectTo() throwing a null reference exception?

I have a mapping:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Foo, FooDto>()
.ForMember(dest => dest.Id,
opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Name,
opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.PhoneNumber,
opt => opt.MapFrom(src => src.PhoneNumbers.Number)) //etc.
});
and I'm trying to use it in a unit test calling into some mocked up EF objects:
var ids = new List<string>()
{
"123";
"456";
"789";
};
var data = new List<Foo>();
foreach(var id in ids)
{
data.Add(new Foo() { Id = id });
}
this.mockContext = new Mock<entities>();
this.mockSet = new Mock<DbSet<Foo>>();
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Provider).Returns(data.Provider);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.Expression).Returns(data.Expression);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.ElementType).Returns(data.ElementType);
this.mockSet.As<IQueryable<Foo>>().Setup(p => p.GetEnumerator()).Returns(data.GetEnumerator());
When I query the entities directly:
var id = "123";
var bar = this.mockContext.Object.Foo.Where(p => p.id == id);
I get back an IQueryable() with a single result, as expected. But when I try to project my object into a DTO:
var id = "123";
var buzz = this.mockContext.Object.Foo.Where(p => p.id == id).ProjectTo<FooDto>(this.config);
The IQueryable I get back throws a Null Reference Exception if I try to access the results in any way. So for example:
buzz.ToList();
buzz.SingleOrDefault(); // This mirrors the actual call in my code since this is a GetById query.
both fail. What am I missing here?
The problem lies is that Foo uses EF navigation properties to refer to other objects. Specifically a PhoneNumber in this instance. Since the test data is created without the linked PhoneNumber object, it breaks inside of the ProjectTo method. This isn't a problem when grabbing the top level Queryable directly, but Automapper needs the objects to exist (even if they're empty) in order to complete the mapping. Changing that line to:
data.Add(new Foo() { Id = id, PhoneNumber = new PhoneNumber() });
allows the ProjectTo method to complete, albeit with null values.

Entity Framework - How to pass Include off to method caller?

I have a method that calls a DbSet from an Entity Framework database:
public static List<CostEntryVM> ToViewModelList(this DbSet<CostEntry> CostEntrys, Expression<Func<CostEntry, bool>> query) {
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
CostEntrys
.Include(x => x.Job)
.Include(x => x.User)
.Where(query)
.ToList());
}
To use this I can then do for example:
CostEntrys.ToViewModelList(x => x.Active == true);
I want to also be able to call:
CostEntrys.ToViewModelList(x => x.Include(y => y.Job).Include(y.User), x => x.Active == true);
I can't for the life of me figure out how the method signature should look or how I would then apply that to the DbSet.
How can I do this?
First you need to change the extension method to:
public static List<CostEntryVM> ToViewModelList(
this DbSet<CostEntry> CostEntrys,
Expression<Func<CostEntry, bool>> query,
Func<IQueryable<CostEntry>, IQueryable<CostEntry>> func)
{
// Adding the predicate query
IQueryable<CostEntry> queryable = CostEntrys.Where(query);
// Adding include paths
IQueryable<CostEntry> queryableWithFetch = func(queryable);
// Executing the query and map it to the view model object
return AutoMapper.Mapper.Map<List<CostEntry>, List<CostEntryVM>>(
queryableWithFetch.ToList());
}
And then you can call it:
CostEntrys.ToViewModelList(
x => x.Active == true,
x => x.Include(y => y.Job).Include(y.User));

How to access Related Data built By the EF

Similar to the simple Membership UserProfiles to Roles in the Table UserInRoles
I have created a Relationship between UserProfiles to Clients in the table UserInClients with this code
modelBuilder.Entity<UserProfiles>()
.HasMany<dbClient>(r => r.Clients)
.WithMany(u => u.UserProfiles)
.Map(m =>
{
m.ToTable("webpages_UsersInClients");
m.MapLeftKey("ClientId");
m.MapRightKey("UserId");
});
My UserProfiles has an public virtual ICollection<dbClient> Clients { get; set; } and my Clients has an public virtual ICollection<UserProfiles> UserProfiles { get; set; }
If you need to see the Models let me know i can post them
In the model and view i would like to
Display all Clients ( Distinct ) and show how many users have access to that client
Create a View that only shows clients a Currently logged in user is allowed to view.
I thought it was as easy as accessing the Properties of my models Clients.ClientID, i have been trying things like Clients.ClientID.select(u=>u.UserId == clientid) but i knew better and know it does not and will not work.
My other thoughts were creating a model with CliendID and UserID in it ( like the table it created ) so i can use a join in my controller to find the right values??
In the end what i'm trying to accomlish is to populate a KendoUI CascadingDropDownList with this line in my GetCascadeClients JsonResult
return Json(db.Clients.Select(c => new { ClientID = c.ClientID, ClientName = c.Client }), JsonRequestBehavior.AllowGet);
My question is, when I'm in my Controller, how do I access this table built by Entity Framework?
EDIT:
SOLUTION QUERY Pieced together by both answers
return Json(db.Clients
.Include(c => c.UserProfiles)
.Where(c => c.UserProfiles.`Any(up => up.UserName == User.Identity.Name))`
.Select(c => new
{
ClientID = c.ClientID,
ClientName = c.Client,
UserCount = c.UserProfiles.Count()
}),
JsonRequestBehavior.AllowGet);
try something like:
return JSON (db.Clients
.Include(c => c.UserProfiles)
.Where(c => c.UserProfiles.UserId == loggedInUserId)
.Select( c => new {
ClientId = c.ClientID,
ClientName = c.Client,
UserCount = c.UserProfiles.Count()}),
JsonRequestBehavior.AllowGet);
the .Include() extension will make sure that you pull all the UserProfiles along with the Clients, allowing you to use that table for filtering, record counts, etc... that .Where clause might need some work actually, but this should be a solid start.
This is more of a LINQ question really:
db.Clients
.Where(c => c.UserProfiles.Any(up => up.UserId == loggedInUserId))
.Select(c => new {
ClientId = c.ClientID,
ClientName = c.Client + " (" + c.UserProfiles.Count() + ")"
})
Due to the fact that it will convert the above into SQL calls, I had to use string concatenation, as if you try to use a nice String.Format("{0} ({1})", c.Client, c.UserProfiles.Count()) it will complain about being unable to translate that to SQL.
The other options is to do a 2-pass query, materializing the data before doing extra formatting:
db.Clients
.Where(c => c.UserProfiles.Any(up => up.UserId == loggedInUserId))
.Select(c => new {
ClientId = c.ClientID,
ClientName = c.Client,
ProfileCount = c.UserProfiles.Count()
})
// this forces SQL to execute
.ToList()
// now we're working on an in-memory list
.Select(anon => new {
anon.ClientId,
ClientName = String.Format("{0} ({1})", anon.ClientName, anon.ProfileCount)
})

Entity framework, remove entity if it detached from another entity

I have model with relation:
modelBuilder.Entity<Product>()
.HasMany(p => p.Properties)
.WithOptional();
For examle, Product1 have 3 Property, and when I remove one property from product (not from db) I want to delete it in db, because it does not used anywhere anymore.
Can I do this by using EF?
If your navigational properties are not virtual:
using(var db = new YourDbContext()
{
var product = db.Products.FirstOrDefault(x => ...);
product.Properties.RemoveAll(x => ...);
db.SaveChanges();
}
Otherwise:
using(var db = new YourDbContext()
{
var product = db.Products.Include("Properties").FirstOrDefault(x => ...);
product.Properties.RemoveAll(x => ...);
db.SaveChanges();
}

EF 4.1 DBContext - Apply filter loading related entities

I'm using EF 4.1 DBContext API, and I'm trying to load a parent entity named User and then explicitly load related Project entities using a .Where(x => x.IsEnabled) filter:
I found a suggested approach here under "Applying filters when explicitly loading related entities". But I cannot figure out why user.Projects is not being populated. Using SQL Profiler, I have verified I'm querying Projects and returning the data. But it's not being loaded into my user object.
Any thoughts?
Here is my code:
public User GetUser(string userName)
{
try
{
using (var context = new Entities())
{
var user = context.Users.FirstOrDefault(x => string.Compare(x.UserName, userName, true) == 0);
context.Entry(user).Collection(x => x.Projects).Query().Where(x => x.IsEnabled).Load();
//TODO: I expect user.Projects.Count() > 0.
}
}
catch (Exception ex)
{
LogWrapper.DumpException(ex);
}
return null;
}
User.projects = context.projects.select(x => x.user).where(x => x.isenabled);
Came across this problem recently, this is what I did. Hope you find it useful.
var query = context.User.Where(x => x.UserName == userName)
.Include(x => x.Projects.All(p => p.IsEnabled == true))
var data = query.ToList();