AutoMapper mapping base class and projection - asp.net-mvc

How do I map this:
public class Domain_1
{
public DomainType DomainType { get; set; }
public Domain_2 Domain2 { get; set; }
public Domain_3 Domain3 { get; set; }
}
to:
public abstract class DTOBase
{
// properties from Domain_1
}
public class DTO_1 : DTOBase
{
// properties from Domain_2
}
public class DTO_2 : DTOBase
{
// properties from Domain_3
}
Ideally,the Domain design should be same as the DTO but I can't due to EF6 and existing database restrictions.
Currently what I have right now is:
this.CreateMap<Domain_1, DTOBase>()
.ConstructUsing(SomeDTOCreatorFactoryMethod);
this.CreateMap<Domain_2, DTO_1>();
What SomeDTOCreatorFactoryMethod does is it creates the DTO based on the DomainType.
This works fine but I wanted to do some projection something like:
var domain_Db = dbContext.Domain1.Where(d => d.Id == 1).ProjectTo<DTOBase>.SingleOrDefault();
// var result = _mapper.Map<Domain1, DTOBase>(domain_Db);
Its throwing an error that cannot instantiate an abstract class. I understand the error but how I can use the factory method in order to create the DTO?
And what if I have to use a custom resolver on certain properties? I know this is not supported but is there a workaround?

Related

How to move data across layers (MVC5 + EF6 + DDD)?

We have a MVC 5 application using DDD principles. I need to send data from the Base Controller to the Base Repository, so I can log what user executed an operation for auditing purposes. The application structure is: Presentation (MVC 5) > Application (AppService) > Domain (Service) > Infra (Repository). The idea is to keep each layer independent from the other as much as possible.
In the BaseController I have the logged user from the session, that is accessible to all classes in the presentation layer:
public ProfileApp ProfileApp
{
get { return Session?[Constantes.Session.PROFILE] == null ? CreateProfileApp() : (ProfileApp)Session[Constantes.Session.PROFILE]; }
set { Session[Constantes.Session.PROFILE] = value; }
}
How can I get that information to the repository, in the Add method, declared as below:
public class RepositoryBase<TEntity> : IDisposable, IRepositoryBase<TEntity> where TEntity : BaseIdentity
{
protected VGPartnerDBContext _Db;
protected DbSet<TEntity> _DbSet;
public RepositoryBase(VGPartnerDBContext p_VGPartnerDBContext)
{
_Db = p_VGPartnerDBContext;
_DbSet = _Db.Set<TEntity>();
}
protected virtual T Add<T>(T obj) where T: BaseIdentity
{
return _Db.Set<T>().Add(obj);
}
public virtual TEntity Add(TEntity obj)
{
return Add<TEntity>(obj);
}
I would suggest to do the following, From you controller or Application layer you need to transfer and store data in DB right? In that case you need to create an base DTO
class AuditableDto
{
public string Username { get; set; }
public string ModifedBy { get; set; }
public DateTime ModifiedDate { get; set; }
}
And same for your Auditable entities you need a base class like this.
class AuditableEntity
{
public string Username { get; set; }
public string ModifedBy { get; set; }
public DateTime ModifiedDate { get; set; }
}
And then you can for each request from UI to you action methods in controller make a generic filter to add in your AuditableDtos this metada that you need and in same way you propogate this information to the Entities. In DDD you should follow principle of Persistence Ignorance and Infrastructure ignorance, all layer shoud depend on Domain layer.

Associate JsonSerializer instructions with a data-model partial class [duplicate]

I am using MetadataType to define Json.NET attributes for the following type, then serializing it using Json.NET inside its ToString() method:
namespace ConsoleApp1
{
public interface ICell
{
int Id { get; }
}
public interface IEukaryote
{
System.Collections.Generic.IEnumerable<ICell> Cells { get; }
string GenericName { get; }
}
public sealed partial class PlantCell
: ICell
{
public int Id => 12324;
}
public sealed partial class Plant
: IEukaryote
{
private readonly System.Collections.Generic.IDictionary<string, object> _valuesDict;
public Plant()
{
_valuesDict = new System.Collections.Generic.Dictionary<string, object>();
var cells = new System.Collections.Generic.List<PlantCell>();
cells.Add(new PlantCell());
_valuesDict["Cells"] = cells;
_valuesDict["GenericName"] = "HousePlant";
}
public System.Collections.Generic.IEnumerable<ICell> Cells => _valuesDict["Cells"] as System.Collections.Generic.IEnumerable<ICell>;
public string GenericName => _valuesDict["GenericName"] as string;
public int SomethingIDoNotWantSerialized => 99999;
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this,
new Newtonsoft.Json.JsonSerializerSettings()
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
}
);
}
}
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[Newtonsoft.Json.JsonProperty]
public System.Collections.Generic.IEnumerable<ICell> Cells;
[Newtonsoft.Json.JsonProperty]
public string GenericName;
//...
}
}
class Program
{
static void Main(string[] args)
{
var plant = new Plant();
System.Console.WriteLine(System.String.Format("Output is {0}", plant.ToString()));
System.Console.ReadKey();
}
}
}
My problem is that Plant.ToString() will return '{}'. Why is that? It was working before. The only change I made was in PlantMetadata where I altered the MemberSerialization to OptIn instead of OptOut, as I had less properties I wanted included than left out.
As stated by Newtonsoft in this issue, MetadataTypeAttribute attributes are in fact supported by Json.NET. However, it appears that Json.NET requires that the MetadataClassType members must be properties when the corresponding "real" members are properties, and fields when the corresponding "real" members are fields. Thus, if I define your Plant type as follows, with two properties and one field to be serialized:
public sealed partial class Plant : IEukaryote
{
public System.Collections.Generic.IEnumerable<ICell> Cells { get { return (_valuesDict["Cells"] as System.Collections.IEnumerable).Cast<ICell>(); } }
public string GenericName { get { return _valuesDict["GenericName"] as string; } }
public string FieldIWantSerialized;
public int SomethingIDoNotWantSerialized { get { return 99999; } }
// Remainder as before.
Then the PlantMetadata must also have two properties and one field for them to be serialized successfully:
//Metadata.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public IEnumerable<ICell> Cells { get; set; }
[JsonProperty]
public string GenericName { get; set; }
[JsonProperty]
public string FieldIWantSerialized;
}
}
If I make Cells or GenericName be fields, or FieldIWantSerialized be a property, then they do not get opted into serialization.
Sample working .Net Fiddle.
Note that, in addition, I have found that the MetadataClassType properties apparently must have the same return type as the real properties. If I change your PlantMetadata as follows:
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public object Cells { get; set; }
[JsonProperty]
public object GenericName { get; set; }
[JsonProperty]
public object FieldIWantSerialized;
}
Then only FieldIWantSerialized is serialized, not the properties. .Net Fiddle #2 showing this behavior. This may be a Newtonsoft issue; as stated in the Microsoft documentation Defining Attributes in Metadata Classes:
The actual type of these properties is not important, and is ignored
by the compiler. The accepted approach is to declare them all as of
type Object.
If it matters, you could report an issue about the return type restriction to Newtonsoft - or report an issue asking that details of their support for MetadataTypeAttribute be more fully documented.

