creating an object in ternary operator from query result

Viewed 269

I need to create objects to return to front end from data I got from database because the response doesnt contain ALL fields of entity and I also add this [NotMapped] propety AmountOfTenants = t.Apartment.Tenants.Count(), to the response.

If I remove ternary operator here t.ApartmentId != null ? and just create new Apartment every time, then, when a Tenant doesnt have any related Apartmetn to him, then my json response contains Tenant with an Apartment object inside of him, where all values are 0/null, thats why i need ternary, to set Apartment to null if Tenant's ApartmentId is null. Without ternary it works, but i get that apartment with 0/null values and when i add ternary operator i get this error: System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.NewExpression' to type 'System.Linq.Expressions.MethodCallExpression'. Please help

List<Tenant> tenants = await _context.Tenants.Include(tenant => tenant.Apartment.House)
                .Select(t => new Tenant
                {
                    Id = t.Id,
                    FirstName = t.FirstName,
                    LastName = t.LastName,
                    PersonalCode = t.PersonalCode,
                    Birthday = t.Birthday,
                    PhoneNumber = t.PhoneNumber,
                    Email = t.Email,
                    ApartmentId = t.ApartmentId,
                    Apartment = t.ApartmentId != null ? new Apartment
                    {
                        Id = t.Apartment.Id,
                        Number = t.Apartment.Number,
                        Floor = t.Apartment.Floor,
                        AmountOfTenants = t.Apartment.Tenants.Count(),
                        AmountOfRooms = t.Apartment.AmountOfRooms,
                        TotalArea = t.Apartment.TotalArea,
                        AvailableArea = t.Apartment.AvailableArea,
                        HouseId = t.Apartment.HouseId,
                        House = t.Apartment.House
                    } : null
                }).ToListAsync();

----------------------------------------------EDIT someone asked for Tenant entity:

namespace RestApi.Models
{
    public class Tenant
    {
        public long Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PersonalCode { get; set; }
        public DateTime Birthday { get; set; }
        public string PhoneNumber { get; set; }
        public string Email { get; set; }
        public long? ApartmentId { get; set; }
        public Apartment? Apartment { get; set; }
    }
}

--------------------------EDIT someone asked for Apartment class:

namespace RestApi.Models
{
    public class Apartment
    {
        public long Id { get; set; }
        public int Number { get; set; }
        public int Floor { get; set; }
        public int AmountOfRooms { get; set; }
        [NotMapped]
        public int AmountOfTenants { get; set; }
        public int TotalArea{ get; set; }
        public int AvailableArea { get; set; }
        public long? HouseId { get; set; }
        public House House { get; set; }
        public ICollection<Tenant> Tenants { get; set; }
    }
}

------------------------------ EDIT I got a friend to check something similar on his machine:

var tenants = await context.Tenants
  .Include(t => t.Apartment).ThenInclude(a => a.House)
  .Include(t => t.Apartment).ThenInclude(a => a.Tenants)
  .Select(t => new Tenant
  {
    Id = t.Id,
    //etc...
    Apartment = t.ApartmentId != null ? new Apartment
    {
      Id = t.Apartment.Id,
      NumberOfTenants = t.Apartment.Tenants.Count(),
      //etc...
    } : null
  }).ToListAsync();

on his machine this new statement inside another new statement works and gives no error

2 Answers

You could try using this, which should eliminate the need to use the NotMapped annotation and you wouldn't need to use the select at that point either.

    public class Apartment
    {
        public long Id { get; set; }
        public int Number { get; set; }
        public int Floor { get; set; }
        public int AmountOfRooms { get; set; }
        public int AmountOfTenants { get { return this.Tenants != null ? this.Tenants.Count : 0; } }
        public int TotalArea { get; set; }
        public int AvailableArea { get; set; }
        public long? HouseId { get; set; }
        public House House { get; set; }
        public ICollection<Tenant> Tenants { get; set; }
    }

New Query

List<Tenant> tenants = await _context.Tenants.Include(tenant => tenant.Apartment).ThenInclude(a => a.House).ToListAsync();

Edit

As for an explanation of the error you are getting. It is basically saying that it can't make a new expression inside of a new expression (i.e. creating a new Apartment inside of a new Tenant)

I'm making an assumption about your Apartments class so correct me if I go astray here. Assumption : A tenant can only have one apartment and an apartment can have multiple tenants.

With that being said you tenant class just needs to reference an Apartment object. The Apartment ID is contained in Apartment object so you're really storing that data twice. If you're looking to add an new tenant to an apartment you don't need the select statement. You might want to do a bullet point summary to clarify your intentions if there is more to this. If you're looking to include all objects within the Tenant class(data from other tables) you can consider lazy loading if your application permits.

public class Tenant
{
    [Key]
    public long Id { get; set; } //Primary Key
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PersonalCode { get; set; }
    public DateTime Birthday { get; set; }
    public string PhoneNumber { get; set; }
    public string Email { get; set; }
    //public long? ApartmentId { get; set; }
    public virtual Apartment Apartment { get; set; }
}

public class Apartment
{
    [Key]
    public Guid ApartmentID { get; set; } //Primary Key
    public virtual List<Tenant> Tenants { get; set; }
}
 public class appStuff
{
    void AddStuff()
    {
        var myContext = new _context();
        Apartment apartment = myContext.Find(Predicate4YourApartment());
        var ten = new Tenant
        {
            Birthday = DateAndTime.Now,
            Apartment = apartment,
            FirstName = "John" //add the rest.
        };

        myContext.TenantsTable.Add(ten);

        //This will add the Tenant to the Tenants table and the apartment to the Apartments table if you create a new one 
        //instead of finding an existing one like shown above
        myContext.SaveChanges(); 

    }
 void GetStuff()
    {
        var myContext = new _context();
        var myTenant = myContext.TenantsTable.Where(x => x.Apartment.ApartmentID == "DesiredID");
    }
}

public class _context : DbContext
{
    public _context() : base()
    {

    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseLazyLoadingProxies();
        base.OnConfiguring(optionsBuilder);

    }
}
Related