EFCore DotNet 5 & Automapper. Map virtual collections - .net-core

Firstly, I am new to Automapper and finding the official documentation poor to say the least.
I am trying to map two complex entities with virtual collections which themselves need to be mapped
Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>>
within each row I need to map
Map<IEnumerable<EntityCollectionItemDb>, IEnumerable<EntityCollectionItemVM>>
A very simplified version of the classes:
public class ComplexEntityDb
{
public int Id;
// Multiple properties (14) removed for brevity
public virtual ICollection<EntityCollectionItemDb> CollectionDb { get; set; }
}
public class ComplexEntityVM
{
public int Id;
// Multiple properties (7) removed for brevity
public virtual ICollection<EntityCollectionItemVM> CollectionDb { get; set; }
}
public class EntityCollectionItemDb
{
public int Id;
// Multiple properties (12) removed for brevity
}
public class EntityCollectionItemVM
{
public int Id;
// Multiple properties (6) removed for brevity
}
What is the correct way to do this with EF Core 5.04 on dotnet 5 using AutoMapper 10.1.1 and DI extensions 8.1.1
I have read dozens of articles on this site and there does not seem to be an easy way to do this.
Many thanks in advance for any help much wiser souls can give.
EDIT: (taken from the comment section of #dglozano's answer) -
I am using mapping profile -
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<ComplexEntityDb, ComplexEntityVM>().ReverseMap();
CreateMap<EntityCollectionItemDb, EntityCollectionItemVM>).ReverseMap();
}
}
and IMapper -
private readonly IMapper _mapper;
public CustomService(IMapper mapper)
{
_mapper = mapper;
}
and trying -
return _mapper.Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>>(source);
Result: EntityCollectionItemVM collection is empty.

You just need to define the mappings of each element type, as the documentation for Nested Mappings suggests. You have to tell Automapper how to map ComplexEntityDb -> ComplexEntityVM and how to map EntityCollectionItemDb -> EntityCollectionItemVM, and then all the mappings of collections of those items will automatically be supported.
So in your case, a minimal example would look like this:
using System;
using AutoMapper;
using System.Collections.Generic;
public class ComplexEntityDb
{
public ComplexEntityDb(int id)
{
Id = id;
var item1 = new EntityCollectionItemDb();
var item2 = new EntityCollectionItemDb();
item1.Id = id * 1000 + 1;
item2.Id = id * 1000 + 2;
CollectionDb = new List<EntityCollectionItemDb>{item1, item2, };
}
public int Id
{
get;
set;
}
// Multiple properties (14) removed for brevity
public ICollection<EntityCollectionItemDb> CollectionDb
{
get;
set;
}
}
public class ComplexEntityVM
{
public int Id;
// Multiple properties (7) removed for brevity
public ICollection<EntityCollectionItemVM> CollectionDb
{
get;
set;
}
}
public class EntityCollectionItemDb
{
public int Id;
// Multiple properties (12) removed for brevity
}
public class EntityCollectionItemVM
{
public int Id;
// Multiple properties (6) removed for brevity
}
public class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ComplexEntityDb, ComplexEntityVM>();
cfg.CreateMap<EntityCollectionItemDb, EntityCollectionItemVM>();
});
var complexEntity1 = new ComplexEntityDb(1);
var complexEntity2 = new ComplexEntityDb(2);
var complexEntity3 = new ComplexEntityDb(3);
var source = new List<ComplexEntityDb>{complexEntity1, complexEntity2, complexEntity3};
var mapper = config.CreateMapper();
var dest = mapper.Map<IEnumerable<ComplexEntityDb>, IEnumerable<ComplexEntityVM>>(source);
foreach(var parentMapped in dest)
{
Console.WriteLine("Mapped parent Id {0}", parentMapped.Id);
foreach(var childMapped in parentMapped.CollectionDb)
{
Console.WriteLine(" - Mapped child Id {0}", childMapped.Id);
}
Console.WriteLine();
}
}
}
Output:
Mapped parent Id 1
- Mapped child Id 1001
- Mapped child Id 1002
Mapped parent Id 2
- Mapped child Id 2001
- Mapped child Id 2002
Mapped parent Id 3
- Mapped child Id 3001
- Mapped child Id 3002
Try it out in this fiddle.
In a more real scenario, it would be the same idea, but I would create a Profile(s) to define each of the mappings configurations and then use the injected IMapper to do execute the mapping.

