Trying to upgrade to AutoMapper 5.1 from v4.2 and am finding that a collection isn't mapping at runtime - the source object has items in the collection, but the mapped destination property is empty.
Under 4.2, everything worked exactly as expected with the same mapping configuration (save for the MemberList.None in the CreateMap() ctor)
I have DTOs like so
public class GeographicEntity
{
...
}
public class County : GeographicEntity
{
...
}
public class State : GeographicEntity
{
public List<County> Counties { get; } = new List<County>();
}
And viewmodels like so
public class GeographicEntityViewModel
{
...
}
public class CountyViewModel : GeographicEntityViewModel
{
...
}
public class StateViewModel : GeographicEntityViewModel
{
public List<CountyViewModel> Counties { get; } = new List<CountyViewModel>();
}
And Mapping confirmation like so
Mapper.Initialize(configuration =>
{
configuration.CreateMap<GeographicEntity, GeographicEntityViewModel>(MemberList.None);
configuration.CreateMap<County, CountyViewModel>(MemberList.None)
.IncludeBase<GeographicEntity, GeographicEntityViewModel>();
configuration.CreateMap<State, StateViewModel>(MemberList.None)
.IncludeBase<GeographicEntity, GeographicEntityViewModel>();
});
After the Mapper.Map<> call, the Counties collection of the StateViewModel is empty (a list with 0 items) even though the source object has items in its .Counties collection:
var st = new State()
... (initialize the state, including the .Counties list)
var stateViewModel = Mapper.Map<StateViewModel>(st);
Any clues would be appreciated!
After some digging, it turns out that the AutoMapper 5 upgrade introduced some breaking changes. Specifically, the behavior has changed in cases like mine where the destination collection has a getter but no setter. In AutoMapper 4, the default behavior was to use the destination property by default, rather than trying to create a new instance. AutoMapper 5 does NOT do that by default.
The solution is to tell AutoMapper to use the destination value explicitly:
.ForMember(dest => dest.Counties, o => o.UseDestinationValue())
I'm sure there's a good reason for introducing a breaking change like this, but it causes no end of heartache when you've implemented a broad pattern and now have to hunt down and fix every mapped object that might be affected by this change.
I'm almost tempted to just bail on the upgrade and stick with Automapper 4.2, as it did exactly what I needed it to without a lot of extra and unnecessary configuration required.
For more detail, refer to https://github.com/AutoMapper/AutoMapper/issues/1599
Related
I would like to ask how to update row that previously was stored as some specific type and should be updated to another specific type.
Let's assume I have configured Entity Framework to use Table Per Hierarchy inheritance strategy. Now, let's say I have these classes:
abstract class Package
{
public string SomeSharedValue { get; private set; }
}
class PublicPackage : Package
{
public int SomeProperty1 { get; private set; }
public Package TurnIntoPrivatePackage(int someProperty2)
{
return new PrivatePackage(someProperty2);
}
}
class PrivatePackage : Package
{
public int SomeProperty2 { get; private set; }
public Package TurnIntoPublicPackage(int SomeProperty1)
{
return new PublicPackage(SomeProperty1);
}
}
and I have configured my model in such a way:
modelBuilder.Entity<Package>(m =>
{
m.HasDiscriminator<int>("Type")
.HasValue<PublicPackage>(1)
.HasValue<PrivatePackage>(2);
});
so right now, how do I turn (update) let's say PublicPackage into PrivatePackage
Would it work if I do something like:
public async Task DoSomething(DbContext dbContext, Guid packageId){
var package = dbContext.Packages.SingleOrDefaultAsync(f => f.Id == packageId);
//now package is of type PublicPackage
var updatedPackage = package.TurnIntoPrivatePackage(someValue)
//updated package has the same Id and other values setted for private package right now but it's new (another) instance with the same id.
dbContext.Update(updatedPackage); // Can I do this? should I detach the previous instance?
await dbContext.SaveChangesAsync()
}
Ok, I've got enough of an idea now what you're trying to accomplish.
Problem
You'd like to use table per hierarchy to abstract 2 different types of packages using EF, and you want to know how to change from one package type to another, or in database terms, set the discriminator value to the new value and update the object accordingly.
Solution
You can't directly or explicitly set the discriminator to another value. The github issue here will explain further: https://github.com/dotnet/efcore/issues/7510
In the issue above you can go with their example to work around that to set the discriminator explicitly by setting it meta data property in the model builder. But direct transmutation from one object to another is not supported. Doesn't appear like they'll ever support it.
You'd have to add the following code:
modelBuilder.Entity<Package>()
//EF Core version 2.0.0 syntax
.Property("Type").Metadata.AfterSaveBehavior = PropertySaveBehavior.Save
I'm not sure if I'd go this route though unless I was dealing with data that I new was going to be static as far as its types. This problem would make me rethink the approach in this use case.
I'm using Entity Framework 6.0.2 to map some simple hand-coded models to an existing database structure. The primary model at the moment is:
public class Occurrence
{
public int ID { get; set; }
public Guid LegacyID { get; set; }
public string Note { get; set; }
public virtual ICollection<OccurrenceHistory> History { get; set; }
}
(The OccurrenceHistory model isn't really relevant to this, but that part is working fine whereby EF loads up the child records for this model.)
The mapping is simple, and I try to be as explicit as I can be (since as the application grows there will be some less-intuitive mapping):
public class OccurrenceMap : EntityTypeConfiguration<Occurrence>
{
public OccurrenceMap()
{
ToTable("Occurrence");
HasKey(o => o.ID);
Property(o => o.ID).IsRequired().HasColumnName("ID");
Property(o => o.LegacyID).IsRequired().HasColumnName("LegacyID");
Property(o => o.Note).IsUnicode().IsOptional().HasColumnName("Note");
}
}
But if I add a private property to the model, EF tries to map it to the database. Something like this:
private OccurrenceHistory CurrentHistory { get; set; }
(Internal to the model I would have some logic for maintaining that field, for other private operations.) When EF generates a SELECT statement it ends up looking for a column called CurrentHistory_ID which of course doesn't exist.
I can make the property public and set the mapping to ignore it:
Ignore(o => o.CurrentHistory);
But I don't want the property to be public. The model is going to internally track some information which the application code shouldn't see.
Is there a way to tell EF to just ignore any and all private members? Even if it's on a per-map basis? I'd particularly like to do this without having to add EF data annotations to the models themselves, since that would not only be a bit of a leaky abstraction (persistence-ignorant models would then have persistence information on them) but it would also mean that the domain core assembly which holds the models would carry a reference to EntityFramework.dll everywhere it goes, which isn't ideal.
A colleague pointed me to a blog post that led to a very practical approach.
So what I have is a private property:
private OccurrenceHistory CurrentHistory { get; set; }
The core of the problem is that I can't use that in my mapping:
Ignore(o => o.CurrentHistory);
Because, clearly, the property is private and can't be accessed in this context. What the blog post suggests is exposing a public static expression which references the private property:
private OccurrenceHistory CurrentHistory { get; set; }
public static readonly Expression<Func<Occurrence, OccurrenceHistory>> CurrentHistoryExpression = o => o.CurrentHistory;
I can then reference that in the mapping:
Ignore(Occurrence.CurrentHistoryExpression);
As with anything, it's a mix of pros and cons. But in this case I think the pros far outweigh the cons.
Pros:
The domain core assembly doesn't need to carry a reference to EntityFramework.dll.
The persistence mapping is entirely encapsulated within the DAL assembly.
Cons:
Models need to expose a little information about their inner workings.
The con breaks encapsulation, but only slightly. Consuming code still can't access that property or its value on instances, it can only see that the property exists statically. Which, really, isn't a big deal, since developers can see it anyway. I feel that the spirit of encapsulation is still preserved on any given instance of the model.
Just as mentioned in this post, I am getting a Json serialization error while serializing an Entity Framework Proxy:
A circular reference was detected while serializing an object of type
'System.Data.Entity.DynamicProxies.PurchaseOrder_446B939192F161CDBC740067F174F7A6059B0F9C0EEE68CD3EBBD63CF9AF5BD0'.
But the difference is, I don't have a circular reference in my entities, and it only occurs in our production environment. Locally everything works fine...
My Entities:
public interface IEntity
{
Guid UniqueId { get; }
int Id { get; }
}
public class Entity : IEntity
{
public int Id { get; set; }
public Guid UniqueId { get; set; }
}
public class PurchaseOrder : Entity
{
public string Username { get; set; }
public string Company { get; set; }
public string SupplierId { get; set; }
public string SupplierName { get; set; }
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
}
public class PurchaseOrderLine : Entity
{
public string Code { get; set; }
public string Name { get; set; }
public decimal Quantity { get; set; }
}
The GetCurrent action on my PurchaseOrderController throwing the exception:
public class PurchaseOrderController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public PurchaseOrderController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public JsonResult GetCurrent()
{
return Json(EnsurePurchaseOrder(), JsonRequestBehavior.AllowGet);
}
private PurchaseOrder EnsurePurchaseOrder()
{
var company = RouteData.GetRequiredString("company");
var repository = _unitOfWork.GetRepository<PurchaseOrder>();
var purchaseOrder = repository
.Include(p => p.Lines)
.FirstOrDefault
(
p => p.Company == company &&
p.Username == User.Identity.Name
);
if (purchaseOrder == null)
{
purchaseOrder = repository.Create();
purchaseOrder.UniqueId = Guid.NewGuid();
purchaseOrder.Company = company;
purchaseOrder.Username = User.Identity.Name;
_unitOfWork.SaveChanges();
}
return purchaseOrder;
}
}
Option 1 (recommended)
Try turning off Proxy object creation on your DbContext.
DbContext.Configuration.ProxyCreationEnabled = false;
Typically this scenario is because the application is using POCO objects (Either T4 Generated or Code-First). The problem arises when Entity Framework wants to track changes in your object which is not built into POCO objects. To resolve this, EF creates proxy objects which lack the attributes in the POCO objects, and aren't serializable.
The reasons why I recommend this approach; using a website means that you probably don't need change tracking (stateful) on Entity Framework objects, it free's up memory and cpu because change tracking is disabled and it will work consistantly on all your objects the same way.
Option 2
Use a serializer (like JSON.Net which is already included in ASP.Net 4) that allows customization to serialize the object(s).
The reasons I do not recommend this approach is that eventually custom object serialization logic will be need to serial proxy objects as other objects types. This means you have a dependency on logic to deliver a result downstream. Changing the object means changing logic, and in an ASP.Net MVC project (any version) instead of only changing a View you have some thing else to change that is not readily known outside of whoever wrote the logic first.
Option 3 (Entity Framework 5.x +)
Use .AsNoTracking() which will disable the proxy objects on the specific query. If you need to use change tracking, this allows a nice intermediate solution to solution #1.
Your POCO entities are perfectly serializable. Your problem is that the dynamic proxies the EF runtime creates for you are usually not. You can set the context.Configuration.ProxyCreationEnabled to false but then you lose lazy loading. My strong recommendation to you is to use Json.NET which supports serialization for EF entities:
ADO.NET Entity Framework support accidently added to Json.NET
Popular high-performance JSON framework for .NET
I spent countless hours attempting all of the various solutions I found scattered throughout the web, including:
[JsonIgnore]
Internal Getters
Disabling LazyLoadingEnabled and ProxyCreationEnabled
Setting ReferenceLoopHandling to "ignore"
Carefully using explicit loading where needed
All of which ultimately proved fruitless for me. Ignoring a property helped one query, but hurt 3 others. It felt like the programming equivalent to whack-a-mole.
The context of my problem was that the data going in an out of my application had to be JSON. No way around it. Inserts and updates obviously pose much less of a problem. But selecting data that's stored in a normalized database (and in my case including a version history) to be serialized is a nightmare.
The solution:
Return the data (properties) you need as anonymous objects.
A code example:
In this case I needed the 3 latest tickets, based on "Date Scheduled". But also needed several properties stored in related entities.
var tickets =
context.TicketDetails
.Where(t => t.DateScheduled >= DateTime.Now)
.OrderBy(t => t.DateScheduled)
.Take(3)
.Include(t => t.Ticket)
.Include(t => t.Ticket.Feature)
.Include(t => t.Ticket.Feature.Property)
.AsEnumerable()
.Select(
t =>
new {
ID = t.Ticket.ID,
Address = t.Ticket.Feature.Property.Address,
Subject = t.Ticket.Subject,
DateScheduled = String.Format("{0:MMMM dd, yyyy}", t.DateScheduled)
}
);
And voila, no self referencing loops.
I realize this situation may not be adequate in all cases given that entities and objects may change. But it's certainly worth some consideration if all else fails.
Whatever classes have the reference of other class just add attribute like this
[Newtonsoft.Json.JsonIgnoreAttribute]
public virtual ICollection<PurchaseOrderLine> Lines { get; set; }
Now everything work smooth
I was having the same issue, what I have done is have passed only needed column to view , In my case. only 2.
List<SubCategory> lstSubCategory = GetSubCateroy() // list from repo
var subCategoryToReturn = lstSubCategory.Select(S => new { Id = S.Id, Name = S.Name });
return this.Json(subCategoryToReturn , JsonRequestBehavior.AllowGet);
I had the same error, however I saw it both on production server and locally. Changing the DbContext configuration didn't quite solve my issue. A different solution was presented to me with the
[IgnoreDataMember]
attribute on DB entity references. See the post here if this sounds more pertinent to your issue.
ASP.NET Web API Serialized JSON Error: "Self Referencing loop"
In your DbContext class, add this line of code:
this.Configuration.ProxyCreationEnabled = false;
For example:
public partial class EmpDBEntities : DbContext
{
public EmpDBEntities()
: base("name=EmpDBEntities")
{
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Department> Departments { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
}
The circular reference happens because you use eager loading on the object.
You have 3 methods:
Turn off eager loading when your loading your Query (linq or lambda)
DbContext.Configuration.ProxyCreationEnabled = false;
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 ;-)
I had the same problem and resolved it by un-checking Json.NET in the project Extensions in the Reference Manager.
(see the image http://i.stack.imgur.com/RqbXZ.png)
I also had to change the project.csproj file to map the correct path for the new version:
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
and still had to configure the web.config
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
Note that in the web.config file I was forced to refer to the OLDER (6.0.0.0) version though the installed version was 6.0.5.
Hope it helps!
I have quite a bit of entities and so far, I've been doing stuff like
Mapper.CreateMap<Employee, EmployeeDetailsDTO>()
.ForSourceMember(mem => mem.NewsPosts, opt => opt.Ignore());
I want to tell AutoMapper to simply ignore missing properties in the destination object without having to specify each of them. So far, I haven't found a way to do so with my multiple SO and Google searches. Anyone has a solution? I'm ready to do some sort of loop or anything, as long as it can be written once and that it will scale with model / dto changes or added properties.
When are you getting the error? Is it when you call AssertConfigurationIsValid ?
If yes, then simply dont call this method
You dont have to call this method, consider the following mapping which works:
public class Foo1
{
public string Field1 { get; set; }
}
public class Foo2
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Mapper.CreateMap<Foo1, Foo2>();
var foo1 = new Foo1() {Field1 = "field1"};
var foo2 = new Foo2();
Mapper.Map(foo1, foo2);//maps correctly, no Exception
You may want to call AssertConfigurationIsValid for other mappings to ensure they are correct so instead what you need to do is organize your mappings into Profiles:
public class MyMappedClassesProfile: Profile
{
protected override void Configure()
{
CreateMap<Foo1, Foo2>();
//nb, make sure you call this.CreateMap and NOT Mapper.CreateMap
//I made this mistake when migrating 'static' mappings to a Profile.
}
}
Mapper.AddProfile<MyMappedClassesProfile>();
and then if you decide you want to check the validity of the mapping (case by case basis in your situation) then call
Mapper.AssertConfigurationIsValid(typeof(MyMappedClassesProfile).FullName);
important in your case and/or any case where you dont call AssertConfigurationIsValid you should use something like AutoFixture and a Unit Test to ensure your mapping is working. (which is the intent of AssertConfigurationIsValid)
Suggested in wal's answer "don't call AssertConfigurationIsValid()" is not safe, as it will hide potential errors in mappings.
It's better to explicitly ignore mapping between classes, for which you are sure that all needed properties already mapped correctly. You can use extensions created in AutoMapper: "Ignore the rest"? answer:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Src, Dest>();
cfg.IgnoreUnmapped<Src, Dest>(); // Ignores unmapped properties on specific map
});
The overload without parameters cfg.IgnoreUnmapped(this IProfileExpression profile)
ignores unmapped properties on all maps and not recommended, because it also hides any potential problems for all classes.
If I have many classes with many properties to ignore, I don't want to have exception when call AssertConfigurationIsValid(), but prefer to report it in a log and just review are all unmapped properties missed intentionally.
Because method to do validation is not exposed by AutoMapper, I catch AssertConfigurationIsValid and return error message as string.
public string ValidateUnmappedConfiguration(IMapper mapper)
{
try
{
mapper.ConfigurationProvider.AssertConfigurationIsValid();
}
catch (AutoMapperConfigurationException e)
{
return e.Message;
}
return "";
}
I am calling the ValidateUnmappedConfiguration method from unit test
[TestMethod]
public void LogUmmappedConfiguration()
{
var mapper = new MapperConfiguration((cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
})).CreateMapper();
var msg=ValidateUnmappedConfiguration(mapper) ;
if (!msg.IsNullOrBlank())
{
TestContext.WriteString("Please review the list of unmapped fields and check that it is intentional: \n"+msg);
}
}
I have a simple mapping and it is working but it's not filling in Output.Details.
I am a bit confused, I think it maybe because I am using the source as "Task" for each one.
Mapper.CreateMap<Task, Output>();
Mapper.CreateMap<Task, Output.Details>().ForMember(
dest => dest.Item, opt => opt.MapFrom(src => src.Name));
As far as i know i have to create 2 maps, 1 for the object and 1 for object contained within.
Problem is the source for the OUTPUT and OUTPUT.DETAILS can be found in TASK
I tried delving into Details within the first map and specifying Mapfrom but it gives the following error which is why i must create 2 maps
must resolve to top-level member. Parameter name: lambdaExpression error
IList<Task> tempItems= GetItems();
IList<Output> items =
Mapper.Map<IList<Task>, IList<Output>>(tempItems);
The map works but my property "Item" availble in Output.Details is NULL
What am I doing wrong? Here is my Destination object.
It fills in Name no problem, but nothing inside DETAILS... they are left NULL.
Task is not my class, but I checked it and all values are there to be copied hence Tag has a value and is a STRING.
public class Output
{
public string Name { get; set; }
public Details Summary { get; private set; }
public class Details
{
public string Item{ get; set; }
}
public Output()
{
Summary = new Details();
}
}
EDIT
Here is an example of the Task class.
EDIT
They is a sample vs 2010 project here and it shows exactly the problem.
http://dl.dropbox.com/u/20103903/AutomapperNotWorking.zip
and here is an image showing the issue, as you can see Summary Item is "NULL" but it should contain the NAME from Task.
First off, always use Mapper.AssertConfigurationIsValid(); to make sure your mapping configuration is valid. I added it to your code and it immediately highlighted the problem: You didn't tell Automapper what to do with the Summary property. Since Task doesn't contain a property called Summary, Automapper needs to know what to do with it.
So the problem isn't really how to map a nested class, you just need to tell Automapper what to do with Summary. Here's the Automapper configuration that works for your example:
Mapper.CreateMap<Task, Output>()
.ForMember(d => d.Summary, o => o.MapFrom(t => new Output.Details {Item = t.Name}));
Mapper.AssertConfigurationIsValid();
That's all you need.
for the new version, it can be performed as follow:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UdtDevQuestion, QuestionViewModel>();
});
config.AssertConfigurationIsValid();