Using Automapper for Many-To-Many Relationships - asp.net-mvc

I'm trying to use Automapper for a many-to-many relationship and entity framework.
This is my entity:
public class ZChangeUnits
{
[Key]
public string CCode1 { get; set; }
[ForeignKey("CCode1")]
public virtual ZUnits ZUnits1 { get; set; }
[Key]
public string CCode2 { get; set; }
[ForeignKey("CCode2")]
public virtual ZUnits ZUnits2 { get; set; }
public decimal NZarib { get; set; }
}
And this is ZUnit entity:
[Key]
public string CCode { get; set; }
public string CTitle { get; set; }
public string CMGCode { get; set; }
public virtual ZCodes ZCodes { get; set; }
I want to map them to this ViewModel, probably I should remove Titles:
public class ChangeUnitsViewModel
{
public string CCode1 { get; set; }
public string ZUnitsCTitle1 { get; set; }
public string CCode2 { get; set; }
public string ZUnitsCTitle2 { get; set; }
public decimal NZarib { get; set; }
}
And this is the mapping I'm currently using:
Mapper.CreateMap<ZChangeUnits, ChangeUnitsViewModel>()
.ForMember(d => d.ZUnitsCTitle1,
o => o.MapFrom(s => s.ZUnits1.CTitle))
.ForMember(d => d.ZUnitsCTitle2,
o => o.MapFrom(s => s.ZUnits2.CTitle));
But it doesn't seem valid and gives the Missing type map configuration or unsupported mapping. exception.
Can you please guide me about how I can use Automapper to perform a basic CRUD operation on this class? Any useful link, I have no idea how to use it.
To make this really complete, this is my controller!
var data = exchangeRepo.GetAll().ToList();
var d = AutoMapper.Mapper.Map<ChangeUnitsViewModel>(data);

Related

How to set the one-to-one relationship with fluent api in this case? (EF6)

I have this two entities:
public partial class Ficheros
{
public Guid Idfichero { get; set; }
public long Iddocumento { get; set; }
public byte[] Fichero { get; set; }
public virtual Documentos IddocumentoNavigation { get; set; }
}
public partial class Documentos
{
public Documentos()
{
ElementosDocumentos = new HashSet<ElementosDocumentos>();
}
public long Iddocumento { get; set; }
public string Nombre { get; set; }
public long? IdtipoDocumento { get; set; }
public string Codigo { get; set; }
public decimal? Espacio { get; set; }
public string Unidades { get; set; }
public long? Bytes { get; set; }
public virtual ICollection<ElementosDocumentos> ElementosDocumentos { get; set; }
public virtual Ficheros Ficheros { get; set; }
public virtual DocumentosTipos IdtipoDocumentoNavigation { get; set; }
}
In the database, IDFichero is an uniqueidentifier and in Documentos the IDDocumento is a big int autoincrement. The main table is Documentos, that has one and only one fichero, and it is requiered.
The examples that I have seen, it would make me that IDFichero was IDDocumento, but to store a file in the database I need that the ID is a uniqueidentifier.
Thanks.
The relationship you are describing in EF terms is one-to-one FK association with both ends required, Documentos being the principal and Ficheros the dependent.
EF does not support explicit FK for this type of association, so start by removing the Ficheros.Iddocumento property:
public partial class Ficheros
{
public Guid Idfichero { get; set; }
public byte[] Fichero { get; set; }
public virtual Documentos IddocumentoNavigation { get; set; }
}
then use the following fluent configuration:
modelBuilder.Entity<Documentos>()
.HasRequired(e => e.Ficheros)
.WithRequiredPrincipal(e => e.IddocumentoNavigation)
.Map(m => m.MapKey("Iddocumento"));

Fluent API One to Many mapping table