Related

EF Core with Lazy Loading tracks unreachable Objects?

I am currently having troubles with entity framework core.
The application I am developing is supposed to help users plan their next business year by increasing/decreasing the quantity of a service they want to provide in the next year.
Based on their input the "worth" of a service is distributed pro rata to other "mini-services" that are contained in the changed service.
To do so I load the affected entries of the main service and the "mini-services" from Database via a repository which then uses Entity Framework.
public IEnumerable<OpsDistributionEntry> FilteredOpsDistributionEntries(int catalogId, IEnumerable<OpsDistributionEntry> filterEntries)
{
return _context.OpsDistributionEntries.FromSqlRaw(
$"SELECT * FROM OpsDistributionEntries WHERE (Id IN (SELECT OpsEntriesId FROM DistributionCatalogOpsDistributionEntry WHERE CatalogsId = {catalogId}) " +
$"AND EntityId IN ({string.Join(",", filterEntries.Select(x => x.EntityId))}))").ToList();
}
I then map those database objects to my domain objects via constructor.
var opsDistributionEntries = new OpsDistributionEntriesFromDatabaseObjects(
_repository.FilteredOpsDistributionEntries(_distCatalogId, _filterEntries));
public class OpsDistributionEntriesFromDatabaseObjects : IOpsDistributionEntries
{
private readonly IOpsDistributionEntries _distribution;
public OpsDistributionEntriesFromDatabaseObjects(IEnumerable<DatabaseObjects.OpsDistributionEntry> distribution)
{
_distribution = new OpsDistributionEntries(distribution.Select(x => new OpsDistributionEntryFromDatabaseObject(x)));
}
public IEnumerator<IOpsDistributionEntry> GetEnumerator()
{
return _distribution.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class OpsDistributionEntryFromDatabaseObject : IOpsDistributionEntry
{
public OpsDistributionEntryFromDatabaseObject(DatabaseObjects.OpsDistributionEntry opsDistributionEntry)
: this(opsDistributionEntry.Id, opsDistributionEntry.TotalCases, opsDistributionEntry.TotalEffectiveWeight, opsDistributionEntry.Provide,
opsDistributionEntry.Freeze,
new OpsFromDatabaseObject(opsDistributionEntry.Entity),
new DrgDistributionsFromDatabaseObjects(opsDistributionEntry.DrgDistribution))
{
}
private OpsDistributionEntryFromDatabaseObject(int id, int totalCases, double totalEffectiveWeight, bool provide, bool freeze, IOps ops,
IDrgDistributions drgDistribution)
{
Id = id;
TotalCases = totalCases;
TotalEffectiveWeight = totalEffectiveWeight;
Provide = provide;
Freeze = freeze;
Ops = ops;
DrgDistribution = drgDistribution;
}
public int Id { get; }
public int TotalCases { get; }
public double TotalEffectiveWeight { get; }
public bool Provide { get; }
public bool Freeze { get; }
public IOps Ops { get; }
public IDrgDistributions DrgDistribution { get; }
}
public sealed class OpsFromDatabaseObject : IOps
{
public OpsFromDatabaseObject(DatabaseObjects.Ops ops) : this(ops.Id, ops.Code, ops.Description, ops.Year)
{
}
private OpsFromDatabaseObject(int id, string code, string description, int year)
{
Id = id;
Code = code;
Description = description;
Year = year;
}
public int Id { get; }
public string Code { get; }
public string Description { get; }
public int Year { get; }
}
I pass the database objects on to different levels, but finally every value is assigned and every possible navigation property is mapped to an domain object.
With those mapped domain objects I recalculate the new "worth" of the service and the correlated "mini-services".
After calculation I again map my Domain Objects to DatabaseObjects.
DatabaseObjects.OpsDistributionEntry ToDatabaseObject() => new DatabaseObjects.OpsDistributionEntry
{
Id = Id,
EntityId = Ops.Id,
Freeze = Freeze,
Provide = Provide,
TotalCases = TotalCases,
TotalEffectiveWeight = TotalEffectiveWeight,
DrgDistribution = DrgDistribution.Select(x => x.ToDatabaseObject()).ToImmutableList(),
};
When I want to add those "updated" Objects to the context via repository
public void UpdateDistributionEntries(IEnumerable<OpsDistributionEntry> opsDistributionEntries)
{
if (opsDistributionEntries == null) throw new ArgumentNullException(nameof(opsDistributionEntries));
_context.OpsDistributionEntries.UpdateRange(opsDistributionEntries);
}
I am getting an Error that the Entities I want to updated are already being tracked by Entity Framework.
After some debugging I think that EF is still tracking the database objects I loaded for mapping the domain objects. I just use the database objects to map values to the domain objects and do not store any reference for them (as far as I understand).
Can any of you maybe tell me why they are still being tracked even if they are "unreachable". Or am I thinking wrong? Might this be because of Lazy Loading?
I've been debugging for almost 14 hours now :D Please someone give me a hint :D
Many thanks in advance

Auto Mapper Constructor initialization Mapping Issue

I have the following Mapping configurations:-
Initialized Data:-
private static IEnumerable<Source> InitializeData()
{
var source= new[]
{
new Source("John", "Doe", "1111111111"),
new Source("Jack", "Handsome", "2222222222"),
new Source("Joe", "Mackenze", "3333333333")
};
return source;
}
Source Model:
public class Source
{
private string First { get; set; }
private string Last { get; set; }
private string Phone { get; set; }
public Source(string first, string last, string phone)
{
First = first;
Last = last;
Phone = phone;
}
}
Destination Model
public class Destination
{
public string First { get; set; }
public string Last { get; set; }
public string Phone { get; set; }
}
Main
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
cfg.CreateMap<Source, Destination>().ReverseMap();
});
var mapper = new Mapper(config);
var source= InitializeData();
var people = mapper.Map<IEnumerable<Destination>>(source);
foreach (var p in people)
{
Console.WriteLine("Name: {0}-{1} Phone: {2}", p.First, p.Last, p.Phone);
}
Console.ReadLine();
}
Problem descriptions:
I have been struggled to understand the AutoMapper mapping between source and destination models.
My source model has a constructor to initialize or accept data from outside. It works fine when I removed the source constructor from the model that's mean flat mapping works fine but constructor initialization has the issue. When I debug in VS2019, it shows the number of records but all fields are empty/null.
What is wrong with the above mapping. I have gone through the AutoMapper reference docs but do not get a hold on this issue.
I highly appreciate your help!
Try calling AssertConfigurationIsValid. Check http://docs.automapper.org/en/latest/Configuration-validation.html.
Your Source properties are private. I assume you meant public.

