Entity Framework 4.0 CTP5 One to One Mapping - c#

Hi i am using CTP5 to map between two entities like that:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsManager { get; set; }
public decimal Credit { get; set; }
public int CreditAlertCount { get; set; }
public decimal TelPrice { get; set; }
public decimal CellPrice { get; set; }
public DateTime InsertDate { get; set; }
public IList<string> PhoneList { get; set; }
public int UserTypeId { get; set; }
public virtual UserType UserType { get; set; }
}
public class UserType
{
public int Id { get; set; }
public int UserLevel { get; set; }
public string TypeDescription { get; set; }
}
//here is configurations
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
Property(c => c.InsertDate).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("INSERT_DATE");
Property(c => c.IsManager).HasDatabaseGenerationOption(DatabaseGenerationOption.Computed).HasColumnName("IS_MANAGER");
Property(c => c.UserName).HasMaxLength(25).IsRequired().HasColumnName("USER_NAME");
Property(c => c.Password).HasMaxLength(25).IsRequired().HasColumnName("USER_PASSWORD");
Property(c => c.CellPrice).IsRequired().HasColumnName("CELL_PRICE");
Property(c => c.TelPrice).IsRequired().HasColumnName("TEL_PRICE");
Property(c => c.CreditAlertCount).IsRequired().HasColumnName("CREDIT_ALERT_COUNT");
Property(c => c.Credit).IsRequired().HasColumnName("CREDIT");
Property(c => c.UserTypeId).IsOptional().HasColumnName("USER_TYPE_ID");
/*relationship*/
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey(p => p.Id, "USER_TYPE_ID"));
ToTable("CRMC_USERS", "GMATEST");
}
}
public class UserTypeConfig : EntityTypeConfiguration<UserType>
{
public UserTypeConfig()
{
/*Identity*/
HasKey(c => c.Id);
Property(c => c.Id).HasDatabaseGenerationOption(DatabaseGenerationOption.Identity).HasColumnName("ID");
/*simple scalars*/
Property(s => s.TypeDescription).IsRequired().HasColumnName("DESCRITPION");
Property(s => s.UserLevel).IsRequired().HasColumnName("USER_LEVEL");
ToTable("CRMC_USER_TYPES", "GMATEST");
}
}
What do i do wrong my User.UserType = null?
How to hell do i map this to work!?
I am dying here for 3 days to work it off.
I'm using DevArt Connection 6.058... some thing
Oracle 10g, C# EntityFramework 4.0