ReferencesAny nhibernate mapping in entity framework?

I'm wondering how to create mapping in entity framework,
that is similar to ReferenceAny in fluent nhibernate.
What I got:
Entities:
public abstract class Document : Entity
{
public virtual ICollection<Feature> Features { get; set; }
}
public class DocumentA : Document
{
}
public class DocumentB : Document
{
}
public class Feature : Entity
{
public IEntity Super { get; set; }
public virtual string Value { get; set; }
}
In Feature mapping:
ReferencesAny(x => x.Super)
.EntityTypeColumn("type")
.EntityIdentifierColumn("super")
.AddMetaValue<DocumentA>("DoucmentA")
.AddMetaValue<DokumentB>("DocumentB")
.IdentityType<int>();
In DocumentA mapping:
HasMany<Feature>(x => x.Features)
.AsSet()
.Inverse()
.KeyColumn("super").Not.KeyNullable()
.Where("type = 'DocumentA'")
.Fetch.Join()
.Cascade.AllDeleteOrphan();
In DocumentB mapping:
HasMany<Feature>(x => x.Features)
.AsSet()
.Inverse()
.KeyColumn("super").Not.KeyNullable()
.Where("type = 'DocumentB'")
.Fetch.Join()
.Cascade.AllDeleteOrphan();
This kind of mapping gives me clean Features table with columns: id, value, type, super,
where type act as discriminator and super is document id.
How map this in EF to achieve Features table with same columns, since I can't change it?

