The expression 'y.Cases' is invalid inside an 'Include' operation - c#

I have one-to-many relationship database. DbSet Companies being "One" and DbSet Cases being "many". Here's my context and model classes:
Database Context
class CaseContext : DbContext
{
public DbSet<ParticipantCompany> Companies{ get; set; }
public DbSet<ParticipantPerson> Persons { get; set; }
public DbSet<LegalCase> Cases { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=Clients.db");
}
ParticipantCompany class inherits from Participant Class.
public class ParticipantCompany : Participant
{
public ParticipantCompany():this(false, "","","","") { }
public ParticipantCompany (bool isclient) : this(isclient, "","","","") { }
public ParticipantCompany(bool isclient, string name, string address, string inncompany, string ogrn) : base(isclient, SubjectType.Company)
{
Name = name;
Address = address;
InnCompany = inncompany;
Ogrn = ogrn;
}
public string InnCompany { get; set; }
public string Ogrn { get; set; }
}
public abstract class Participant
{
public Participant(bool isclient, SubjectType Type, string name, string address)
{
SubjType = Type;
IsClient = isclient;
Name = name;
Address = address;
}
public Participant(bool isclient, SubjectType Type ) : this(isclient, Type, "","")
{
}
public int Id { get; set; }
public SubjectType SubjType { get; private set; }
public bool IsClient { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public List<LegalCase> Cases = new List<LegalCase>();
}
LegalCase class represents "Many" in relationships
public class LegalCase
{
public LegalCase() : this("", CaseType.ArbGeneral){}
public LegalCase(string casefabula, CaseType casetype)
{
CaseFabula = casefabula;
CaseType = casetype;
}
public int Id { get; set; }
public string CaseFabula { get; set; }
public CaseType CaseType { get; set; }
//public ICaseParticipant Client { get; set; }
public int? CompanyId { get; set; }
public ParticipantCompany Company { get; set; }
public int? PersonId { get; set; }
public ParticipantPerson Person { get; set; }
}
Now here's the Query:
using(var db = new CaseContext())
{
var QClients = db.Companies
.Where(x => x.IsClient == true)
//Exception: The expression 'y.Cases' is
// invalid inside an 'Include' operation,
// since it does not represent a property
// access: 't => t.MyProperty'. etc
.Include(y => y.Cases)
.ToList();
}
I tried to explicitly cast y to ParticipantCompany since it's what the prompt of the exection seems to suggest:
To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty')
But it generates the same exception:
using(var db = new CaseContext())
{
var QClients = db.Companies
.Where(x => x.IsClient == true)
//Same exception
.Include(y => (y as ParticipantCompany).Cases)
.ToList();
}

Change Cases from being field to property as the exception suggests:
public List<LegalCase> Cases { get; set; } = new List<LegalCase>();
From the docs:
Each entity type in your model has a set of properties, which EF Core will read and write from the database. If you're using a relational database, entity properties map to table columns.
By convention, all public properties with a getter and a setter will be included in the model.

Related

.NET C# I want to get the IDs (1/multiple) from a data if it much the ID of another table

Models:
namespace practise_API.Model
{
public class SourceAttributes
{
[Key]
public int SourceEntityId { get; set; }
public int ATiD { get; set; }
public string Name { get; set; }
public string Datatype { get; set; }
}
public class OtherData
{
public int ID { get; set; }
public string Ename { get; set; }
public string Type { get; set; }
public string Source { get; set; }
public string Frequency { get; set; }
}
}
IRepository interface:
namespace API.Repository
{
public interface ISourceRepository
{
IEnumerable<SourceEntities> GetSourceEntities();
IEnumerable<SourceAttributes> GetSourceAttributes(int id);
........
void Save();
}
}
Repository implementation:
public class SourceRepository : ISourceRepository
{
private readonly MapperDbContext _dbContext;
public SourceRepository(MapperDbContext dbContext)
{
_dbContext = dbContext;
}
public IEnumerable<SourceAttributes> GetSourceAttributes()
{
return _dbContext.SourceAttributes
.Where(i => i.SourceEntityId == OtherData.ID)
.ToList();
}
}
I want to get all the SourceAttributes.SourceentityIds that match that one ID from OtherData. The way I thought about it was to use where() System.Linq but I can't get the OtherData.ID.
Also this is going to be called later in my Get() of my controller.
The ISourceRepository.GetSourceAttributes has an id parameter, but the implementation is missing it resulting surely in a compiler error.
Modify it:
public IEnumerable<SourceAttributes> GetSourceAttributes(int otherDataId)
{
return _dbContext.SourceAttributes
.Where(i => i.SourceEntityId == otherDataId)
.ToList();
}
To get the ids you want:
var ids = sourceRepository
.GetSourceAttributes(myOtherDataInstance.ID)
.Select(i => i.ID)
.ToArray(); // or leave it as IEnumerable as you wish

AutoMapper - map to derived objects depend on condition

I want to map source class to derived (from abstract) destination classes depend on value of some property.
I have the following source classes:
public partial class ApplicationDriver
{
public virtual ICollection<ApplicationDriverEquipment> Equipments { get; set; }
}
public partial class ApplicationDriverEquipment
{
public int Id { get; set; }
[StringLength(256)]
public string Make { get; set; }
[StringLength(256)]
public string Model { get; set; }
[StringLength(256)]
public string Year { get; set; }
[StringLength(256)]
public string VINNumber { get; set; }
[StringLength(256)]
public string PlateNumber { get; set; }
[StringLength(256)]
public string CurrentMileage { get; set; }
[StringLength(256)]
public string Length { get; set; }
public string Type { get; set; }
public int DriverId { get; set; }
public virtual ApplicationDriver Driver { get; set; }
}
I want to map to the following classes, depend on Type parameter:
public class ApplicationDriverDomain
{
public List<ApplicationDriverEquipmentAbstractDomain> Equipments { get; set; }
}
public abstract class ApplicationDriverEquipmentAbstractDomain
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Year { get; set; }
public string PlateNumber { get; set; }
public string CurrentMileage { get; set; }
public string Type { get; protected set; }
}
public class ApplicationDriverEquipmentTractorDomain : ApplicationDriverEquipmentAbstractDomain
{
public ApplicationDriverEquipmentTractorDomain()
{
Type = ApplicationDriverEquipmentTypeStaticStringsDomain.Tractor;
}
public string VINNumber { get; set; }
}
public class ApplicationDriverEquipmentTrailerDomain : ApplicationDriverEquipmentAbstractDomain
{
public ApplicationDriverEquipmentTrailerDomain()
{
Type = ApplicationDriverEquipmentTypeStaticStringsDomain.Trailer;
}
public string Length { get; set; }
}
public class ApplicationDriverEquipmentStraightTruckDomain : ApplicationDriverEquipmentAbstractDomain
{
public ApplicationDriverEquipmentStraightTruckDomain()
{
Type = ApplicationDriverEquipmentTypeStaticStringsDomain.StraightTruck;
}
public string VINNumber { get; set; }
public string Length { get; set; }
}
public class ApplicationDriverEquipmentCargoVanDomain : ApplicationDriverEquipmentAbstractDomain
{
public ApplicationDriverEquipmentCargoVanDomain()
{
Type = ApplicationDriverEquipmentTypeStaticStringsDomain.CargoVan;
}
public string VINNumber { get; set; }
public string Length { get; set; }
}
I try to do it:
ApplicationDriverEquipmentAbstractDomain GetEquipment(Infrastructure.Asset.ApplicationDriverEquipment infrastructure)
{
ApplicationDriverEquipmentAbstractDomain result = null;
var config = new MapperConfiguration(cfg => cfg.AddProfile<AutoMapperApplicationModel>());
var mapper = config.CreateMapper();
switch (infrastructure.Type)
{
case ApplicationDriverEquipmentTypeStaticStringsDomain.Tractor:
result = mapper.Map<ApplicationDriverEquipmentTractorDomain>(infrastructure);
break;
case ApplicationDriverEquipmentTypeStaticStringsDomain.Trailer:
result = mapper.Map<ApplicationDriverEquipmentTrailerDomain>(infrastructure);
break;
case ApplicationDriverEquipmentTypeStaticStringsDomain.StraightTruck:
result = mapper.Map<ApplicationDriverEquipmentStraightTruckDomain>(infrastructure);
break;
case ApplicationDriverEquipmentTypeStaticStringsDomain.CargoVan:
result = mapper.Map<ApplicationDriverEquipmentCargoVanDomain>(infrastructure);
break;
}
return result;
}
CreateMap<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentTractorDomain>();
CreateMap<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentTrailerDomain>();
CreateMap<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentStraightTruckDomain>();
CreateMap<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentCargoVanDomain>();
CreateMap<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentAbstractDomain>()
.Include<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentTractorDomain>()
.Include<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentTrailerDomain>()
.Include<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentStraightTruckDomain>()
.Include<Infrastructure.Asset.ApplicationDriverEquipment, ApplicationDriverEquipmentCargoVanDomain>()
.ForMember(dest => dest.Type, opt => opt.ResolveUsing(GetEquipment))
;
CreateMap<Infrastructure.Asset.ApplicationDriver, ApplicationDriverDomain>()
.ForMember(dest => dest.Equipments, opt => opt.MapFrom(src => src.Equipments));
but I got an error:
"Error mapping types.\r\n\r\nMapping types:\r\nApplicationDriver ->
ApplicationDriverDomain\r\nInfrastructure.Asset.ApplicationDriver ->
Domain.POCO.Application.ApplicationDriverDomain\r\n\r\nType Map
configuration:\r\nApplicationDriver ->
ApplicationDriverDomain\r\nInfrastructure.Asset.ApplicationDriver ->
Domain.POCO.Application.ApplicationDriverDomain\r\n\r\nProperty:\r\nEquipments"
Updated:
So I believe I understand what you are trying to do, and apologies I may have slightly led you down the incorrect route. You flow is basically to distinguish what infrastructure type the source object is and then create that type of object. Also you need to understand the two different Mapper set up ways.
In the first part of your code you are trying to set it up with an instance of the Mapper but then using my Static style of using the Mapper.Map I would recommend always using the static style so that you have the ability to do some more dynamic ways of pulling mapping profiles in.
Mapper.Initialize(cfg => cfg.AddProfile<AutomapperRules>());
var domain = Mapper.Map<Domain.ApplicationDriverEquipmentTractorDomain>(inf);
Next you only need to reference that a mapping type from the underlying source to the domain types in your profile i.e.
CreateMap<ApplicationDriverEquipmentInfrastructure, ApplicationDriverEquipmentTractorDomain>();
CreateMap<ApplicationDriverEquipmentInfrastructure, ApplicationDriverEquipmentTrailerDomain>();
CreateMap<ApplicationDriverEquipmentInfrastructure, ApplicationDriverEquipmentStraightTruckDomain>();
CreateMap<ApplicationDriverEquipmentInfrastructure, ApplicationDriverEquipmentCargoVanDomain>();
Then what you need to do is to call your GetEquipment method from the mapping that describes the ApplicationDriver i.e.
CreateMap<ApplicationDriver, ApplicationDriverDomain>()
.ForMember(dest => dest.Equipments, opt => opt.ResolveUsing(x => x.Equipments.Select(GetEquipment)));
private ApplicationDriverEquipmentAbstractDomain GetEquipment(ApplicationDriverEquipmentInfrastructure infrastructure)
{
switch (infrastructure.Type)
{
case "Tractor":
return Mapper.Map<ApplicationDriverEquipmentTractorDomain>(infrastructure);
case "Trailer":
return Mapper.Map<ApplicationDriverEquipmentTrailerDomain>(infrastructure);
case "StraightTruck":
return Mapper.Map<ApplicationDriverEquipmentStraightTruckDomain>(infrastructure);
case "CargoVan":
return Mapper.Map<ApplicationDriverEquipmentCargoVanDomain>(infrastructure);
}
return null;
}
Example Usage:
Mapper.Initialize(cfg => cfg.AddProfile<AutomapperRules>());
var inf = new ApplicationDriverEquipmentInfrastructure()
{
CurrentMileage = "mil",
Length = "123",
Make = "ccc",
Model = "15",
Type = "Tractor",
VINNumber = "vin"
};
var driver = new ApplicationDriver()
{
Equipments = new List<ApplicationDriverEquipmentInfrastructure>() {inf}
};
var domain = Mapper.Map<ApplicationDriverDomain>(driver);
Inheritance in AM works by checking the type of the source, not by using a discriminator. That's what you were supposed to understand from the docs. One way to solve your problem is to pass an existing destination to Map. Created by smth like the GetEquipment method you have there. ApplyBaseMapping is a hack, you use Include/IncludeBase to reuse configuration. Unfortunately you've also hit a bug already fixed in the MyGet build, so the real error was kind of hidden from you. The only way to debug this in your version is by checking the execution plan.

Add items to an entity collection via ChangeTracker

I chose to put a complete example of my code in order to demonstrate what I exactly need. Briefly, what I would like to do, is to get a generic code in public override int SaveChanges() that could work to all entities that implements a translation rather than write a one-by-one.
ENTITIES
public partial class EntityOne
{
public long EntityOneId { get; set; }
public string SomeProperty { get; set; }
public virtual ICollection<EntityOneTranslation> EntityOneTranslations { get; set; }
public EntityOne()
{
this.EntityOneTranslations = new HashSet<EntityOneTranslation>();
}
}
public class EntityOneTranslation : EntityTranslation<long, EntityOne>
{
public string LocalizedEntityOneProp1 { get; set; }
public string LocalizedEntityOneProp1 { get; set; }
}
public partial class EntityTwo
{
public long EntityTwoId { get; set; }
public string SomeProperty { get; set; }
public virtual ICollection<EntityTwoTranslation> EntityTwoTranslations { get; set; }
public EntityTwo()
{
this.EntityTwoTranslations = new HashSet<EntityTwoTranslation>();
}
}
public class EntityTwoTranslation : EntityTranslation<long, EntityTwo>
{
public string LocalizedEntityTwoProp1 { get; set; }
public string LocalizedEntityTwoProp2 { get; set; }
}
public class EntityTranslation<TEntityKey, TEntity> : ILanguage
{
[Key, Column(Order = 0), ForeignKey("Entity")]
public TEntityKey EntityKey { get; set; }
[Key, Column(Order = 1), ForeignKey("Language")]
public long LanguageId { get; set; }
public virtual TEntity Entity { get; set; }
public virtual Language Language { get; set; }
}
INTERFACE
public interface ILanguage
{
long LanguageId { get; set; }
}
Here is the target
How would I get the entity navigation property using reflection/or something in order to reuse my code that could work too all entities that has a translation property collection?
I already tried to ask the same thing from 2 other posts, but I didn't give all the info. I guess that's why nobody could give me the expected answer.
Adding new entries over entity navigation property collection
Cast PropertyInfo to Collection
SAVE CHANGES OVERRIDE
public override int SaveChanges()
{
foreach (var entityOneEntry in ChangeTracker.Entries<EntityOne>())
{
if (entityOneEntry.State == EntityState.Added)
{
//Get entity localized properties values of current language.
var currentLanguageEntry = entityOneEntry.Entity.EntityOneTranslations.FirstOrDefault();
var localizedEntityOneProp1 = currentLanguageEntry.LocalizedEntityOneProp1;
var localizedEntityOneProp2 = currentLanguageEntry.LocalizedEntityOneProp2;
//Get all languages but the current one.
var languages = Language.Where(l => l.LanguageId != currentCulture.Key);
//Add missing translations copying the same values.
foreach (var language in languages)
entityOneEntry.Entity.EntityOneTranslations.Add(new EntityOne()
{
LanguageId = language.LanguageId,
LocalizedEntityOneProp1 = localizedEntityOneProp1,
LocalizedEntityOneProp2 = localizedEntityOneProp2
});
}
}
foreach (var entityOneEntry in ChangeTracker.Entries<EntityTwo>())
{
if (entityOneEntry.State == EntityState.Added)
{
//Get entity localized properties values of current language.
var currentLanguageEntry = entityOneEntry.Entity.EntityTwoTranslations.FirstOrDefault();
var localizedEntityTwoProp1 = currentLanguageEntry.LocalizedEntityTwoProp1;
var localizedEntityTwoProp2 = currentLanguageEntry.LocalizedEntityTwoProp2;
//Get all languages but the current one.
var languages = Language.Where(l => l.LanguageId != currentCulture.Key);
//Add missing translations copying the same values.
foreach (var language in languages)
entityOneEntry.Entity.EntityTwoTranslations.Add(new EntityTwo()
{
LanguageId = language.LanguageId,
LocalizedEntityTwoProp1 = localizedEntityTwoProp1,
LocalizedEntityTwoProp2 = localizedEntityTwoProp2
});
}
}
}

EF - not supported in LINQ to Entities

I am trying to list some food items with a controller. I use Repository pattern with UnitOfWork for the data in another assembly and referenced it in a BaseApiController. The Data property is my UnitOfWork instance.
var result = Data.Food
.FindAll()
.Select(FoodItemViewModel.Create);
return result;
and here is my ViewModel:
public static Expression<Func<FoodItem, FoodItemViewModel>> Create
{
get
{
return fi => new FoodItemViewModel
{
Id = fi.Id,
Description = fi.Description,
DiaryEntries = fi.DiaryEntries
.Select(s => new DiaryEntityViewModel()
{
Id = s.Id,
Quantity = s.Quantity
}
};
}
}
But all I get is:
"The specified type member 'DiaryEntries' is not supported in LINQ to
Entities. Only initializers, entity members, and entity navigation
properties are supported."
My DiaryEntries member in the ViewModel is
IEnumerable<DiaryEntityViewModel>
and my DiaryEntries member in the Data instance is
IRepository<DiaryEntry>
and DiaryEntry is my model class
and here is my FoodItem model class:
public class FoodItem
{
private IEnumerable<Measure> measures;
private IEnumerable<DiaryEntry> diaryEntries;
public FoodItem()
{
this.measures = new HashSet<Measure>();
this.diaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual IEnumerable<DiaryEntry> DiaryEntries
{
get
{
return this.diaryEntries;
}
set
{
this.diaryEntries = value;
}
}
public virtual IEnumerable<Measure> Measures
{
get
{
return this.measures;
}
set
{
this.measures = value;
}
}
}
Change you FoodItem class to the one below, IEnumerable<T> is not supported as a type for a navigation collection :
public class FoodItem
{
public FoodItem()
{
this.Measures = new HashSet<Measure>();
this.DiaryEntries = new HashSet<DiaryEntry>();
}
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<DiaryEntry> DiaryEntries
{
get;
set;
}
public virtual ICollection<Measure> Measures
{
get;
set;
}
}

How to use Automapper inside GenericRepository to Map Entities to DTO?

I want to add a method to my Repository that maps the given Generic type to a DTO. Assume that these are my Entity and DTO:
public class User : BaseEntity
{
public string firstName { get; set; }
public string lastName { get; set; }
List<UserOperation> operations { get; set; }
}
public class Operation : BaseEntity
{
public string src { get; set; }
public string title { get; set; }
}
public class UserOperation : BaseEntity
{
public int sortOrder { get; set; }
public Int64 userId { get; set; }
[JsonIgnore]
public virtual User user { get; set; }
public Int64 operationId { get; set; }
[JsonIgnore]
public virtual Operation operation { get; set; }
}
And I want to have a method like this in GenericRepository:
public interface IGenericRepository<T, Key>
{
PagedResult<U> getAllGrid<U>(IQueryable<T> query, SearchOption option) where U : class;
}
public class GenericRepository<T, Key> : IGenericRepository<T, Key> where T : BaseEntity
{
public PagedResult<U> getAllGrid<U>(IQueryable<T> query, SearchOption option) where U : class
{
Mapper.CreateMap<T, U>();
var totalRecordsCount = query.Count();
if (totalRecordsCount != 0)
{
List<U> mappedEntities = new List<U>();
foreach (T item in query.OrderBy(option.orderBy + " " + option.orderByType).Skip(option.start * option.size).Take(option.size))
{
mappedEntities.Add(Mapper.Map<T, U>(item));
}
return new PagedResult<U>()
{
Result = true,
Records = JsonConvert.SerializeObject(mappedEntities)
};
}
else
return new PagedResult<U>() { Result = true, MainRecords = null };
}
}
public class UserOperationRepository : GenericRepository<UserOperation, Int64>, IUserOperationRepository
{
public UserOperationRepository()
{
base.context = new AppContext();
}
public PagedResult<UserOperationDTO> getAllGrid(long userId, SearchOption option)
{
var query = base.context.Set<UserOperation>().AsQueryable();
return base.getAllGrid<UserOperationDTO>(query, option);
}
}
I'm new to automapper and GenericRepository.
AutoMapper supports projection - you don't need to call Mapper.Map:
var mappedEntities = query
.OrderBy(option.orderBy + " " + option.orderByType)
.Skip(option.start * option.size)
.Take(option.size)
.Project().To<U>()
.ToList();
I would also gather all your CreateMap's in one place - CreateMap is expensive and is only intended to be called once per AppDomain.

Categories