How do you set some properties and leave others as defaults using AutoFixture and AutoMoqCustomization?

I am new to AutoFixture so I hope you can help. How do you set some properties in an object but leave others as the AutoFixture default - while using XUnit's [Theory] attribute and an AutoDataAttribute.
For example, in the contrived Airport example below based on Jason Robert's Pluralsight course, when setting the property (or the Airport object) e.g.
f.Customize<Mock<IAirport>>(c => c.Do(m => m.SetupGet(i => i.code).Returns("NOO")));
the other properties are often null, or I have to manually set them rather than letting AutoFixture do it. I would prefer to have cleaner code where the fixtureFactory sets all the properties for the Airport so that the V2 unit test only passed in a single Airport parameter.
So, within the fixtureFactory
How do you set MULTIPLE properties?
How does one use the default AutoFixture values rather than leaving the uninitialized values as
null?
Thanks!
using AutoFixture;
using AutoFixture.AutoMoq;
using AutoFixture.Xunit2;
using Moq;
using System;
using Xunit;
namespace AirportTesterWithAutoFixture
{
public interface IAirport
{
string city { get; set; }
string code { get; set; }
string country { get; set; }
string name { get; set; }
void CallAirTrafficControl();
}
public class Airport : IAirport
{
public string name { get; set; }
public string city { get; set; }
public string code { get; set; }
public string country { get; set; }
public Airport()
{
}
public Airport(string name, string code, string country, string city)
{
this.name = name;
this.code = code;
this.country = country;
this.city = city;
}
public void CallAirTrafficControl()
{
if (this.country.Equals("Canada") && this.code.StartsWith("Y"))
{
// Send "Bonjour!"();
}
else
{
throw new Exception("Invalid code for Canada");
}
}
}
public class UnitTest1
{
[Fact]
public void V1_Validate_ExceptionThrown_ForInvalidCanadianAirportCode()
{
var fixture = new Fixture();
var sut = fixture.Create<Airport>();
// Overwrite code and country with invalid setting for Canada.
sut.country = "Canada";
sut.code = "NOT";
Assert.ThrowsAny<Exception>(() => sut.CallAirTrafficControl());
}
[Theory]
[AutoMoqInvalidAirportDataAttribute]
public void V2_Validate_ExceptionThrown_ForInvalidCanadianAirportCode(IAirport sut, string name, string city)
{
Airport airport = new Airport(name, sut.code, sut.country, city);
Assert.ThrowsAny<Exception>(() => airport.CallAirTrafficControl());
}
}
// https://stackoverflow.com/questions/58998834/how-to-use-ifixture-buildt-with-automoqcustomization-when-t-is-an-interface
public class AutoMoqInvalidAirportDataAttribute : AutoDataAttribute
{
public static Func<IFixture> fixtureFactory = () =>
{
IFixture f = new Fixture().Customize(new AutoMoqCustomization());
f.RepeatCount = 5;
// How do you set MULTIPLE properties?
// How does one use the default AutoFixture values rather than leaving the uninitialized values as null?
// Can one pass a custom property used earlier in the Fixture creation process to another custom property used later?
f.Customize<Mock<IAirport>>(c => c.Do(m => m.SetupGet(i => i.code).Returns("NOT")));
return f;
};
public AutoMoqInvalidAirportDataAttribute() : base(fixtureFactory)
{
}
}
}
AutoFixture does not populate mock properties by default, but it can be done. These blog posts describe how to do it:
https://blog.ploeh.dk/2013/04/05/how-to-configure-automoq-to-set-up-all-properties/
https://blog.ploeh.dk/2013/04/08/how-to-automatically-populate-properties-with-automoq/
Author of AutoFixture does not recommend this approach, however, as he considers declaration of properties in interfaces a design smell.
I could not find the original discussion about this topic unfortunately, but it is hidden somewhere on StackOverflow in the comments. Maybe you will be able to find it if you go through Mark Seemann's profile.

