I have a Question.
I have 2 Dto Object and one Model.
What i'm trying todo is map the AssetDTO to the Asset Model using Automapper.
I have no clue on how to accomplish this.
public class AssetDTO
{
public string Code { get; set; }
public string CodeType { get; set; }
public List<MetricDataDTO> Data { get; set; }
}
public class MetricDataDTO
{
public string Value{ get; set; }
public string Flow { get; set; }
}
I have one model that look like this.
public class Asset
{
public string Code { get; set; }
public string CodeType { get; set; }
public string Value{ get; set; }
public string Flow { get; set; }
}
I tryed setting up the mapping with automapper but without any luck :(
Hope anyone can help me out,Thanks in advance!!
if suppose the value and flow inside asset is array, I think your current model for Asset need to be change (i.e. by creating new field List<MetricData> like List<MetricDataDTO>). Later you can check this one AutoMapper - Mapping list of objects to bigger list of objects.
In case for each asset there's only one value and flow, your model for AssetDTO will become:
public class AssetDTO
{
public string Code { get; set; }
public string CodeType { get; set; }
public MetricDataDTO Data { get; set; }
}
And for such case, here the example for nested object (based on Using automapper for nested objects): https://dotnetfiddle.net/qVg59r
Below answer also much more simpler https://stackoverflow.com/a/74471904/10766263
Thanks to Lucian Bargaoanu!
If you need more control when flattening, you can use IncludeMembers. You can map members of a child object to the destination object when you already have a map from the child type to the destination type.
This allows you to reuse the configuration in the existing map for the child types MetricDataDTO when mapping the parent types AssetDTO and Asset .
It works in a similar way to mapping inheritance, but it uses composition, not inheritance.
cfg.CreateMap<AssetDTO, Asset>().IncludeMembers(s=>s.MetricDataDTO);
You can check this link for details.
https://docs.automapper.org/en/latest/Flattening.html#includemembers
Hope it can help you.
Related
I have not worked with EF for a while.
As an exercise, I am writing a core web api that allows keeping track of a user medias.
A media can be a postcard, a photo album, a recording, a book...
I would like to know what is the way to go/best practice in writing the Add (createMedia) method:
[HttpPost]
public async Task<ActionResult<bool>> Add(Media media)
My model is comprised of several specific classes representing one type of media - like Postcard, Photoalbum, recording, etc. In addition, there is a Media type - which contains shared properties among all media types:
public class Media
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public MediaType Type { get; set; }
public bool InUse { get; set; }
public string Date { get; set; } //yyyy-mm-dd
public string Owner { get; set; }
}
As an example of one of the specific media types:
public class Postcard
{
[ForeignKey("Id")]
public int MediaId { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
I designed my EntityFramework db to consist of a 1-1 relation between Media and the relevant specific media table.
What is the best practice in writing the Add method ? Should it receive a Media object, and based on MediaType create the respective type ? I started with this approach, and had the action receive a second parameter named detailsJson, which I would parse and fill the respective object using reflection, but figured out that POST binding will not bind 2 objects.
I'm not well versed in design patterns.
Should there exist as many AddBook, AddPostcard... as many media types ?
I understand all models should be POCO objects, without inheritance.
I read about DTOs, but does not see how it helps me here.
I guess the Postcard and Book although both media types, they have different properties? for example, From, To, Place fields will not be there in book, whereas Book might have ISBN, Author, Genre etc.
so essentially they are different POCO objects altogether, media type is just a relationship they have in common
In this case, it's OK to have separate Add methods like AddBook, AddPostcard etc.
Update:
The individual POCO objects can refer to Media type to avoid duplicating same properties, same as underlying entity relationship
public class Postcard
{
public Media Media { get; set; }
public string From { get; set; }
public string To { get; set; }
public string Place { get; set; }
public string Language { get; set; }
}
What about having a Key-Value table?
public class MediaValue {
public string MediaId {get;set;}
public string AttributeName {get;set;}
public string AttributeValue{get;set;}
//the key is the couple MediaId - AttributeName
}
public class Media {
//shared properties
public MediaType MediaType {get;set;}
public IEnumerable<MediaValue> Attributes {get;set;}
}
public enum MediaType {
PostCard, PhotoAlbum, ....
}
then you could add the DTOs foreach mediatype
public abstract class MediaDTO {
//shared properties
}
public class PostCardDTO : MediaDTO {
public string PostCardSpecificAttribute {get;set;} // this.PostCardCustomAttribute = aMediaDbObject.Attributes.FirstOrDefault(x => x.AttributeName == "PostCardSpecificAttribute");
}
I'm hooking up to a pretty extensive online service using JSON in C# and have noticed they use the same name with different values (and types).
In creating the JSON models I'm running into an issue where the different models require different value types.
For example.
namespace Mylibrary
{
// event
public class event
{
public Item item { get; set; }
public string type { get; set; }
}
public class Item
{
public string url { get; set; }
public string icon { get; set; }
}
// context
public class context
{
public Item item { get; set; }
public string creator { get; set; }
}
public class Item
{
public int index { get; set; }
public string name { get; set; }
}
}
If I rename the above item class I can no longer use the json deserializer. However, of course I get a compiler error because of the duplicate class name "Item".
There are somewhere above 30 data models that I need to generate for this service. In looking closer at their schema, this is going to be an issue for over 90% of those models. The models themselves are very large, the example above is a simplified example of what I'm running into to illustrate the problem.
In thinking about this issue, I'm betting this would be a rather common occurrence. How is this dealt with?
As #mecek points out, it's the property names that matter, not the class names. So just give the classes unique names:
EventItem
ContextItem
Then you can use JsonProperty to rename the properties:
public class Context
{
[JsonProperty("item")]
public ContextItem Item { get; set; }
[JsonProperty("creator")]
public string Creator { get; set; }
}
I have two related entities called DataTag and TagSource that look like the following:
public class DataTag : BaseModel
{
[Column("DataTagId")]
public override Guid ID { get; set; }
public string Tag { get; set; }
public Guid TagSourceId { get; set; }
public TagSource TagSource { get; set; }
}
public class TagSource : BaseModel
{
[Column("TagSourceId")]
public override Guid ID { get; set; }
public string Description { get; set; }
public bool IsInternal { get; set; }
public string Source { get; set; }
public ICollection<DataTag> DataTags { get; set; }
}
I am allowing the user to Include the navigation properties through the url like "/api/DataTags?Include=TagSource". The problem is when I include the TagSource, it also includes the collection of DataTags in that object which I don't want unless the user specifies it (For example "/api/DataTags?Include=TagSource.DataTags". Is there any way to stop that property from being loaded when I include the TagSource? I have tried making the properties virtual and turning lazy loading off globally but that didn't work. The reason I haven't marked them virtual is because I am using AutoMapper and I only want to include the navigation properties that the user specifies.
As in the comments you need to create a DTO object. There is a good article here detailing how to do this with WebAPI
http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-5
Edit.
The problem with this is you will need a lot of different DTO objects for each possible outcome which could become messy. If your return type is JSON you can add this attribute to your properties:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
Firstly : Apologies for my English.
Secondly : I had the same issue with a code first database model that creates foreign keys this way : public virtual Collection<Object> Objects {get; set;}
and I found a workaround by setting the property setter as private:
public virtual Collection<Object> Objects {get; private set;}
Then the EF cannot populate the Objects collection because with a private set you can only assign a value in constructors.
I have a model and a partial model which contains only the properties that I need to expose in JSON.
But the properties between the model and his partial model are redundant.
How can I avoid that or improve my approach?
namespace Dashboard.Models.UserModels
{
public class UserModel
{
public int id { get; set; }
public string dbName { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public int idExternal { get; set; }
public int idInstance { get; set; }
public string login { get; set; }
public string password { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public LuccaUserModel()
{
idInstance = -1;
}
// partial model for json result
// not sure is the best way or have to be here
public class PartialUserModel
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string emailPro { get; set; }
public string emailPerso { get; set; }
public DateTime? dtContractStart { get; set; }
public DateTime? dtContractEnd { get; set; }
public string url { get; set; }
}
// UserModel Methods
}
}
You can rename PartialUserModel UserModelBase class (or leave it as is... it just makes better logical sense to do so) and make UserModel to inherit from it:
public class UserModel : UserModelBase
{
...
}
Of course you'll need to remove all duplicate properties from UserModel in this case.
It's a thin line between doing a proper design and building an overkill design. Answer depends on many inputs, among which I chose to have project and model breadth most important.
In hope to have my answer clearer, I have to say I use different terminology. Data which is adopted for use in UI is usually called ViewModel. In your case, you would build UserViewModel which contains necessary subset of information.
If I'm working on a one-off project, I'll reuse model as a ViewModel. I'll do this by having helper method which removes sensitive information, loads up or cuts off data which is lazy loaded from database and does other preparation on data. All this is done with same model class.
If it's not a short term project, I look to create separate ViewModel classes which I map from model data. Then, if I'm working with mostly flat data I use AutoMapper tool to have data automatically copied, instead of writing my own mappers.
As another answer here states, you write a basic class with data you need in UI and extend it with other model data, however this is not a good approach for several reasons.
If violates separation of concerns. Project dealing with model and persistance should not know about your ViewModel
You may need to flatten data from related objects into ViewModel objects. In that case, your model objects would have fields which should not be there, or would be redundant.
You may need calculated fields and helper methods in ViewModel which would again end up in model, confusing everyone that is not updated about design.
You could want to adopt several unrelated model classes to same ViewModel class
To try and put it shortly, either reuse model class or create ViewModels. There is unfortunately no clever solution. If you find one, please post a comment as I'd like to hear about it :)
I am using Automapper for mapping my domain model and DTO.
When I map Mapper.Map<SiteDTO, SiteEntity> it works fine.
But when I use collections of the same entities, it doesn't map.
Mapper.Map<Collection<SiteEntity>, Collection<SiteDTO>>(siteEntityCollection);
AS per Automapper Wiki, it says the lists implementing ICollection would be mapped, I am using Collection that implements ICollection, but automapper doesn't map it. Am I doing something wrong.
public class SiteEntity //SiteDTO has exactly the same properties, so I am not posting it here.
{
public int SiteID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public byte Status { get; set; }
public int ModifiedBy { get; set; }
public DateTime ModifiedDate{ get; set; }
public long TimeStamp{ get; set; }
public string Description{ get; set; }
public string Notes{ get; set; }
public ObservableCollection<AreaEntity> Areas{ get; set; }
public void SiteEntity()
{
Areas=new ObservableCollection<AreaEntity>();
}
}
EDIT: SiteEntity updated to include the constructor.
I have been using IList<> without any problems.
I would check the mapping of child domain models first.
Most probably they are not set yet. In your example: mapping of AreaEntity -> AreaEntityDto.
Mapper.Map<AreaEntity, AreaEntityDto>
Code example from wiki:
Mapper.CreateMap<ParentSource, ParentDestination>()
.Include<ChildSource, ChildDestination>();
Mapper.CreateMap<ChildSource, ChildDestination>();
Based on the code you posted Automapper will fail to map because you do not have a default constructor for SiteEntity that creates a new ObservableCollection Areas.
Since this is not there you will get a null reference exception when it trys to map Areas.