I am trying to map reference data entities to dtos, both of which inherit from a generic base class within their own namespace and using an "all purpose" profile to add the mappings. Consider the following code:
namespace Dtos
{
public abstract class ReferenceData<TId>
where TId : Enum
{
public TId Id { get; set; }
public string Description { get; set; }
}
public class ConcreteDto : ReferenceData<MyEnum> { }
}
namespace Entities
{
public abstract class ReferenceData<TId>
where TId : Enum
{
public TId Id { get; set; }
public string Description { get; set; }
}
public class ConcreteEntity : ReferenceData<MyEnum> { }
}
namespace DtoMapping
{
internal abstract class ReferenceDataDtoProfile<TDto, TEntity, TId> : Profile
where TDto : Dtos.ReferenceData<TId>
where TEntity : Entities.ReferenceData<TId>
where TId : Enum
{
public ReferenceDataDtoProfile()
{
}
protected IMappingExpression<TDto, TEntity> CreateDtoToEntityMap()
{
return this.CreateMap<TDto, TEntity>()
.IncludeBase<Dtos.ReferenceData<TId>, Entities.ReferenceData<TId>>();
}
protected IMappingExpression<TEntity, TDto> CreateEntityToDtoMap()
{
return this.CreateMap<TEntity, TDto>()
.IncludeBase<Entities.ReferenceData<TId>, Dtos.ReferenceData<TId>>();
}
protected void CreateMaps()
{
this.CreateDtoToEntityMap();
this.CreateEntityToDtoMap();
}
}
internal sealed class ProfileForConcreteEntity : ReferenceDataDtoProfile<Dtos.ConcreteDto, Entities.ConcreteEntity, MyEnum>
{
public PeriodProfile()
{
this.CreateMaps();
}
}
}
When I run the application and try to get to the endpoint in question I can see in the debugger that the code of the ReferenceDataDtoProfile class gets executed but then I get the exception that there are no mappings for this objects, specifically:
InvalidOperationException: Missing map from
Dtos.ReferenceData'1[MyEnum] to Entities.ReferenceData'1[MyEnum].
Create using CreateMap(ReferenceData'1, ReferenceData'1).
As you can see I am adding the "IncludeBase" method for both ReferenceData abstract classes so I do not understand why I am getting this exception.
On my Web API project I have included the AutoMapper.Extensions.Microsoft.DependendyInjection package version 7.0.0.
Thank you.
It appears that IncludeBase just specifies that the mapping of the derived objects will inherit the configuration of the base objects. However, you still need to create the actual mapping for both the base and derived objects:
protected IMappingExpression<TDto, TEntity> CreateDtoToEntityMap()
{
this.CreateMap<Dtos.ReferenceData<TId>, Entities.ReferenceData<TId>>();
return this.CreateMap<TDto, TEntity>()
.IncludeBase<Dtos.ReferenceData<TId>, Entities.ReferenceData<TId>>();
}
protected IMappingExpression<TEntity, TDto> CreateEntityToDtoMap()
{
this.CreateMap<Entities.ReferenceData<TId>, Dtos.ReferenceData<TId>>();
return this.CreateMap<TEntity, TDto>()
.IncludeBase<Entities.ReferenceData<TId>, Dtos.ReferenceData<TId>>();
}
See Mapping Inheritance for more info.
Related
I have the following interface I want to implement in a class:
public interface IAgro {
AgroState agroState { get; }
}
The problem is that instead of implementing AgroState in the class using the interface I want my property to implement a different class which inherits from AgroState
public class E1_AgroState : AgroState
{
...
}
public class BasicWalkingEnemy : Entity, IAgro
{
public E1_AgroState agroState { get; }
}
This is something I am used to do in Swift with protocols, for example, but C# compiler complains with
'BasicWalkingEnemy' does not implement interface member 'IAgro.agroState'. 'BasicWalkingEnemy.agroState' cannot implement 'IAgro.agroState' because it does not have the matching return type of 'AgroState'. [Assembly-CSharp]csharp(CS0738)
For now one solution I found is doing it like:
public class BasicWalkingEnemy : Entity, IAgro
{
public AgroState agroState { get { return _agroState; } }
public E1_AgroState _agroState { get; private set; }
}
But I think that is very inelegant.
Is there a better solution to my problem?
Typically the way you'd do this is with explicit interface implementation so that anyone who only knows about your object via IAgro will just see AgroState, but any code that knows it's working with BasicWalkingEnemy will get E1_Agrostate:
// Note: property names changed to comply with .NET naming conventions
public class BasicWalkingEnemy : Entity, IAgro
{
// Explicit interface implementation
AgroState IAgro.AgroState => AgroState;
// Regular property
public E1_AgroState AgroState { get; private set; }
}
I have this base class:
public class BaseEvent
{
public int EventID { get; set; }
public int GetEventID()
{
return EventID;
}
}
And then I have another class inherited from that base one:
public class ValidationResult<T> where T : BaseEvent
{
private void AddEventStatusUpdater(ValidationResult<T> validationResult)
{
validationResult.GetEventID();
}
}
The issue I´m having is that I cannot access the GetEventID() method from the base class.
I think this may happen because I´m using a T generic. Is there any other way to access this method?
public class ValidationResult<T> where T : BaseEvent
Says that T must be a BaseEvent, not that ValidationResult<T> inherits from BaseEvent. That'd be:
public class ValidationResult<T> : BaseEvent
And there T would not have any constraint
Is that what you want?
You have begun to implement a generic interface rather than inheriting from the base class. I think you meant to do the following:
public class ValidationResult : BaseEvent
{
private void AddEventStatusUpdater()
{
var id = this.GetEventID();
}
}
I am trying to create multilayer application which consist on DL, BL and GUI modules. DL contains entities, BL contains ViewModels and Services and GUI contains Controllers. My goal is to BL knows about DL, and GUI knows about BL but not DL.
So I have Entity hierarchy like this (in DL):
namespace DL
{
public abstract class EntityBase
{
public int Id { get; set; }
}
public class Student : EntityBase
{
public string Name { get; set; }
}
}
And ViewModel hierarchy (in BL module):
namespace BL
{
public abstract class ViewModelBase
{
public int Id { get; set; }
}
public class StudentViewModel : ViewModelBase
{
public string Name { get; set; }
}
}
And Services (also in BL):
using DL;
namespace BL
{
public interface IServiceBase<out TViewModel>
where TViewModel : ViewModelBase
{
TViewModel GetViewModel(int id);
}
public abstract class ServiceBase<TEntity, TViewModel> : IServiceBase<TViewModel>
where TViewModel : ViewModelBase, new()
{
public virtual TViewModel GetViewModel(int id)
{
return new TViewModel() { Id = id };
}
}
public class StudentsService : ServiceBase<Student, StudentViewModel>
{
}
}
And I want to use this in GUI:
using BL;
namespace GUI
{
class StudentController : ControlerBase<StudentsService, StudentViewModel>
{
public StudentController(StudentsService service)
: base(service)
{
}
public void DoSomething()
{
var s = this.service.GetViewModel(123);
}
}
class ControlerBase<TService, TViewModel>
where TService : IServiceBase<TViewModel>
where TViewModel : ViewModelBase
{
protected readonly TService service;
public ControlerBase(TService service)
{
this.service = service;
}
public TViewModel GetViewModel(int id)
{
return this.service.GetViewModel(id);
}
}
}
It looks good for me. Controllers knows about IService and ViewModels and everything should work, but when I try to compile, I get this error message:
Error 1 The type 'DL.Student' is defined in an assembly that is not
referenced. You must add a reference to assembly 'DL, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null'.
Ok, I understand that StudentsService derived from generic ServiceBase which is "entity aware". But why compiler bothers that?
Moreover, when I'm not calling GetViewModel method in Controller, everything compiles correctly. Why?
And when I write in StudentsService completely ridiculous method like this:
public new virtual StudentViewModel GetViewModel(int id)
{
return base.GetViewModel(id);
}
also everything compiles correctly. Why?
And finally - what should I do, to not writing this bizarre "new virtual" method in all my services?
I have problem with constraints on generic method. Here is code for all classes:
namespace Sdk.BusinessObjects
{
public interface IBusinessObject
{
}
}
namespace Sdk.BusinessObjects
{
[DataContract]
public class AccountDetails : IBusinessObject
{
[DataMember]
public virtual Guid AccountId { get; set; }
// More properties...
}
}
namespace Sdk.BusinessLogic
{
public interface IManager<T> where T : IBusinessObject
{
T Add(T businessObject);
void Delete(T businessObject);
IList<T> ListAll();
}
}
namespace Sdk.BusinessLogic
{
public interface IAccountManager : IManager<AccountDetails>
{
void ChangeAccountState(Guid accountId, string state);
}
}
namespace Sdk.BusinessLogic
{
public interface IManagerFactory
{
T Create<T>() where T : IManager<IBusinessObject>;
}
public class ManagerFactory : IManagerFactory
{
public T Create<T>() where T : IManager<IBusinessObject>
{
// resolve with Unity and return
}
}
}
So, I have main IBusinessObject interface for all business objects (like AccountDetails) and IManager as generic manager interface for business objects. I wanted to create factory for these managers with constraints. When I try something like this in UnitTest:
IManagerFactory factory = new ManagerFactory();
factory.Create<IAccountManager>();
I get error:
The type 'Sdk.BusinessLogic.IAccountManager' cannot be used as type parameter 'T' in the generic type or method 'Sdk.BusinessLogic.IManagerFactory.Create()'. There is no implicit reference conversion from 'Sdk.BusinessLogic.IAccountManager' to 'Sdk.BusinessLogic.IManager'.
How can this be done?
Basically your problem is that IManager<T> is invariant, and has to be as you've got values coming out of the API and values going into it. So an IAccountManager isn't an IManager<IBusinessObject>, because otherwise you could write:
IAccountManager m1 = new SomeImplementation();
IManager<IBusinessObject> m2 = m1;
m2.Add(new SomeArbitraryBusinessObject());
An account manager is only meant to manage accounts, not just any business object.
One option is to use two generic type parameters instead of one for ManagerFactory.Create:
public TManager Create<TManager,TObjectType>()
where TManager : IManager<TObjectType>
where TObjectType : IBusinessObject
it seems if i use an custom class as base of an entity,the ObjectContext.CreateObjectSet will fail with stackoverflow exception
code is:
// This is generated by EF4 and i modify it to my custom class
public partial class EntityA : GClass<EntityA>
{
......
}
public partial class TestEntities : ObjectContext
{
public ObjectSet<EntityA> EntityAs
{
get
{
if ((_EntityAs == null))
{
// here will throw stackoverflow exception
_EntityAs = base.CreateObjectSet<EntityA>("EntityAs");
}
return _EntityAs;
}
}
private ObjectSet<EntityA> _EntityAs;
}
// This is custom class
public partial class EntityA
{
}
// This is my custom base class
public class GClass<T> : EntityObject where T : class
{
public virtual string GetStr()
{
return "GClass";
}
}
I recommend creating an interface for your entity objects instead of changing the base class. Generated code should not be modified.
Update: Due to unexplained downvotes, I'm adding the code below, which spells out precisely what I mean:
// Generated by EF4
public partial class EntityA : EntityObject
{
...
}
// Interface defined in another file
public interface IGClass<T> where T : IGClass<T>
{
string GetStr();
}
// Automatically generated by T4 template
public partial class EntityA : IGClass<EntityA>
{
public virtual string GetStr()
{
return "GClass";
}
}
The resulting code does use CRGP, but does so via an interface instead of a base class.
More info on T4 templates is here.