CamelCaseNamingStrategy vs CamelCasePropertyNamesContractResolver

With JSON.Net, I see 2 different ways of saying that I want my properties to be serialized in camelCase:
CamelCaseNamingStrategy
CamelCasePropertyNamesContractResolver
According to this code snippet, both options give the same result since the assertion does not fail:
public class Bar
{
public int SomeValue { get; set; }
}
public class Foo
{
public Bar Bar { get; set; } = new Bar();
public string AnotherValue { get; set; }
}
[Fact]
public void TestBothOptions()
{
var x = new Foo();
x.AnotherValue = "test";
x.Bar.SomeValue = 12;
var serializerSettingsWithNamingStrategy = new JsonSerializerSettings();
serializerSettingsWithNamingStrategy.ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(),
};
var serializerSettingsWithContractResolver = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var one = JsonConvert.SerializeObject(x, serializerSettingsWithNamingStrategy);
var two = JsonConvert.SerializeObject(x, serializerSettingsWithContractResolver);
Assert.Equal(one, two); // {"bar":{"someValue":12},"anotherValue":"test"}
}
So, does anybody know the difference between the two options?
CamelCaseNamingStrategy is newer and more flexible to use, since you can specify, via attributes, different naming strategies to use for different classes or properties in your model without using a resolver, e.g.:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class MyClass
{
public string CamelCaseProperty1 { get; set; }
public string CamelCaseProperty2 { get; set; }
[JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy)]
public string DefaultCaseProperty { get; set; }
}
You can also create your own custom naming strategy class if you need to.
CamelCasePropertyNamesContractResolver still survives for backward compatibility. After CamelCaseNamingStrategy was introduced, the source code for that resolver was changed to use the strategy. You can see this clearly in this excerpt from the source code:
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
...
public CamelCasePropertyNamesContractResolver()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
...
}
Note there are subtle differences between the CamelCasePropertyNamesContractResolver and the DefaultContractResolver in terms of how they cache information, which may or may not be of concern to you. See Does Json.NET cache types' serialization information? for more information about that.