Why model binder cannot recover abstract classes after POST?

I'm starting to working on ASP.NET using MVC. I writing to action results, one of them is a HTTP GET and the another HTTP POST
[HttpGet]
public ActionResult DoTest()
{
Worksheet worksheets = new worksheets(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
Now, Worksheet class has a property called Problems and this is a collection, but uses as an abstract class item.
public class Worksheet
{
public List<Problem> Problems { get; set; }
}
Here's my abstract class and one implementation
public abstract class Problem
{
public Problem() { }
public int Id { get; set; }
public abstract bool IsComplete { get; }
protected abstract bool CheckResponse();
}
public class Problem1 : Problem
{
...
public decimal CorrectResult { get; set; }
// this is the only property of my implementation class which I need
public decimal? Result { get; set;}
public override bool IsComplete
{
get { return Result.HasValue; }
}
protected override bool CheckResponse()
{
return this.CorrectResult.Equals(this.Result.Value);
}
}
I have right now, many implementations of Problem class, but I really need to get just one value of my implementation class. But it thrown the above image error.
What can I do to allow model binder recover that part of my abstracts classes
The following code would not compile:
var problem = new Problem();
... because the Problem class is abstract. The MVC engine cannot just create a Problem directly. Unless you give it some way to know which type of Problem to instantiate, there's nothing it can do.
It is possible to create your own ModelBinder implementation, and tell MVC to use it. Your implementation could be tied to a Dependency Injection framework, for example, so that it knows to create a Problem1 whenever a Problem class is requested.
Or you could simply change your action method to take a concrete type:
public ActionResult DoTest(IEnumerable<Problem1> problems)
{
return PartialView("_Problems",
new Worksheet {
Problems = problems.Cast<Problem>().ToList()
});
}

Problem with Automapper and Windsor

I have a problem with Automapper when I try use custom resolver which uses dependency injection.
I have the following model:
public class User : Entity
{
public virtual string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country : Entity
{
public virtual string Name { get; set; }
}
and the following view model:
public class RegistrationViewModel
{
[Required]
public string Name { get; set; }
public int CountryId { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
in order to map I use the following code:
Mapper.Map(registrationViewModel, user);
earlier I register the following:
Mapper.Reset();
container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<ISession>().
UsingFactoryMethod(() => NHibernateSessionFactory.RetrieveSession()).
LifeStyle.Is(LifestyleType.Transient));
container.Register(Component.For(typeof(LoadingEntityResolver<>)).ImplementedBy(typeof(LoadingEntityResolver<>)).LifeStyle.Transient);
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
My BasicProfile is the following:
public class BasicProfile : Profile
{
public const string VIEW_MODEL = "MyBasicProfile";
public override string ProfileName
{
get { return VIEW_MODEL; }
}
protected override void Configure()
{
CreateMaps();
}
private void CreateMaps()
{
CreateMap<RegistrationViewModel, User>()
.ForMember(dest => dest.Country, _ => _.ResolveUsing<LoadingEntityResolver<Country>>().FromMember(src => src.CountryId))
);
}
}
The custom resolver is done in the following way:
public class LoadingEntityResolver<TEntity> : ValueResolver<int, TEntity>
where TEntity: Entity
{
private readonly ISession _session;
public LoadingEntityResolver(ISession session)
{
_session = session;
}
protected override TEntity ResolveCore(int source)
{
return _session.Load<TEntity>(source);
}
}
When the mapping code is being run I get the following exception:
AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to Models.User.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to LModels.Country.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Destination property: Country
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> System.ArgumentException : Type 'Mapping.LoadingEntityResolver`1[Models.Country]' does not have a default constructor
I have no idea what may be wrong. It is probably something with constructing the resolver. When I try the following there is no problem:
var resolver = container.Resolve<LoadingEntityResolver<Country>>();
Assert.IsInstanceOf<LoadingEntityResolver<Country>>(resolver);
I would be greatfull for any help.
Best regards
Lukasz
You have some pretty hefty DI stuff going on here :-) I would avoid having AutoMapper resolve entities from a database or whatever. Makes the code difficult to understand and following objects lifetime could become a nightmare.
Anyway, to fix your problem simply swap the order from (wrong):
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
to (correct):
Mapper.Initialize(x =>
{
x.ConstructServicesUsing(container.Resolve);
x.AddProfile<BasicProfile>();
});

Resources