TB_R_LEAVE_Configuration
public class TB_R_LEAVE_REQ_ID_Configuration : DataUseLogConfiguration<Entities.TB_R_LEAVE_REQ_ID>
{
public TB_R_LEAVE_REQ_ID_Configuration()
{
ToTable("TB_R_LEAVE_REQ_ID");
HasKey(e => e.LEAVE_REQ_ID);
HasRequired<TB_R_LEAVE_REQ>(e => e.TB_R_LEAVE_REQ);
Property(e => e.LEAVE_REQ_ID).HasColumnName("LEAVE_REQ_ID").HasMaxLength(10);
Property(e => e.LEAVE_ID).HasColumnName("LEAVE_ID").HasMaxLength(5);
}
}
DataUseLogConfiguration (abstract base class)
public abstract class DataUseLogConfiguration<T> : EntityTypeConfiguration<T> where T : DataUseLog
{
public DataUseLogConfiguration()
{
Property(e => e.CREATED_BY).HasColumnName("CREATED_BY").HasMaxLength(5).IsRequired();
Property(e => e.CREATED_DT).HasColumnName("CREATED_DT").IsRequired();
Property(e => e.UPDATED_BY).HasColumnName("UPDATED_BY").HasMaxLength(5).IsOptional();
Property(e => e.UPDATED_DT).HasColumnName("UPDATED_DT").IsOptional();
}
}
I want to ignore all DataUseLogConfiguration properties
I tried use Ignore on TB_R_LEAVE_Configuration:
Ignore(e => e.UPDATED_BY);
and this happen:
"The property 'UPDATED_BY' is not a declared property on type
'TB_R_LEAVE_REQ_ID'. Verify that the property has not been explicitly
excluded from the model by using the Ignore method or
NotMappedAttribute data annotation. Make sure that it is a valid
primitive property."
Without Ignore this happen:
"ORA-00904: \"Extent2\".\"UPDATED_BY\": invalid identifier"
The Data Model also use inherited base class
public class TB_R_LEAVE_REQ_ID : DataUseLog
{
public string LEAVE_REQ_ID { get; set; }
public string LEAVE_ID { get; set; }
public virtual TB_M_LEAVE TB_M_LEAVE { get; set; }
public virtual TB_R_LEAVE_REQ TB_R_LEAVE_REQ { get; set; }
}
How i properly ignore properties on base class just using fluent API on TB_R_LEAVE_Configuration (im try to avoid changing the data model)
thanks for everyone who try to help me, btw this is how i solve it with just edit the TB_R_LEAVE_REQ_ID_Configuration
public class TB_R_LEAVE_REQ_ID_Configuration : EntityTypeConfiguration<Entities.TB_R_LEAVE_REQ_ID>
{
public TB_R_LEAVE_REQ_ID_Configuration()
{
ToTable("TB_R_LEAVE_REQ_ID");
HasKey(e => e.LEAVE_REQ_ID);
HasRequired<TB_R_LEAVE_REQ>(e => e.TB_R_LEAVE_REQ);
Ignore(e => e.UPDATED_BY);
Ignore(e => e.UPDATED_DT);
Ignore(e => e.CREATED_BY);
Ignore(e => e.CREATED_DT);
Property(e => e.LEAVE_REQ_ID).HasColumnName("LEAVE_REQ_ID").HasMaxLength(10);
Property(e => e.LEAVE_ID).HasColumnName("LEAVE_ID").HasMaxLength(5);
}
}
notice that i changed the inheritance to EntityTypeConfiguration then just add the ignores as usual
Hope this help everyone in the future
Related
I'm getting the following exception thrown when attempting to include a navigation property for a derived type:
System.InvalidOperationException: Invalid include.
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.PopulateIncludeTree(IncludeTreeNode includeTreeNode, Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.ProcessInclude(NavigationExpansionExpression source, Expression expression, Boolean thenInclude)
at Microsoft.EntityFrameworkCore.Query.Internal.NavigationExpandingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
...
Using the following simplified class structure:
public abstract class BaseJob
{
public int Id { get; set; }
}
public abstract class WorkerJob : BaseJob
{
public Person Person { get; set; }
}
public class PaintingJob : WorkerJob
{
public string Color { get; set; }
}
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
When querying via the DbContext as documented here:
// Throws InvalidOperationException
var jobs = await _context.BaseJobs
.Include(job => (job as WorkerJob).Person) // WorkerJob is abstract
.ToListAsync();
This must be related to the navigation property existing on an abstract class as adjusting the query to the following works as intended:
// Works as intended
var jobs = await _context.BaseJobs
.Include(job => (job as PaintingJob).Person) // PaintingJob is concrete
.ToListAsync();
However, I have many concrete derived classes and I don't want to have to change the query every time a new concrete class is added. Could this be a bug with EF Core, or am I doing something wrong?
Absolutely not a bug, the error was mine. I had misconfigured the context as follows:
modelBuilder.Entity<PaintingJob>(entity =>
{
entity.HasBaseType<WorkerJob>();
entity.HasOne(x => x.Person)
.WithMany()
.HasForeignKey(x => x.PersonId);
});
Changing this to use the abstract class when defining the navigation property resolves the exception being thrown and returns results as expected.
modelBuilder.Entity<WorkerJob>(entity =>
{
entity.HasBaseType<BaseJob>();
entity.HasOne(x => x.Person)
.WithMany()
.HasForeignKey(x => x.PersonId);
});
We are mapping domain hierarchy to the Dto hierarchy. Some of the properties of the domain base class get flattened. We are using ReverseMap to simplify the mapping back to domain from dto.
We've mapped Contained.Id do ContainedId of Dto and ignored the path for the reverse map.
What we were expecting, that mapping back to domain object shouldn't create new instances for Contained-property. But that was not the case.
After some investigations we've found out, that it was due to not-inheriting the .ForPath(, opt => opt.Ignore()) for the ReverseMap.
We are using 9.0.0 version of Automapper.
Here is the code:
public class Contained {
public Guid Id { get; set; }
}
public class Base {
public Contained Contained { get; set;}
}
public class Derived: Base { }
public abstract class BaseDto {
public Guid? ContainedId { get; set; }
}
public class DerivedDto: BaseDto { }
[Test]
public void ForPathInheritanceWorks()
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Base, BaseDto>()
.IncludeAllDerived()
.ReverseMap()
.ForPath(d => d.Contained.Id, opt => opt.Ignore());
cfg.CreateMap<Derived, DerivedDto>()
.ReverseMap();
});
var mapper = configuration.CreateMapper();
var derived = mapper.Map<Derived>(new DerivedDto());
Assert.That(derived.Contained, Is.Null); // fails, because derived.Contained is not null.
}
To workaround an issue we had to add
.ForPath(d => d.Contained.Id, opt => opt.Ignore())
after ReverseMap of the derived class.
For me it looks like a bug in Automapper.
I have a source class like this:
public class Basket {}
a target class like this:
public class BasketModel
{
public string Property { get; set; }
}
and a mapping like this:
Mapper.CreateMap<Basket, BasketModel>()
.ForMember(x => x.Property, o => o.ResolveUsing(x => "anything"));
Now I've made the "Property" property in the original model virtual and created a new class that inherits from the model:
public class BasketModel
{
public virtual string Property { get; set; }
}
public class BasketModel2 : BasketModel
{
public override string Property
{
get
{
return "some value";
}
}
}
I've updated the mapping as such:
Mapper.CreateMap<Basket, BasketModel>()
.ForMember(x => x.Property, o => o.ResolveUsing(x => "anything"))
.Include<Basket, BasketModel2>();
And created the mapping
Mapper.CreateMap<Basket, BasketModel2>()
.ForMember(x => x.Property, o => o.Ignore());
Now when I try to map into BasketModel2, instead of null, the value of Property is "anything".
What am I missing here?
Ok I think I had a brainfart when I wrote this code. model2.Property is never going to be null because it's a getter that always returns the same string. I have some refactoring to do, but AutoMapper was doing what it needs to do, I was just using it wrong.
I'm trying to do a pretty simple thing in Entity Framework.
I have a product that has zero or more parameters and these parameters will be mapped to their own tables. However, I'm unable to get this to work. I've been trying to get the mappings right and then use the migrations to see what the database is supposed to look like. I know that this is very simple in NHibernate, but I'm forced against my will to use Entity Framework v6.
Background
These are my entities:
namespace Entities
{
public class EntityState
{
public int Id { get; set; }
}
public class ProductState : EntityState
{
public virtual ICollection<ProductParameterState> Parameters { get; set; }
}
public abstract class ProductParameterState : EntityState
{
}
public class ColorParameterState : ProductParameterState
{
public virtual string Color { get; set; }
}
public class SizeParameterState : ProductParameterState
{
public virtual int Size { get; set; }
}
}
I would like to store this in the following schema:
How to do this?
My attempts
Table-per-class
I tried mapping using TPC:
namespace Mappings
{
public class ProductMap : EntityTypeConfiguration<ProductState>
{
public ProductMap()
{
HasKey(x => x.Id);
Property(x => x.Name);
HasMany(x => x.Parameters);
}
}
public class ColorParameterMap : EntityTypeConfiguration<ColorParameterState>
{
public ColorParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Color);
Map(x =>
{
x.ToTable("ColorParameters");
x.MapInheritedProperties();
});
}
}
public class SizeParameterMap : EntityTypeConfiguration<SizeParameterState>
{
public SizeParameterMap()
{
HasKey(x => x.Id);
Property(x => x.Size);
Map(x =>
{
x.ToTable("SizeParameters");
x.MapInheritedProperties();
});
}
}
}
But this gives the error The association 'ProductState_Parameters' between entity types 'ProductState' and 'ProductParameterState' is invalid. In a TPC hierarchy independent associations are only allowed on the most derived types..
Don't use an inheritence strategy
So I tried to remove the MapInheritedProperties, but then it wants to create an additional, and unwanted, table:
CreateTable(
"dbo.ProductParameterStates",
c => new
{
Id = c.Int(nullable: false, identity: true),
ProductState_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.ProductStates", t => t.ProductState_Id)
.Index(t => t.ProductState_Id);
I don't want this. I am able to get rid of this one by removing the Parameters property in Product, but then I'm not able to use the Parameters of a Product.
Am I asking for too much or is it possible?
You can use TPC, but the relationship must be bidirectional with explicit FK defined (which I guess is the opposite of "independent association" mentioned in the error message).
Add inverse navigation property and FK property to your base entity:
public abstract class ProductParameterState : EntityState
{
public int ProductId { get; set; }
public ProductState Product { get; set; }
}
and use the same entity configurations as in your first attempt, except for the ProductMap where you either remove the following
HasMany(x => x.Parameters);
or change it to
HasMany(e => e.Parameters)
.WithRequired(e => e.Product)
.HasForeignKey(e => e.ProductId);
I'm using AutoMapper in my ASP.NET MVC4 project. I have a problem when mapping 2 class Question and QuestionViewModel. Here my two model classes:
public class Question
{
public int Id { get; set; }
public string Content { get; set; }
public Tuple<int, int> GetVoteTuple()
{
"some code here"
}
}
public class QuestionViewModel
{
public int Id { get; set; }
public string Content { get; set; }
public Tuple<int, int> VoteTuple { get; set; }
}
Here is my controller code :
public class QuestionController: Controller
{
public ActionResult Index(int id)
{
Question question = Dal.getQuestion(id);
Mapper.CreateMap<Question, QuestionViewModel>()
.ForMember(p => p.VoteTuple,
m => m.MapFrom(
s => s.GetVoteTuple()
));
QuestionViewModel questionViewModel =
Mapper.Map<Question, QuestionViewModel>(question);
return View(questionViewModel);
}
}
When I run this code the VoteTuple property in QuestionViewModel has null value. How can I map 2 class with Tuple property ?
Thanks.
Mapping from Tuple to Tuple is not possible by default through Automapper, because Tuple doesn't have setter properties (they can only be initialized through the constructor).
You have 2 options:
1) Create a custom resolver for Automapper and then use the .ResolveUsing method in the mapping config: .ForMember(p => p.VoteTuple, m => m.ResolveUsing<CustomTupleResolver>())
2) Map to a properties / a class instead, like this:
public class QuestionViewModel
{
public int Id { get; set; }
public string Content { get; set; }
public int VoteItem1 { get; set; }
public int VoteItem2 { get; set; }
}
And then:
.ForMember(p => p.VoteItem1, m => m.MapFrom(g => g.Item1))
.ForMember(p => p.VoteItem2, m => m.MapFrom(g => g.Item2))
You don't really need to use Tuple in your view model, so I'd recommend the 2nd option.
Edit:
I see that you've updated your code so that GetVoteTuple() is a function, not a property. In that case, you could easily adapt the code like this:
.ForMember(p => p.VoteItem1, m => m.MapFrom(g => g.GetVoteTuple().Item1))
.ForMember(p => p.VoteItem2, m => m.MapFrom(g => g.GetVoteTuple().Item2))
Your CreateMap call is incorrect:
Mapper.CreateMap<Question, QuestionViewModel>()
.ForMember(p => p.VoteTuple,
m => m.MapFrom(
s => s.GetVoteTuple()
//-----------^
));
Try using ResolveUsing instead of MapFrom (and use the generic s argument in your lambda instead of the local variable reference:
Mapper.CreateMap<Question, QuestionViewModel>()
.ForMember(p => p.VoteTuple,
m => m.ResolveUsing(
s => s.GetVoteTuple()
));
MapFrom is used to map properties directly. Since you're wanting to "map" from the result of a function call, ResolveFrom is more appropriate.
Also, you should only call CreateMap once in your application, typically that's done in Application_Start in global.asax
try this :
Mapper.CreateMap<Question, QuestionViewModel>()
.ForMember(p => p.VoteTuple,op=>op.MapFrom(v=>new Tuple<int,int>(v.GetVoteTuple.Item1,v.GetVoteTuple.Item2)));