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.
Related
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.
I would like to know, how could I, with AutoMapper, Map one Dto to multiple entities.
Lemme explain.
I've got one Dto, with an enum to describe its type (to avoid having multiple dtos)
Depending on that enum (RelationType here), I would like to map it to the correct Model (Entity, what ever, it's another object that I use in database).
public class BCardDto : IMappedDto
{
public long Id { get; set; }
public BCardRelationType RelationType { get; set; }
public long RelationId { get; set; }
}
Here are is my Model base:
public class BCardModel : IMappedDto
{
public long Id { get; set; }
}
And here the derived model :
public class CardBCardModel : BCardModel
{
// ormlite, ignore that
[Reference]
public CardModel Card { get; set; }
[ForeignKey(typeof(CardModel), ForeignKeyName = "fk_bcard_card")]
public long RelationId { get; set; }
}
How do I map my Dto to the correct Model depending on the enum i've given ?
(I don't wanna use Mapper.Map everywhere but I wanna let mapper do the runtime mapping job)
Here is how I do it for the Model -> Dto
cfg.CreateMap<CardBCardModel, BCardDto>()
.ForMember(s => s.RelationType, expression => expression.UseValue(BCardRelationType.Card))
.IncludeBase<BCardModel, BCardDto>();
Tell me if I do something wrong and explain me why please :)
Thanks by advance,
Blowa.
Let's say you have a setup wherein there is a base class and 2 classes which derive the base class:
public class ModelBase
{
public string Name { get; set; }
}
public class ModelOne : ModelBase { }
public class ModelTwo : ModelBase { }
Let's also say you have a DTO with an enum as below:
public class ModelDto
{
public string Name { get; set; }
public ModelType ModelType { get; set; }
}
public enum ModelType
{
One = 1,
Two = 2
}
So now the task is: How do I map the ModelDto to either ModelOne or ModelTwo depending on the value in ModelDto.ModelType property?
Here is how:
Mapper.Initialize(cfg => cfg.CreateMap<ModelDto, ModelBase>().ConstructUsing(x =>
{
switch (x.ModelType)
{
case ModelType.One:
return new ModelOne { Name = x.Name };
case ModelType.Two:
return new ModelTwo { Name = x.Name };
default:
throw new InvalidOperationException("Unknown ModelType...");
}
}));
Usage
var dto1 = new ModelDto { ModelType = ModelType.One, Name = "ModelOne" };
var dto2 = new ModelDto { ModelType = ModelType.Two, Name = "ModelTwo" };
var one = Mapper.Map<ModelBase>(dto1);
var two = Mapper.Map<ModelBase>(dto2);
Another way to do the mapping is by using dynamic:
public class PersonDto
{
public string Name { get; set; }
}
public class StudentDto : PersonDto
{
public int studentNumber { get; set; }
}
public class EmployeDto : PersonDto
{
public string EmployeId { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public class Student : Person
{
public int StudentNumber { get; set; }
}
public class Employe : Person
{
public string EmployeId { get; set; }
}
Create Map by using:
Mapper.CreateMap<StudentDto, Student>();
Mapper.CreateMap<EmployeDto, Employe>();
Do the Mapping by:
try
{
var student = MapPerson((dynamic) studentDto);
var employe = MapPerson((dynamic) employeDto);
}
catch
{
throw new InvalidOperationException("Unknown ModelType...");
}
And define two Methods
public static Student MapPerson(StudentDto studentDto)
{
return Mapper.Map<StudentDto, Student>(studentDto);
}
public static Employe MapPerson(EmployeDto employeDto)
{
return Mapper.Map<EmployeDto, Employe>(employeDto);
}
The benefit is that you don't need a key and avoid the switch statement
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
});
}
}
}
I have an Generic Abstract Class with some properties like Id, Name, Status, this class inherits several catalogs.
My question is whether it is possible to create a method with a restriction for the catalogs that implement the Abstract Class.
I give some examples so that they understand what I want to do:
public abstract class AbsCatalog<T>
{
public T Id { get; set; }
public string Name { get; set; }
public bool Status { get; set; }
}
These are the classes that implement the abstract class
public class Agent : AbsCatalog<string>
{
public string Office { get; set; }
public Estado Estado { get; set; }
}
public class Models : AbsCatalog<int>
{
public int Year { get; set; }
public string Description { get; set; }
}
The method I want to implement is the following:
List<Agent> Agents = service.GetAgents();
string AgentsDescription = GetDescription<Agent>(Agents);
List<Model> Models = service.GetModels();
string ModelsDescription = GetDescription<Model>(Models);
private string GetDescription<T>(List<T> list) where T : AbsCatalog<T>
{
string description = string.Empty;
if (list.Exists(x => x.Id.ToString() == "0"))
description = "";
else
description = string.Join(", ", list.Where(x => x.Status).Select(x => x.Name).ToArray());
return description;
}
I think the only way is to use two generic type parameters here, for example:
private string GetDescription<T, U>(List<T> list) where T : AbsCatalog<U>
{
//snip
}
And then call it like this:
string AgentsDescription = GetDescription<Agent, string>(Agents);
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;
}
}