It's really helpful if anyone could explain me how to create a mapping (associated)table for one to many relationship using Fluent API.`
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public List<Image> Images { get; set; }
}
public class Image
{
public int ID { get; set; }
public string Name { get; set; }
public List<Category> Category{ get; set; }
The mapping table should contain CategoryID and ImageID.
The solution should be something similar to this. (This is for many to many)
modelBuilder.Entity<Category>()
.HasMany(c => c.Images).WithMany(i => i.Categories)
.Map(t => t.MapLeftKey("CategoryID")
.MapRightKey("ImageID")
.ToTable("CategoryImage"));
I want Fluent API to create new mapping table for the below relationship.
public class Category
{
public List<Image> Images{get; set;}
}
public class Image
{
public Category Category{ get; set; }
}
Adding NewTable:
modelBuilder
.Entity<NewTable>()
.HasRequired(_ => _.Category)
.WithMany()
.HasForeignKey(_ => _.CategoryId);
modelBuilder
.Entity<Image>()
.HasRequired(_ => _.NewTable)
.WithMany(_ => _.Images)
.HasForeignKey(_ => _.NewTableId)
public class NewTable
{
public int ID { get; set; }
public int CategoryId { get; set; }
public List<Image> Images { get; set; }
public virtual Category Category { get; set; }
}
public class Image
{
public int ID { get; set; }
public string Name { get; set; }
public List<Category> Categories { get; set; }
public int NewTableId { get; set; }
public virtual NewTable NewTable { get; set; }
}

Entity Framework Many to Many Cascade Delete Issue

Having a strange issue working on a code first EF project.
I have the following entities:
public class Booking
{
public Guid BookingId { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
public class AccountingDocumentItem
{
public Guid AccountingDocumentItemId { get; set; }
public virtual List<Employee> Employees { get; set; }
public virtual List<Booking> Bookings { get; set; }
}
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
As you can see, there is meant to be many-to-many relationship between AccountingDocumentItem and both Bookings and Employees. When configuring my AccountingDocumentItem I use the following:
public AccountingDocumentItemConfiguration()
{
HasMany(x => x.Employees);
HasMany(x => x.Bookings);
}
What is strange is that this works perfectly for Employees. I get a AccountingDocumentItemEmployees table created. But for Bookings I get the following error:
"Introducing FOREIGN KEY constraint 'FK_dbo.AccountingDocumentItemBookings_dbo.Bookings_Booking_BookingId' on table 'AccountingDocumentItemBookings' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints."
Now I've tried to do this along the lines of below code:
HasMany(x => x.Bookings).WithMany(b => b.AccountingDocumentItems)...
But I only get the option to do a Map using the above line, no option to do a WillCascadeOnDelete(false).
Can someone point out what I'm doing wrong, because comparing it to how I handle Employees I can't see any difference.
EDIT:
My original post abbreviated the entities, which is probably where the problem is arising. Here is the full entity:
public class AccountingDocument
{
public Guid AccountingDocumentId { get; set; }
public Guid SiteId { get; set; }
public virtual Site Site { get; set; }
public Guid? ClientId { get; set; }
public virtual Client Client { get; set; }
public Guid? SupplierId { get; set; }
public virtual Supplier Supplier { get; set; }
public string DocumentNumber { get; set; }
public string Reference { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public Guid LinkedAccountingDocumentId { get; set; }
public virtual AccountingDocument LinkedAccountingDocument { get; set; }
public byte AccountingDocumentTypeId { get; set; }
public DateTime CreationDate { get; set; }
public DateTime DocumentDate { get; set; }
public decimal Total { get; set; }
public Guid UserId { get; set; }
public virtual User User { get; set; }
public string Room { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
public class AccountingDocumentItem
{
public Guid AccountingDocumentItemId { get; set; }
public Guid AccountingDocumentId { get; set; }
public virtual AccountingDocument AccountingDocument { get; set; }
public string Description { get; set; }
public Guid TaxId { get; set; }
public virtual Tax Tax { get; set; }
public decimal Quantity { get; set; }
public string Unit { get; set; }
public decimal Cost { get; set; }
public decimal SellInclusive { get; set; }
public decimal SellExclusive { get; set; }
public decimal DiscountPercentage { get; set; }
public decimal TotalInclusive { get; set; }
public decimal TotalExclusive { get; set; }
public decimal CommissionInclusive { get; set; }
public decimal CommissionExclusive { get; set; }
public int LoyaltyPoints { get; set; }
public bool IsSeries { get; set; }
public byte ItemType { get; set; }
public Guid? ServiceId { get; set; }
public virtual Service Service { get; set; }
public Guid? ProductId { get; set; }
public virtual Product Product { get; set; }
public Guid? VoucherId { get; set; }
public virtual Voucher Voucher { get; set; }
public int SortOrder { get; set; }
public Guid? SourceId { get; set; }
public virtual Source Source { get; set; }
public Guid? CostCentreId { get; set; }
public virtual CostCentre CostCentre { get; set; }
public Guid? ClientId { get; set; }
public virtual Client Client { get; set; }
public Guid PackageGroupId { get; set; }
public Guid PackageServiceId { get; set; }
public virtual List<Employee> Employees { get; set; }
public virtual List<Booking> Bookings { get; set; }
public virtual List<MedicalDiagnosis> MedicalDiagnoses { get; set; }
}
public class Booking
{
public Guid BookingId { get; set; }
public Guid SiteId { get; set; }
public Site Site { get; set; }
public Guid? ClientId { get; set; }
public Client Client { get; set; }
public Guid BookingStateId { get; set; }
public BookingState BookingState { get; set; }
public virtual List<AccountingDocumentItem> AccountingDocumentItems { get; set; }
}
And my configuration:
public class AccountingDocumentConfiguration : EntityTypeConfiguration<AccountingDocument>
{
public AccountingDocumentConfiguration()
{
Property(x => x.Reference).HasMaxLength(200);
HasRequired(x => x.Site);
Property(x => x.DocumentNumber).IsRequired().HasMaxLength(100);
Property(x => x.Reference).HasMaxLength(200);
Property(x => x.Description).HasMaxLength(500);
Property(x => x.Notes).HasMaxLength(500);
HasOptional(x => x.LinkedAccountingDocument);
Property(x => x.AccountingDocumentTypeId).IsRequired();
Property(x => x.CreationDate).IsRequired();
Property(x => x.DocumentDate).IsRequired();
Property(x => x.Total).IsRequired();
Property(x => x.Room).HasMaxLength(50);
}
}
public class AccountingDocumentItemConfiguration : EntityTypeConfiguration<AccountingDocumentItem>
{
public AccountingDocumentItemConfiguration()
{
Property(x => x.Description).IsRequired().HasMaxLength(200);
HasMany(x => x.Employees);
HasMany(x => x.Bookings);
HasMany(x => x.MedicalDiagnoses);
Property(x => x.Unit).HasMaxLength(50);
}
}
Even with the added text above it's working for me, once I comment out the added nav properties that aren't fully defined above. The FK error means that there might be a race condition if you happen to delete (See this article), but with whats here I can't tell. Do you need to have cascading deletes on your database? If not, you could just turn it off - I realize it's a pretty broad stroke on a minor problem though.
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
If this is not an option - it's something else that you haven't included. Do you have mappings for the Bookings table? It looks like Bookings have a required Site - is it possible that deleting a site, could trigger a delete in a bunch of other things? It looks like Site could do something like this Site -> Account Document -> Accounting Document item.. Site -> Booking possibly?
Here's another SO question that could possibly be related.

Entityframework port from model first to code first

I am still on my quest to port from a Model First to Code First implementation of EntityFramework. I have made significant progress, with the help of Eranga. I have run into another snag, and I just cant explain what is hapening. I have two Entity objects Topic and Course
A Topic can have one Course that is required
A Course can have 0 or more topics
when i execute the following linq it generates wierd SQL
var topics = from o in db.Topics where o.ParentTopic == null &&
o.Course.Id == c.Id select o;
The SQL generated is
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[ShortDescription] AS [ShortDescription],
[Extent1].[LongDescription] AS [LongDescription],
[Extent1].[Property] AS [Property],
[Extent1].[Difficulty] AS [Difficulty],
[Extent1].[Weight] AS [Weight],
[Extent1].[Course_Id] AS [Course_Id],
[Extent1].[ParentTopic_Id] AS [ParentTopic_Id],
[Extent1].[Course_Id1] AS [Course_Id1]
FROM [dbo].[Topics] AS [Extent1]
WHERE ([Extent1].[ParentTopic_Id] IS NULL) AND ([Extent1].[Course_Id] = #p__linq__0)
Notice that there is an added field called Course_Id1 that is not in my object and not declared as a foreign key. I thought that in OnModelCreating() I had specified the parent child relationship correctly from both sides (I would have thought you only needed to do it from either side), but i cant get EntityFramework not to generate the extra field that obviously does not exist in the database. Remember my database was originally created using a ModelFirst approach.
Can anyone explain where the extra field is comming from????
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Topic
modelBuilder.Entity<Topic>()
.HasRequired(m => m.Course)
.WithMany(m=>m.Topics)
.HasForeignKey(m => m.Course_Id);
modelBuilder.Entity<Topic>()
.HasOptional(m => m.ParentTopic)
.WithMany(m => m.ChildTopics)
.HasForeignKey(m => m.ParentTopic_Id);
//////// lots of code removed for brevity. //////
modelBuilder.Entity<Course>()
.HasMany(m=>m.Topics)
.WithRequired(m => m.Course)
.HasForeignKey(m => m.Course_Id);
}
public partial class Topic
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public string Property { get; set; }
public double? Difficulty { get; set; }
public double? Weight { get; set; }
[JsonIgnore]
public virtual Course Course { get; set; }
public int Course_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Question> Questions { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> ChildTopics { get; set; }
[JsonIgnore]
public virtual Topic ParentTopic { get; set; }
public int? ParentTopic_Id { get; set; }
[JsonIgnore]
public virtual ICollection<RTIQueueEntryData> RTIQueueEntryData { get; set; }
[JsonIgnore]
public virtual ICollection<Intervention> Interventions { get; set; }
[JsonIgnore]
public virtual ICollection<RtiStudentGroup> RtiStudentGroups { get; set; }
}
public partial class Course
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public string Version { get; set; }
public string Year { get; set; }
public string ImportedId { get; set; }
[Required]
public string LocalCourseNumber { get; set; }
[Required]
public string NCESCourseNumber { get; set; }
[Required]
public string StateCourseNumber { get; set; }
public int? Grade { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
[JsonIgnore]
public virtual Department Department { get; set; }
public int DepartmentId { get; set; }
[JsonIgnore]
public virtual ICollection<StudentGroup> StudentGroups { get; set; }
[JsonIgnore]
public virtual ICollection<CutPointTemplate> CutPointTemplates { get; set; }
[JsonIgnore]
public virtual School School { get; set; }
public int School_Id { get; set; }
[JsonIgnore]
public virtual ICollection<Staff> RTIStaff { get; set; }
[JsonIgnore]
public virtual ICollection<Topic> Topics { get; set; }
}
You have another relationship between Course and Topic created by convention due to this navigation property:
public virtual ICollection<Topic> PerformanceIndicators { get; set; }
EF will put an (invisible, not exposed) end of the relationship into the Topic class. By default the relationship is one-to-many. Hence you get an additional foreign key property in the Topics table (= Course_Id1).

EF4 - Can a POCO be used as both an Entity and ComplexType?

I am using EF4 CTP5. Here are my POCOs:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Is there a way to get Address to be a ComplexType for the Order class? After playing around with this, I'm guessing not, but maybe there's a way I haven't seen.
EDIT: In response to Shawn below, I gave it my best shot:
//modelBuilder.Entity<Order>().Ignore(o => o.BillingAddress);
//modelBuilder.Entity<Order>().Ignore(o => o.ShippingAddress);
modelBuilder.Entity<Order>()
.Property(o => o.BillingAddress.City).HasColumnName("BillingCity");
Fails at runtime with error "The configured property 'BillingAddress' is not a declared property on the entity 'Order'." Trying to use Ignore() doesn't work. Next, the Hanselman article is CTP4, but the CTP5 equivalent is:
modelBuilder.Entity<Order>().Map(mapconfig =>
{
mapconfig.Properties(o => new {
o.Id
, o.Total
, o.BillingAddress.City
});
mapconfig.ToTable("Orders");
});
Fails with error "Property 'BillingAddress.City' of type 'Order' cannot be included in its mapping."
I give up. Maybe the final release will have something like this. Or maybe I need to switch to NHibernate =)
All you need to do is to place ComplexTypeAttribute on Address class:
[ComplexType]
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Alternatively, you can achieve this by fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Address>();
}
But you cannot have Address type as to be both an Entity and a Complex Type, it's one way or another.
Take a look at this blog post where I discuss this at length:
Associations in EF Code First CTP5: Part 1 – Complex Types
If you want Address to be in the same table as Order, you're going to have to tell EF that in the DbContext OnModelCreating override.
Take a look here: http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx

Resources