You've setup a required association between User and UserType, therefore you cannot have a User object without a UserType (i.e. User.UserType == null). To be able to do that you need to make 2 changes to your object model and fluent API:
1.Change the type of UserTypeId property to int?:
public int? UserTypeId { get; set; }
2.Remove the code from your fluent API that reads:
HasRequired(p => p.UserType).WithMany().IsIndependent().Map(m => m.MapKey...
You don't need any of those stuff. Everything will be configured by Code First based on convention for you.

Related

Entity Framework 6 automatically changes entity value

I have two entities as shown in the screenshot:
each DIMPeriodDates can connect to many DIMPeriodComparatives and
each DIMPeriodComparatives can connect to many DIMPeriodDates
In other words, DIMPeriod can connect to themselves with order number.
This is the DIMPeriod class :
public class DIMPeriodDate
{
public enum EnumDIMPeriodPresentStatus
{
Refresh,
Operation
}
public enum EnumDIMPeriodType
{
Decisive,
Predicted
}
public enum EnumDIMPeriodAuditStatus
{
Audited,
NotAudited
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
public string Title { get; set; }
public string? Desc { get; set; }
public bool IsClosed { get; set; } = false;
[Column(TypeName = "date")]
public DateTime DateStart { get; set; }
[Column(TypeName = "date")]
public DateTime DateEnd { get; set; }
public List<DIMPeriodComparative> PeriodComparativeList { get; set; } = new();
public List<DIMPeriodComparative> PeriodBaseComparativeList { get; set; } = new();
}
And this is the PeriodComparative class :
public class DIMPeriodComparative
{
public int PeriodComparativeID { get; set; }
public int PeriodBaseID { get; set; }
public int Order { get; set; } = 1;
public DIMPeriodDate PeriodComparative { get; set; }
public DIMPeriodDate PeriodBase { get; set; }
}
Here is my Fluent API config :
modelBuilder.Entity<DIMPeriodComparative>()
.HasKey(q => new { q.PeriodComparativeID, q.PeriodBaseID });
modelBuilder.Entity<DIMPeriodComparative>()
.HasOne(q => q.PeriodComparative)
.WithMany(q => q.PeriodComparativeList)
.HasForeignKey(q=>q.PeriodComparativeID)
.OnDelete(DeleteBehavior.NoAction);
modelBuilder.Entity<DIMPeriodComparative>()
.HasOne(q => q.PeriodBase)
.WithMany(q => q.PeriodBaseComparativeList)
.HasForeignKey(q=>q.PeriodBaseID)
.OnDelete(DeleteBehavior.NoAction);
Now when I insert a new DIMPeriodComparatives entity to specific DIMPeriodDates like this :
After calling SaveChanges, the value automatically has changed :
PeriodBase and PeriodComparative have different Value with different id 11 and 13 ...
i change fulent api to this :
modelBuilder.Entity<DIMPeriodDate>()
.HasMany(q => q.PeriodComparativeBase)
.WithMany(q => q.PeriodComparative)
.UsingEntity<DIMPeriodComparative>(right =>
right.HasOne(q => q.PeriodComparative)
.WithMany()
.HasForeignKey(q => q.PeriodComparativeID)
, left =>
left.HasOne(q=>q.PeriodBase)
.WithMany()
.HasForeignKey(q=>q.PeriodBaseID)
, joinent =>
joinent.HasKey(q => new {q.PeriodComparativeID,q.PeriodBaseID})
);
and issue is fixed ...

Unique Constraint Not Working with Entity Framework Core

I'm using Entity Framework Core 2.2.6 and I have two entities Profile and Category. Each Category will have ProfileId to identify to which Profile it belongs. I'm trying to enforce uniqueness for Name and ProfileId in Category. But however my unique constraint fails.
Here is my Entities,
BaseEntity:
public class BaseEntity<TKey>
{
public TKey Id { get; set; }
public bool Active { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public string CreatedBy { get; set; }
public DateTimeOffset? ModifiedAt { get; set; }
public string ModifiedBy { get; set; }
}
Profile:
public class Profile : BaseEntity<Guid>, IAggregateRoot
{
private Profile()
{
// required by EF
}
public Profile(string brandName)
{
Guard.Against.NullOrEmpty(brandName, nameof(brandName));
BrandName = brandName;
}
public string BrandName { get; set; }
public string Caption { get; set; }
}
Category:
public class Category : BaseEntity<Guid>, IAggregateRoot
{
private Category()
{
// required by EF
}
public Category(string name)
{
Guard.Against.NullOrEmpty(name, nameof(name));
Name = name;
}
public Category(string name, string code) : this(name)
{
Guard.Against.NullOrEmpty(code, nameof(code));
Code = code;
}
public string Name { get; set; }
public string Code { get; set; }
public Guid ProfileId { get; set; }
}
Category Entity Configuration:
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
{
public void Configure(EntityTypeBuilder<Category> builder)
{
builder.HasAlternateKey(c => new { c.ProfileId, c.Name });
// builder.HasIndex(c => new { c.ProfileId, c.Name }).IsUnique();
builder.Property(c => c.Name)
.IsRequired()
.HasMaxLength(50);
builder.Property(c => c.Code)
.HasMaxLength(10);
builder.HasOne<Profile>()
.WithMany()
.HasForeignKey(p => p.ProfileId)
.IsRequired();
}
}
I tried builder.HasAlternateKey(c => new { c.ProfileId, c.Name }); and builder.HasIndex(c => new { c.ProfileId, c.Name }).IsUnique();. But both doesn't seem to work. Please can you assist on where I go wrong?

How to use Automapper to flatten list of entity hierarchies?

I want to use automapper to flatten a list of entity heirarchies returned back from Entity Framework Core.
Here are my entities:
public class Employee {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public double? PayRateRegular { get; set; }
public double? PayRateLoadedRegular { get; set; }
public double? GMOutput { get; set; }
public string EmployeeType { get; set; }
//List of CommissionDetails where this employee is the consultant
public IEnumerable<CommissionDetail> CommissionDetailConsultants { get; set; } = new List<CommissionDetail>();
}
public class Project {
public int Id { get; set; }
public string Description { get; set; }
public double? BillRateRegular { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public Customer Customer { get; set; }
public int CustomerId { get; set; }
}
public class Customer {
public int Id { get; set; }
public string Name { get; set; }
}
public class CommissionDetail {
public string SaleType { get; set; }
public double CommissionPercent { get; set; }
public bool? IsReported { get; set; }
public int? Level { get; set; }
public string BasedOn { get; set; }
public Project Project { get; set; }
public int ProjectId { get; set; }
public Employee SalesPerson { get; set; }
public int SalesPersonEmployeeId { get; set; }
public Employee Consultant { get; set; }
public int ConsultantEmployeeId { get; set; }
}
Here is my DTO:
public class ConsultantGridViewModel
{
public string ConsultantName { get; set; }
public string CustomerName { get; set; }
public string SalesPersonName { get; set; }
public string ProjectDescription { get; set; }
public double? PayRate { get; set; }
public double? LoadedRated { get; set; }
public double? BillRate { get; set; }
public double? GM { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double CommissionPercent { get; set; }
public int? CommissionLevel { get; set; }
}
Here is my call to EF:
return await _dbContext.Employee
.AsNoTracking()
.Include(e => e.CommissionDetailConsultants)
.ThenInclude(cd => cd.SalesPerson)
.Include(e => e.CommissionDetailConsultants)
.ThenInclude(cd => cd.Project)
.ThenInclude(p => p.Customer)
.Where(e => e.EmployeeType == "Contractor")
.ToListAsync();
I'm currently flattening it with SelectMany as follows:
var consultants = employees.SelectMany(e =>
e.CommissionDetailConsultants,
(emp, com) => new ConsultantGridViewModel {
ConsultantName = emp.Name,
PayRate = emp.PayRateRegular,
LoadedRated = emp.PayRateLoadedRegular,
GM = emp.GMOutput,
BillRate = com.Project.BillRateRegular,
ProjectDescription = com.Project.Description,
ProjectStartDate = com.Project.StartDate,
ProjectEndDate = com.Project.EndDate,
CustomerName = com.Project.Customer.Name,
SalesPersonName = com.SalesPerson.Name,
CommissionPercent = com.CommissionPercent,
CommissionLevel = com.Level
});
I would like to use automapper instead. I've used automapper for all my other DTO mappings but I can't figure out how to use it to flatten a nested object like this.
Let rewrite what you have currently with SelectMany + Select utilizing the Consultant navigation property:
var consultants = employees
.SelectMany(e => e.CommissionDetailConsultants)
.Select(com => new ConsultantGridViewModel
{
ConsultantName = com.Consultant.Name,
PayRate = com.Consultant.PayRateRegular,
LoadedRated = com.Consultant.PayRateLoadedRegular,
GM = com.Consultant.GMOutput,
BillRate = com.Project.BillRateRegular,
ProjectDescription = com.Project.Description,
ProjectStartDate = com.Project.StartDate,
ProjectEndDate = com.Project.EndDate,
CustomerName = com.Project.Customer.Name,
SalesPersonName = com.SalesPerson.Name,
CommissionPercent = com.CommissionPercent,
CommissionLevel = com.Level
});
Now it can be seen that the CommissionDetail contains all the necessary data, so while you can't avoid SelectMany, you can replace the Select by creating a mapping from CommissionDetail to ConsultantGridViewModel and use something like this:
var consultants = Mapper.Map<List<ConsultantGridViewModel>>(
employees.SelectMany(e => e.CommissionDetailConsultants));
or even better, project directly to the DTO:
var consultants = await _dbContext.Employee
.Where(e => e.EmployeeType == "Contractor")
.SelectMany(e => e.CommissionDetailConsultants)
.ProjectTo<ConsultantGridViewModel>()
.ToListAsync();
Now the mapping.
AutoMapper will map automatically members like CommisionPercent. Also the Flattening feature will handle automatically mappings like Project.EndDate -> ProjectEndDate, Consultant.Name -> ConsultantName etc.
So as usual with AutoMapper you should specify manually the mapping of properties which don't fall into previous categories. The minimal configuration in this case would be something like this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<CommissionDetail, ConsultantGridViewModel>()
.ForMember(dst => dst.PayRate, opt => opt.MapFrom(src => src.Consultant.PayRateRegular))
.ForMember(dst => dst.LoadedRated, opt => opt.MapFrom(src => src.Consultant.PayRateLoadedRegular))
.ForMember(dst => dst.GM, opt => opt.MapFrom(src => src.Consultant.GMOutput))
.ForMember(dst => dst.BillRate, opt => opt.MapFrom(src => src.Project.BillRateRegular))
.ForMember(dst => dst.CustomerName, opt => opt.MapFrom(src => src.Project.Customer.Name))
.ForMember(dst => dst.CommissionLevel, opt => opt.MapFrom(src => src.Level));
});
P.S. You can even avoid SelectMany by basing your queries directly on CommissionDetail entity, for instance
var consultants = await _dbContext.Set<CommissionDetail>()
.Where(c => c.Consultant.EmployeeType == "Contractor")
.ProjectTo<ConsultantGridViewModel>()
.ToListAsync();
Note that when you do direct projection, there is no need of AsNoTracking or Include / ThenInclude.

Cannot add FK using EntityFramework

I'm learning ASP.NET Core with Entity Framework and I'm trying to add an FK in my UserDetails table. These are the model:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual UserDetails UserDetail { get; set; }
}
public class UserDetails
{
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}
The table User is the Master table which contains all the registered user in my application (I'm using AspNetCore.Identity).
Actual I want add as FK the property UserId which must bound the Id of User. So inside the ApplicationContext class I did the following:
public class DemoAppContext : IdentityDbContext<ApplicationUser>
{
public DemoAppContext(DbContextOptions<DemoAppContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey(d => d.???; <- problem here
});
}
public DbSet<User> Users { get; set; }
public DbSet<UserDetails> UserDetails { get; set; }
}
I overrided the OnModelCreating and using the ModelBuilder I defined for UserDetails table the MaxLength of some properties. In the last line of builder.Entity<UserDetails> I tried to assign the FK creating the relationship with HasOne => UserKey which contains the object User. The relationship is 1 to 1 so I used WithOne and assigned UserDetail which contains the UserDetails object.
At the end I used HasForeignKey but when I type d. the compiler doesn't show any properties.
What I did wrong? Maybe I overcomplicated the things?
Sorry for any errors, and thanks in advance for any explanation.
The following code will work:
builder.Entity<UserDetails>(entity =>
{
entity.Property(e => e.Biography).HasMaxLength(150);
entity.Property(e => e.Country).HasMaxLength(10);
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<UserDetails>(x => x.UserId); //???; < -problem here
});
Can you try this:
entity.HasOne(d => d.UserKey)
.WithOne(p => p.UserDetail)
.HasForeignKey<User>(b => b.Id);
or
public class UserDetails
{
[ForeignKey(nameof(UserKey))]
public string UserId { get; set; }
public string Biography { get; set; }
public string Country { get; set; }
public Uri FacebookLink { get; set; }
public Uri TwitterLink { get; set; }
public Uri SkypeLink { get; set; }
public virtual User UserKey { get; set; }
}

Entity Framework One-To-Many

First of all I have these two models to store a post in two tables one for shared data and the other contains cultured data for English and Arabic
public class Post
{
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public virtual ICollection<PostContent> Contents { get; set; }
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }
public int PostId { get; set; }
[InverseProperty("PostId")]
public virtual Post Post{ set; get; }
}
Mapping
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(p => p.Post).WithMany(p => p.Contents).HasForeignKey(p=>p.PostId);
ToTable("PostContents");
}
}
I have two questions
1- Is these models are connected properly. Is there something else I need to do ?
2- I need to select all Posts with their contents where the culture of the content 'en' for example. I used this:
var res = context.Posts.Include(p => p.Contents.Single(c => c.Culture.Equals("en")));
and have this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.Parameter name: path
If you know you are not going to support more than two cultures then I would just add to your Post class.
public class Post
{
public Post()
{
Contents = new List<PostContent>();
}
public int Id { set; get; }
public bool Active { get; set; }
public bool Featured { get; set; }
public int? EnglishContentId { get;set;}
public int? ArabicContentId { get;set;}
PostContent EnglishContent {get;set;}
PostContent ArabicContent {get;set;}
}
public class PostContent
{
public int Id { set; get; }
public string Title { get; set; }
public string Summary { get; set; }
public string Details { get; set; }
[StringLength(2)]
public string Culture { get; set; }/*This property is not required*/
}
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Posts");
HasOptional(p => p.EnglishContent).WithMany().HasForeignKey(p=>p.EnglishContentId);
HasOptional(p => p.ArabicContent).WithMany().HasForeignKey(p=>p.ArabicContentId);
}
}
public class PostContentMap : EntityTypeConfiguration<PostContent>
{
public PostContentMap()
{
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("PostContents");
}
}
The Above design will simplify your design and queries, will improve the performance alot.
But if you might have to support more cultures then you got the design and mapping right.
As far as EF 5, include does not allow filters, but I am not sure about EF 6.0
atleast you can get all posts that have english contents as follows
Add using System.Data.Entity;
var res = context.Posts.Include(p => p.Contents).Where(c => c.Contents.Any(cp=>cp.Culture.Equals("en")));

Categories

Resources