EF Code First + remove orphans which marked as Modified (IsDeleted = 1)

I have the next problem. My code context + model:
public class MediaPlanContext : DbContext
{
public MediaPlanContext() : base(lazyLoading:false) {}
public DbSet<MediaPlan> MediaPlan { get; set; }
public DbSet<MovieType> MovieType { get; set; }
public DbSet<MediaPlanItem> MediaPlanItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<MediaPlanItem>()
.HasKey(mpi => new {mpi.Id, mpi.MediaPlanId});
modelBuilder
.Entity<MediaPlanItem>()
.Property(mpi => mpi.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder
.Entity<MediaPlan>()
.HasMany(mp => mp.MediaPlanItems)
.WithRequired()
.HasForeignKey(mpi => mpi.MediaPlanId)
.WillCascadeOnDelete();
}
}
public class MediaPlan : IBaseObject
{
public virtual ICollection<MediaPlanItem> MediaPlanItems { get; set; }
}
public class MediaPlanItem : IBaseObject
{
public int MediaPlanId {get;set;}
public MediaPlan MediaPlan {get;set;}
}
public interface IBaseObject
{
public int Id {get;}
public DateTime DateCreated {get;}
public DateTime DateModified {get;set;}
}
Also I use repository to handle with my objects (IBaseObject-s) with root-object MediaPlan.
When object in my DB will become deleted I mark entity (record) as IsDeleted = 1 and I have some logic in my repository class to handle regular delete as update, change EntityState to Modified instead of Deleted.
Problem with the next code:
var rep = new MediaPlanRepository(new MediaPlanContext());
var withItems = rep.GetWithMediaPlanItems();
var m1 = withItems.First();
var mpi1 = m1.MediaPlanItems.First();
m1.MediaPlanItems.Remove(mpi1); // 6 items before remove
// 5 items after remove
rep.SaveChanges();
// 6 items after save changes :(
Question: Can I handle the moment after saveChanges occurs and detach my IsDeleted = 1 entity? Is is resolve my problem?
Remark: Related entities loaded to root object as projection and as Julie says in paragraph 'Scenario When This May Not Work As Expected' can produce problems with entities that is already tracked by context.
Code:
public override int SaveChanges()
{
var result = base.SaveChanges();
// AfterSave code
var isDeletedEntities = EfContext.ChangeTracker
.Entries()
.Select(dbE => new {
DBEntity = dbE,
BaseObject = (dbE.Entity as IBaseObject)})
.Where(dbe => dbe.BaseObject.IsDeleted);
foreach (var isDeletedEntity in isDeletedEntities)
{
isDeletedEntity.DBEntity.State = EntityState.Detached;
}
}

Resources