AutoMapper and .NET Core: Profile is not effective - .net-core

In an ASP.NET Core application I have a profile class (for AutoMapper) like this:
public class CandidateProfile : AutoMapper.Profile
{
public CandidateProfile()
{
CreateMap<Job.Candidate, Job.Candidate>()
.ForMember(x => x.Id, y => y.UseDestinationValue());
}
}
In Startup.cs I registerAutoMapper with DI like this:
services.AddAutoMapper(c =>
{
c.AddProfile<JobProfile>();
c.AddProfile<CandidateProfile>();
c.AddProfile<ApplicationProfile>();
}
My aim is to not change the value of the Id property in the destination object. However the destination Id always gets set to 0 which is the value of the source object.
existingCandidate = _mapper.Map(app.Candidate, existingCandidate);
After calling this code, existingCandidate.Id is 0.
What am I doing wrong?

My aim is to not change the value of the Id property in the
destination object.
So, basically for Id, you don't want the source property to be mapped to the destination property. You want it left alone. Simply Ignore the mapping for that property then -
CreateMap<Candidate, Candidate>()
.ForMember(x => x.Id, y => y.Ignore());
Edit :
Following is the code that is working for me with above configuration (version 10.0) -
public void Test()
{
Candidate appCandidate = new Candidate { Id = 0, Name = "alice" };
Candidate existingCandidate = new Candidate { Id = 4, Name = "bob" };
existingCandidate = _Mapper.Map(appCandidate, existingCandidate);
}

Related

Seeding many-to-many databases in EFCore5 with ModelBuilder?

There are many questions about seeding many-to-many relationships in Entity Framework. However, most of them are extremely old, and many-to-many behavior has changed significantly in EFCore5. The official docs recommend overriding OnModelCreating to implement ModelBuilder.Entity<>.HasData().
However, with the new many-to-many behavior (without explicit mappings), I can find no clear path to seed the intermediate tables. To use the example of this tutorial, the BookCategories class is now implicit. Therefore, there is no path to explicitly declare the intermediate table values while seeding.
I've also tried simply assigning the arrays, e.g.,:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public ICollection<Category> Categories { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public ICollection<Book> Books { get; set; }
}
And then at seed time:
Book book = new Book() { BookId = 1, Title = "Brave New World" }
Category category = new Category() { CategoryId = 1, CategoryName = "Dystopian" }
category.Books = new List<Book>() { book };
book.Categories = new List<Category>() { category };
modelBuilder.Entity<Book>().HasData(book);
modelBuilder.Entity<Category>().HasData(category);
... but there are no entries created for BookCategories in the resulting migration. This was somewhat expected, as this article suggests that one must explicitly seed the intermediate table. What I want is something like this:
modelBuilder.Entity<BookCategory>().HasData(
new BookCategory() { BookId = 1, CategoryId = 1 }
);
However, again, since there is no concrete class to describe BookCategories in EFCore5, the only way I can think of to seed the table is to manually edit the migration with additional MigrationBuilder.InsertData commands, which rather defeats the purpose of seeding data via application code.
However, again, since there is no concrete class to describe BookCategories in EFCore5
Actually, as explained in the What's new link, EF Core 5 allows you to have explicit join entity
public class BookCategory
{
public int BookId { get; set; }
public EBook Book { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
and configure the many-to-many relationship to use it
modelBuilder.Entity<Book>()
.HasMany(left => left.Categories)
.WithMany(right => right.Books)
.UsingEntity<BookCategory>(
right => right.HasOne(e => e.Category).WithMany(),
left => left.HasOne(e => e.Book).WithMany().HasForeignKey(e => e.BookId),
join => join.ToTable("BookCategories")
);
This way you can use all normal entity operations (query, change tracking, data model seeding etc.) with it
modelBuilder.Entity<BookCategory>().HasData(
new BookCategory() { BookId = 1, CategoryId = 1 }
);
still having the new many-to-many skip navigations mapping.
This is probably the simplest as well as the type-safe approach.
In case you thing it's too much, using the conventional join entity is also possible, but you need to know the shared dictionary entity type name, as well as the two shadow property names. Which as you will see by convention might not be what you expect.
So, by convention the join entity (and table) name is
{LeftEntityName}{RightEntityName}
and the shadow property (and column) names are
{LeftEntityNavigationPropertyName}{RightEntityKeyName}
{RightEntityNavigationPropertyName}{LeftEntityKeyName}
The first question would be - which is the left/right entity? The answer is (not documented yet) - by convention the left entity is the one which name is less in alphabetical order. So with your example Book is left, Category is right, so the join entity and table name would be BookCategory.
It can be changed adding explicit
modelBuilder.Entity<Category>()
.HasMany(left => left.Books)
.WithMany(right => right.Categories);
and now it would be CategoryBook.
In both cases the shadow property (and column) names would be
CategoriesCategoryId
BooksBookId
So neither the table name, nor the property/column names are what you'd normally do.
And apart from the database table/column names, the entity and property names are important because you'd need them for entity operations, including the data seeding in question.
With that being said, even if you don't create explicit join entity, it's better to configure fluently the one created automatically by EF Core convention:
modelBuilder.Entity<Book>()
.HasMany(left => left.Categories)
.WithMany(right => right.Books)
.UsingEntity("BookCategory", typeof(Dictionary<string, object>),
right => right.HasOne(typeof(Category)).WithMany().HasForeignKey("CategoryId"),
left => left.HasOne(typeof(Book)).WithMany().HasForeignKey("BookId"),
join => join.ToTable("BookCategories")
);
Now you can use the entity name to access the EntityTypeBuilder
modelBuilder.Entity("BookCategories")
and you can seed it similar to normal entities with shadow FK properties with anonymous type
modelBuilder.Entity("BookCategory").HasData(
new { BookId = 1, CategoryId = 1 }
);
or for this specific property bag type entity, also with Dictionary<string, object> instances
modelBuilder.Entity("BookCategory").HasData(
new Dictionary<string, object> { ["BookId"] = 1, ["CategoryId"] = 1 }
);
Update:
People seem to misinterpret the aforementioned "extra" steps and find them redundant and "too much", not needed.
I never said they are mandatory. If you know the conventional join entity and property names, go ahead directly to the last step and use anonymous type or Dictionary<string, object>.
I already explained the drawbacks of taking that route - loosing the C# type safety and using "magic" strings out of your control. You have to be smart enough to know the exact EF Core naming conventions and to realize that if you rename class Book to EBook the new join entity/table name will change from "BookCategory" to "CategoryEBook" as well as the order of the PK properties/columns, associated indexes etc.
Regarding the concrete problem with data seeding. If you really want to generalize it (OP attempt in their own answer), at least make it correctly by using the EF Core metadata system rather than reflection and assumptions. For instance, the following will extract these names from the EF Core metadata:
public static void HasJoinData<TFirst, TSecond>(
this ModelBuilder modelBuilder,
params (TFirst First, TSecond Second)[] data)
where TFirst : class where TSecond : class
=> modelBuilder.HasJoinData(data.AsEnumerable());
public static void HasJoinData<TFirst, TSecond>(
this ModelBuilder modelBuilder,
IEnumerable<(TFirst First, TSecond Second)> data)
where TFirst : class where TSecond : class
{
var firstEntityType = modelBuilder.Model.FindEntityType(typeof(TFirst));
var secondEntityType = modelBuilder.Model.FindEntityType(typeof(TSecond));
var firstToSecond = firstEntityType.GetSkipNavigations()
.Single(n => n.TargetEntityType == secondEntityType);
var joinEntityType = firstToSecond.JoinEntityType;
var firstProperty = firstToSecond.ForeignKey.Properties.Single();
var secondProperty = firstToSecond.Inverse.ForeignKey.Properties.Single();
var firstValueGetter = firstToSecond.ForeignKey.PrincipalKey.Properties.Single().GetGetter();
var secondValueGetter = firstToSecond.Inverse.ForeignKey.PrincipalKey.Properties.Single().GetGetter();
var seedData = data.Select(e => (object)new Dictionary<string, object>
{
[firstProperty.Name] = firstValueGetter.GetClrValue(e.First),
[secondProperty.Name] = secondValueGetter.GetClrValue(e.Second),
});
modelBuilder.Entity(joinEntityType.Name).HasData(seedData);
}
Also here you don't need to know which type is "left" and which is "right", neither requires special base class or interface. Just pass sequence of entity pairs and it will properly seed the conventional join entity, e.g. with OP example, both
modelBuilder.HasJoinData((book, category));
and
modelBuilder.HasJoinData((category, book));
would do.
Update (EF Core 5.0.2)
It's working well using the name of the associative table:
builder.Entity("ContractDeclarationType").HasData(
new { ContractsId = 1L, DeclarationTypesId = 1L },
new { ContractsId = 1L, DeclarationTypesId = 2L },
new { ContractsId = 1L, DeclarationTypesId = 3L });
I ended up whipping up a generic solution to this problem based upon the answer from Ivan (thanks!). I'm now able to seed all my M2M tables with this syntax:
// Add book1 and book2 to category1:
modelBuilder.HasM2MData(new [] { book1, book2 }, new [] { category1 });
This may not be fully robust, but it should work with conventional M2M mappings.
It makes some assumptions:
T1 & T2 Inherit from some ModelBase that provides an Id property.
T1 & T2 Have exactly one ICollection<OtherType> property.
You know the correct order (which model is T1 and which is T2) — this can be discovered by running the migration for the tables first and inspecting the migration.
You're running EFCore5 RC2 or later (see this issue).
public static void HasM2MData<T1, T2>
(this ModelBuilder mb, T1[] t1s, T2[] t2s)
where T1 : ModelBase where T2 : ModelBase
{
string table = $"{typeof(T1).Name}{typeof(T2).Name}";
PropertyInfo t1Prop = GetM2MProperty<T1, T2>();
PropertyInfo t2Prop = GetM2MProperty<T2, T1>();
string t1Key = $"{t1Prop.Name}Id";
string t2Key = $"{t2Prop.Name}Id";
foreach (T1 t1 in t1s) {
foreach (T2 t2 in t2s) {
mb.Entity(table).HasData(new Dictionary<string, object>() { [t2Key] = t1.Id, [t1Key] = t2.Id });
}
}
}
// Get a property on T1 which is assignable to type ICollection<T2>, representing the m2m relationship
private static PropertyInfo GetM2MProperty<T1, T2>() {
Type assignableType = typeof(ICollection<T2>);
List<PropertyInfo> props = typeof(T1).GetProperties()
.Where(pi => pi.PropertyType.IsAssignableTo(assignableType))
.ToList();
if (props.Count() != 1) {
throw new SystemException(
$"Expected {typeof(T1)} to have exactly one column of type {assignableType}; got: {props.Count()}");
}
return props.First();
}
In the migration, we see something like:
migrationBuilder.InsertData(
table: "BookCategory",
columns: new[] { "BooksId", "CategoriesId" },
values: new object[,]
{
{ "book1", "category1" },
{ "book2", "category1" }
});

AutoMapper 8.0 missing GetPropertyMaps

Prior to AutoMapper 8.0, I used this code to find a property mapping by string, example: entity model has property named "currency_id" and DTO has property named "currency". I have defined bi-directional mapping in AutoMapper, and I used this code to find source/target property relat
public static string GetDestinationPropertyFor<TSrc, TDst>(IMapper IMapper, string sourceProperty)
{
var mapper = AutoMapper.IMapper.ConfigurationProvider;
// TSrc = source generic type
// TDst = destination generic type
var map = mapper.FindTypeMapFor<TSrc, TDst>();
var propertyMap = map.GetPropertyMaps()
.FirstOrDefault(pm =>
pm.SourceMember.Name == sourceProperty
);
return propertyMap.DestinationProperty.Name;
}
In AutoMapper Profile:
this.CreateMap<EntityModels.contact, DTO.contact>()
.ForMember(m => m.currency, src => src.MapFrom(f => f.currency_id))
;
this.CreateMap<DTO.contact, EntityModels.contact>()
.ForMember(m => m.currency_id, src => src.MapFrom(f => f.currency))
;
When I called my method like this:
var _dboField = GetDestinationPropertyFor<DTO.contact, EntityModels.contact>(this.mapper, "currency");
Console.WriteLine(_dboField);
// output should be "currency_id"
After upgrading to AutoMapper 8.0 I got this error at build:
'TypeMap' does not contain a definition for 'GetPropertyMaps' and no accessible extension method 'GetPropertyMaps' accepting a first argument of type 'TypeMap' could be found (are you missing a using directive or an assembly reference?)
What are replacements for GetPropertyMaps in AutoMapper 8.0?
Thanks!
As Lucian suggested, MemberMaps is a possible replacement. However, PropertyMaps does exactly the same as GetPropertyMaps in AutoMapper 7.0. DestinationProperty has also been renamed to DestinationMember.
AutoMapper 7.0 code:
public static string GetDestinationPropertyFor<TSrc, TDst>(IMapper IMapper, string sourceProperty)
{
var mapper = AutoMapper.IMapper.ConfigurationProvider;
// TSrc = source generic type
// TDst = destination generic type
var map = mapper.FindTypeMapFor<TSrc, TDst>();
var propertyMap = map.GetPropertyMaps()
.FirstOrDefault(pm =>
pm.SourceMember.Name == sourceProperty
);
return propertyMap.DestinationProperty.Name;
}
AutoMapper 8.0 code:
public static string GetDestinationPropertyFor<TSrc, TDst>(IMapper IMapper, string sourceProperty)
{
var mapper = AutoMapper.IMapper.ConfigurationProvider;
// TSrc = source generic type
// TDst = destination generic type
var map = mapper.FindTypeMapFor<TSrc, TDst>();
var propertyMap = map.PropertyMaps
.FirstOrDefault(pm =>
pm.SourceMember.Name == sourceProperty
);
return propertyMap.DestinationMember.Name;
}

How to get the reference to all Entity Framework EntityTypeConfiguration

I am working with Entity Framework 6 and ASP.NET and I come across this problem:
suppose I have these 2 entity mappings configured on 2 class model A,B:
class A { string property1 { get; set }, string property2 {get; set} }
class B { string property1 { get; set }, string property2 {get; set} }
public class AConfiguration : EntityTypeConfiguration<A> {
ToTable("TableA");
Property( x => x.propperty1 ).maxLength(10).HasColumnName("Column1");
Property( x => x.property2 ).maxLength(10).HasColumnName("Column2");
}
public class BConfiguration : EntityTypeConfiguration<B> {
ToTable("TableB");
Property( x => x.propperty1 ).maxLength(10).HasColumnName("Column1");
Property( x => x.property2 ).maxLength(10).HasColumnName("Column2");
}
Assume I already finished configuring EF for my projects and reading and writing to DB works perfectly.
Now my question is: Is there a way to retrieve all the properties that we configured above in a dictionary format ?
For example:
//This would return a dictionary that looks somehow like this:
/* {
"TableA": {
property1: {
maxLength: 10
}
property2: {
maxLength: 10
}
},
"TableB": {
property1: {
maxLength: 10
}
property2: {
maxLength: 10
}
}
}
*/
// I want to look for some method similar to this
var allEntityTypeConfiguration = entityFrameworkConfig.getAllEntityTypeConfiguration()
After All, all I want to retrieve is the list of all properties defined in EntityTypeConfiguration and its configuration ( e.x: maxLength 10, or something like that)
I googled this problem for a while but still did not find any methods provided by EF. Is there a workaround if possible ?
Working code
using (var dbContext = new YourDBEntities())
{
var metadata = ((IObjectContextAdapter)dbContext).ObjectContext.MetadataWorkspace;
var tables = metadata.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.Single()
.BaseEntitySets
.OfType<EntitySet>()
.Where(s => !s.MetadataProperties.Contains("Type")
|| s.MetadataProperties["Type"].ToString() == "Tables");
//tables will be having list of table in your entity framework
foreach (EntitySet table in tables) //foreach on tables list
{
//if you need table name
var tableName = table.MetadataProperties.Contains("Table")
&& table.MetadataProperties["Table"].Value != null
? table.MetadataProperties["Table"].Value.ToString()
: table.Name;
//if you need schema name
var tableSchema = table.MetadataProperties["Schema"].Value.ToString();
//Here are the list of complete columns in that table
var columnDetails = table.ElementType.Members;
//you can get each column properties from above columns list
}
}

GraphDiff: "Cascading" Owned Collections

I'm trying to use GraphDiff (latest available version in NuGet) to handle what I consider a not terribly difficult entity model. Consider a model like so:
class A
{
public virtual ICollection<B> Bs { get; set; }
}
class B
{
public virtual ICollection<C> Cs { get; set; }
}
If I'm updating an instance of A (call it aEntity), I could do:
context.UpdateGraph(aEntity, map =>
map.OwnedCollection(a => a.Bs, withB =>
withB.OwnedCollection(b => b.Cs)))
Now I'd also, sometimes, like to update B's independently.
context.UpdateGraph(bEntity, map => map.OwnedCollection(b => b.Cs));
So I figured I could "cascade" the changes by introducing a property, like so:
class BMapper {
Expression<Func<IUpdateConfiguration<B>, object>> MappingExpression
{
get
{
return map => map.OwnedCollection(b => b.Cs);
}
}
}
... and then use that in both scenarios like so:
// Update an A and any of its B's
context.UpdateGraph(aEntity, map =>
map.OwnedCollection(a => a.Bs, (new BMapper()).MappingExpression))
// Update a B by itself
context.UpdateGraph(bEntity, (new BMapper()).MappingExpression);
Updating the B by itself works fine, but updating an A falls down in expression land:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
'string' does not contain a definition for 'Body'
Is there a way to share mappings in GraphDiff?

Using Moq can you verify a method call with an anonymous type?

I'm trying to verify a method call using Moq, but I can't quite get the syntax right. Currently, I've got this as my verify:
repository.Verify(x => x.ExecuteNonQuery("fav_AddFavorites", new
{
fid = 123,
inputStr = "000456"
}), Times.Once());
The code compiles, but the test fails with the error:
Expected invocation on the mock once, but was 0 times:
x => x.ExecuteNonQuery("fav_AddFavorites", new <>f__AnonymousType0<Int32, String>(123, "000456"))
No setups configured.
Performed invocations:
IRepository.ExecuteNonQuery("fav_AddFavorites", { fid = 123, inputStr = 000456 })
How can I verify the method call and match the method parameters for an anonymous type?
UPDATE
To answer the questions:
I am trying to verify both that the method was called and that the parameters are correct.
The signature of the method I'm trying to verify is:
int ExecuteNonQuery(string query, object param = null);
The setup code is simply:
repository = new Mock<IRepository>();
UPDATE 2
It looks like this is a problem with Moq and how it handles anonymous types in .Net. The code posted by Paul Matovich runs fine, however, once the code and the test are in different assemblies the test fails.
This Passes
public class Class1
{
private Class2 _Class2;
public Class1(Class2 class2)
{
_Class2 = class2;
}
public void DoSomething(string s)
{
_Class2.ExecuteNonQuery(s, new { fid = 123, inputStr = "000456" });
}
}
public class Class2
{
public virtual void ExecuteNonQuery(string s, object o)
{
}
}
/// <summary>
///A test for ExecuteNonQuery
///</summary>
[TestMethod()]
public void ExecuteNonQueryTest()
{
string testString = "Hello";
var Class2Stub = new Mock<Class2>();
Class1 target = new Class1(Class2Stub.Object);
target.DoSomething(testString);
Class2Stub.Verify(x => x.ExecuteNonQuery(testString, It.Is<object>(o => o.Equals(new { fid = 123, inputStr = "000456" }))), Times.Once());
}
##Update##
That is strange, it doesn't work in different assemblies. Someone can give us the long definition about why the object.equals from different assemblies behaves differently, but for different assemblies, this will work, any variance in the object values will return a different hash code.
Class2Stub.Verify(x => x.ExecuteNonQuery(testString, It.Is<object>(o => o.GetHashCode() == (new { fid = 123, inputStr = "000456" }).GetHashCode())), Times.Once());
One option is to "verify" it in a Callback. Obviously this needs to be done at Setup time, e.g.:
aMock.Setup(x => x.Method(It.IsAny<object>())).Callback<object>(
(p1) =>
{
dynamic o = p1;
Assert.That(o.Name, Is.EqualTo("Bilbo"));
});
None of the answers are great when your test assembly is different than the system under test's assembly (really common). Here's my solution that uses JSON serialization and then strings comparison.
Test Helper Function:
using Newtonsoft.Json;
public static class VerifyHelper
{
public static bool AreEqualObjects(object expected, object actual)
{
var expectedJson = JsonConvert.SerializeObject(expected);
var actualJson = JsonConvert.SerializeObject(actual);
return expectedJson == actualJson;
}
}
Example System Under Test:
public void DoWork(string input)
{
var obj = new { Prop1 = input };
dependency.SomeDependencyFunction(obj);
}
Example Unit Test:
var expectedObject = new { Prop1 = "foo" };
sut.DoWork("foo");
dependency.Verify(x => x.SomeDependencyFunction(It.Is<object>(y => VerifyHelper.AreEqualObjects(expectedObject, y))), Times.Once());
This solution is really simple, and I think makes the unit test easier to understand as opposed to the other answers in this thread. However, because it using simple string comparison, the test's anonymous object has to be set up exactly the same as the system under the test's anonymous object. Ergo, let's say you only cared to verify the value of a single property, but your system under test sets additional properties on the anonymous object, your unit test will need to set all those other properties (and in the same exact order) for the helper function to return true.
I created a reusable method based on Pauls answer:
object ItIsAnonymousObject(object value)
{
return It.Is<object>(o => o.GetHashCode() == value.GetHashCode());
}
...
dependency.Verify(
x => x.SomeDependencyFunction(ItIsAnonymousObject(new { Prop1 = "foo" })),
Times.Once());
Also, this can be used for property name case-insensitive comparison:
protected object ItIsAnonymousObject(object value)
{
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
return It.Is<object>(o => JsonSerializer.Serialize(o, options) == JsonSerializer.Serialize(value, options));
}

Resources