Automapping Collections - c#

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.

Related

AutoMapper DTOModel with Nested List to Model

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.

EF Core name mapping

I've been trying to figure out how to do the following (although my research did not help): I have the these three classes:
public abstract class Classifier
{
public int ClassifierId { get; set; }
public string ClassifierName { get; set; }
public DateTime DateAdded { get; set; }
}
public class ManualClassifier : Classifier
{
public int ManualClassifierId { get; set; }
public string user_name { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string password { get; set; }
}
public class ToolClassifier : Classifier
{
public int ToolId { get; set; }
public string ToolName { get; set; }
}
Both the ManualClassifier and ToolClassifer inherit from Classifier. I'm using EF Core to map this to a database but the question is the following: I've already searched a bit and I must make use of a descriminator which basically is an implicitly created column that will say the type of, in this case, classifier. So far so good. The issue arises when I have a property called ManualClassifierId as well as a ToolId. I want this two properties to map to the ClassifierId property. So in the table representing the entity Classifier, the ClassifierId property will either be the ManualClassifierId or the ToolId.
How can I achieve this mapping? Also, this solution would mean that both child classes would both have empty fileds in the tables (due to inheriting the three properties from the Classifier class). Is there a better solution? Perhaps just erase the Id's from both child classes a let them inherit the parent one?
Thank you in advance!
To use the same column name in both classes, you can add a Column attribute to both properties. Then they will both use that column name in the database. See ColumnAttribute(String).
Use it like this:
public class ManualClassifier : Classifier
{
[Column(Name="ClassifierId")]
public int ManualClassifierId { get; set; }
...........
}
Do the same with ToolId.

Entity Framework Include without nested properties

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.

Entity Framework Class / Database Schema advice needed

I asked a question recently, and quite frankly, from the answer that was given, I am second guessing my entire strategy/how I design the classes and database.
I have not yet used the virtual keyword, nor Icollection at all in any of my Entity Framework projects, and quite frankly, after reading about it in some of the examples I do not fully understand why it is needed, or how it works.
In a sample application, I have a simple design where there are three lists - people, notes and pictures. The relationships are such that a person can own multiple notes and pictures, as well as people having a logo which is a picture.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
When I want to select a list of notes that a person owns, I simply perform db.Notes.Where(x=>x.owner=="y") on the notes object. I think I understand that if I were to use Icollection on the person class, I could instead perform something along the lines of db.person.select(x=> x.notes) to retrieve all the notes. Am I correct in this thinking?
If you were in my position with the relatively simple example above, how would you build the classes (involving ICollection, virtual or anything else)?
In addition and most importantly, the above is just an example, however in my actual application, I have used a very similar structure where I use my custom type as the "connector"/Foreign Key.
In many examples I have been reading, (in the above example) they would be using public int OwnerID instead of public person Owner. This has really thrown me and I am questioning my entire EF strategy. What are the differences?
Any advice would be greatly appreciated.
I think you are making this more difficult that is needed. If you were laying out regular classes you would relate them to each other rather than finding related id's and loading them separately which you are doing in your example.
public class Person
{
public int ID { get; set; }
public string name { get; set; }
public ICollection<Note> Notes { get; set; }
public ICollection<Picture> Pictures { get; set; }
public Picture logo { get; set; }
}
public class Note
{
public int ID { get; set; }
public string Text { get; set; }
public Person Owner { get; set; }
}
public class Picture
{
public int ID { get; set; }
public string Path { get; set; }
public Person Owner { get; set; }
}
So now say you have gotten your person object using the query
var person = _context.People.Where(m=>m.ID=randomIntWeWant).First();
We can get all related items as properties.
For Notes
person.Notes
For Photos
person.Photos
ICollection is related to lazy loading. By declaring a property as ICollection on one side, your are saying you have a many-to-one relationship between the objects. If you declare a property as ICollection on both sides, you are saying it is a many-to-many relationship. EF takes care of creating the tables that track that relationship.

EF 4.1 - Code First - JSON Circular Reference Serialization Error

I am getting an a Circular Reference Serialization Error although, to my knowledge I do not have any circular references. I am retrieving a set of Orders from the database and sending them to the client as JSON. All the code is shown below.
This is the error:
Error
A circular reference was detected
while serializing an object of type
'System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812'.
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: A
circular reference was detected while
serializing an object of type
'System.Data.Entity.DynamicProxies.Order_83CECF2AA4DE38232F9077D4B26941AB96BC61230419EA8AC42C9100E6072812'.
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
My classes are as follows:
Order
public class Order
{
[Key]
public int OrderId { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
public int CertificationPeriodId { get; set; }
public virtual CertificationPeriod CertificationPeriod { get; set; }
public int AgencyId { get; set; }
public virtual Agency Agency { get; set; }
public int PrimaryDiagnosisId { get; set; }
public virtual Diagnosis PrimaryDiagnosis { get; set; }
public int ApprovalStatusId { get; set; }
public virtual OrderApprovalStatus ApprovalStatus { get; set; }
public int ApproverId { get; set; }
public virtual User Approver { get; set; }
public int SubmitterId { get; set; }
public virtual User Submitter { get; set; }
public DateTime ApprovalDate { get; set; }
public DateTime SubmittedDate { get; set; }
public Boolean IsDeprecated { get; set; }
}
Patient
public class Patient
{
[Key]
public int PatientId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleInitial { get; set; }
public bool IsMale;
public DateTime DateOfBirth { get; set; }
public int PatientAddressId { get; set; }
public Address PatientAddress { get; set; }
public bool IsDeprecated { get; set; }
}
Certification Period
public class CertificationPeriod
{
[Key]
public int CertificationPeriodId { get; set; }
public DateTime startDate { get; set; }
public DateTime endDate { get; set; }
public bool isDeprecated { get; set; }
}
Agency
public class Agency
{
[Key]
public int AgencyId { get; set; }
public string Name { get; set; }
public int PatientAddressId { get; set; }
public virtual Address Address { get; set; }
}
Diagnosis
public class Diagnosis
{
[Key]
public int DiagnosisId { get; set; }
public string Icd9Code { get; set; }
public string Description { get; set; }
public DateTime DateOfDiagnosis { get; set; }
public string Onset { get; set; }
public string Details { get; set; }
}
OrderApprovalStatus
public class OrderApprovalStatus
{
[Key]
public int OrderApprovalStatusId { get; set; }
public string Status { get; set; }
}
User
public class User
{
[Key]
public int UserId { get; set; }
public string Login { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string NPI { get; set; }
public string Email { get; set; }
}
NOTE: ADDRESS CLASS IS NEW ADDITION DURING EDIT
Address
public class Address
{
[Key]
public int AddressId { get; set; }
public string StreetAddress { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Phone { get; set; }
public string Title { get; set; }
public string Label { get; set; }
}
The code that executes the serialization is here:
Excerpt from OrderController
public ActionResult GetAll()
{
return Json(ppEFContext.Orders, JsonRequestBehavior.AllowGet);
}
Thanks
You could try to remove the virtual keyword from all navigation properties to disable lazy loading and proxy creation and then use eager loading instead to load the required object graph explicitely:
public ActionResult GetAll()
{
return Json(ppEFContext.Orders
.Include(o => o.Patient)
.Include(o => o.Patient.PatientAddress)
.Include(o => o.CertificationPeriod)
.Include(o => o.Agency)
.Include(o => o.Agency.Address)
.Include(o => o.PrimaryDiagnosis)
.Include(o => o.ApprovalStatus)
.Include(o => o.Approver)
.Include(o => o.Submitter),
JsonRequestBehavior.AllowGet);
}
Referring to your previous post it looks like your application isn't relying on lazy loading anyway because you introduced there the virtual properties to load the object graph lazily, possibly causing now the serialization trouble.
Edit
It's not necessary to remove the virtual keyword from the navigation properties (which would make lazy loading completely impossible for the model). It's enough to disable proxy creation (which disables lazy loading as well) for the specific circumstances where proxies are disturbing, like serialization:
ppEFContext.Configuration.ProxyCreationEnabled = false;
This disables proxy creation only for the specific context instance ppEFContext.
(I've just seen, #WillC already mentioned it here. Upvote for this edit please to his answer.)
When you know that you need to serialize from a particular context, you can disable proxy creation for that particular query like below. This has worked for me and is better than revising my model classes.
using (var context = new MeContext())
{
context.Configuration.ProxyCreationEnabled = false;
return context.cars.Where(w => w.Brand == "Ferrari")
}
This approach takes away the proxy object type for this particular instance of the context so the returned objects are the actual class and therefore serialization is not a problem.
ie:
{Models.car}
instead of
{System.Data.Entity.DynamicProxies.car_231710A36F27E54BC6CE99BB50E0FE3B6BD4462EC‌​A19695CD1BABB79605296EB}
The problem is that your are actually serializing an entity framework generated proxy object. Unfortunatly this has some issues when used with the JSON serializer. You might consider to map your entities to special simple POCO classes for the sake of JSON compatibility.
There is an attribute to add to Entity Framework objects
[ScriptIgnore]
This makes the code not perform Circular references.
I think they have fixed this in the latest version.
Check out the help docs under the section "Serializing and Deserializing JSON -> Serialization and Preserving Object References".
Set this setting when initializing the JSON.Net Serializer:
PreserveReferencesHandling = PreserveReferencesHandling.Objects;
So an example would be this:
var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);
I verified that this works with my code first solution, and a circular reference in the navigation properties. If you look at the resulting JSON it should have "$id" and "$ref" properties everywhere.
An alternative solution would be to use anonymous types as the result of a LINQ query.
In my project, I am using lazy loading extensively, and disabling it was not the right thing to do.
An alternative solution, if only some values from objects are necessary, is build an anonymous class and return it, like the example below:
public JsonResult AjaxFindByName(string term)
{
var customers = context.Customers
.Where(c => c.Name.ToUpper().Contains(term.ToUpper())).Take(10)
.AsEnumerable()
.Select(c => new {
value = c.Name,
SSN = String.Format(#"{0:000\-00\-0000}", c.SSN),
CustomerID = c.CustomerID });
return Json(customers, JsonRequestBehavior.AllowGet);
}
The circular reference happens because you use eager loading on the object.
You have a couple of methods:
Turn off eager loading when your loading your Query (linq or lambda)
DbContext.Configuration.ProxyCreationEnabled = false;
Remove the virtual keyword from the Domainmodel
Detach the objects (= no eager loading functionality & no proxy)
Repository.Detach(entityObject)
DbContext.Entry(entityObject).EntityState = EntityState.Detached
Clone the properties
You could use something like AutoMapper to clone the object, don't use the ICloneable interface, because it also clones the ProxyProperties in the object, so that won't work.
In case you are building an API, try using a separte project with a different configuration (that doesn't return proxies)
PS. Proxies is the object that's created by EF when you load it from the Entity Framework. In short: It means that it holds the original values and updated values so they can be updated later. It handles other things to ;-)
For those using the proxy EF/Linq2SQL classes my solution was to simply remove the parent reference on my child entities.
So in my model, I selected the relationship and changed the Parent reference to be Internal rather than Public.
May not be an ideal solution for all, but worked for me.
You can remove the virtual keyword:
public virtual Patient Patient { get; set; } -> public Patient Patient { get; set; }
Keep in mind that when you remove the virtual keyword, lazy loading will be turned off.
I was able to solve this problem by using the method described here:
http://mytechworld.officeacuity.com/index.php/2010/02/serializing-entity-framework-objects-into-json-using-asp-net-mvc/

Categories

Resources