I am trying to create a many-to-many relation between my User and Request classes. The migration should generate an intermediate table but its generating a one-to-many in both table.
public class ApplicationUser : IdentityUser
{
...
public virtual ICollection<Request> Requests{ get; set; }
}
public partial class Request
{
...
public virtual ICollection<ApplicationUser> Users{ get; set; }
}
public partial class _12 : DbMigration
{
public override void Up()
{
AddColumn("dbo.AspNetUsers", "Request_Id", c => c.Int());
AddColumn("dbo.Requests", "ApplicationUser_Id", c => c.String(maxLength: 128));
CreateIndex("dbo.AspNetUsers", "Request_Id");
CreateIndex("dbo.Requests", "ApplicationUser_Id");
AddForeignKey("dbo.AspNetUsers", "Request_Id", "dbo.Requests", "Id");
AddForeignKey("dbo.Requests", "ApplicationUser_Id", "dbo.AspNetUsers", "Id");
}
...
}
Got it working with he fluent API.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasMany(t => t.Requests)
.WithMany(t => t.Users);
}
Giving me this
public partial class _12 : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.ApplicationUserRequests",
c => new
{
ApplicationUser_Id = c.String(nullable: false, maxLength: 128),
Request_Id = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.ApplicationUser_Id, t.Request_Id })
.ForeignKey("dbo.AspNetUsers", t => t.ApplicationUser_Id, cascadeDelete: true)
.ForeignKey("dbo.Requests", t => t.Request_Id, cascadeDelete: true)
.Index(t => t.ApplicationUser_Id)
.Index(t => t.Request_Id);
}
...
}
I don't have to use the fluent api for table I added myself but the user table is different.
Related
as you know, jsoncolumn support has arrived for efcore7.
I quickly used
yes, I had no problems with creating columns with migration. I added new data
but i have the following problem in query operation
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<IdentitySchema>().OwnsMany(
identitySchema => identitySchema.AuthorityCodes, ownedNavigationBuilder =>
{
var q=ownedNavigationBuilder.ToJson();
})
.OwnsMany(
identitySchema => identitySchema.UserIds, ownedNavigationBuilder =>
{
var x= ownedNavigationBuilder.ToJson();
}); ;
base.OnModelCreating(builder);
}
public class UserAg
{
public string UserId { get; set; }
}
_context.IdentitySchema.Select(f => new
{
f.UserIds,
f.AuthorityCodes
}).Where(f => f.UserIds.Any(f => f.UserId == "1")).ToList();
I have a project with 4 classes:Direction, Area, Section and Local. Direction have many areas, Area have many sections and section have many locals. Local have positives locals and negatives locals, therefore Local entity will have a self many to many relationship. I'm using Automapper for convert LocalDto to Local, but when i try to update this entity with positives locals and/or negatives locals inserted, the system generate this exception:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
So, they are my mapper classes for my entities:
*******DirectionMapper*******
public static class DirectionMappers
{
public static void SettingMappingDirectionToDirectionDto()
{
Mapper.CreateMap<Direction, DirectionDto>()
.ForMember(directionDto => directionDto.AreasDtosList,
mc => mc.MapFrom(direction => direction.AreasCollection));
}
public static void SettingMappingDirectionDtoToDirection()
{
Mapper.CreateMap<DirectionDto, Direction>()
.ForMember(direction => direction.AreasCollection,
mc => mc.MapFrom(directionDto => directionDto.AreasDtosList));
}
public static void SettingMappingDirectionToString()
{
Mapper.CreateMap<Direction, string>().ConvertUsing(direction => direction.Name ?? string.Empty);
}
}
********AreaMapper**********
public class AreaMappers
{
public static void SettingMappingAreaToAreaDto()
{
Mapper.CreateMap<Area, AreaDto>()
.ForMember(areaDto => areaDto.SectionsDtosList, mc => mc.MapFrom(area => area.SectionsCollection))
.ForMember(areaDto => areaDto.DirectionDto, mc => mc.MapFrom(area => area.Direction));
}
public static void SettingMappingAreaDtoToArea()
{
Mapper.CreateMap<AreaDto, Area>()
.ForMember(area => area.SectionsCollection, mc => mc.MapFrom(areaDto => areaDto.SectionsDtosList))
.ForMember(area => area.Direction,mc=> mc.MapFrom(areaDto=> areaDto.DirectionDto));
}
public static void SettingMappingAreaToString()
{
Mapper.CreateMap<Area, string>().ConvertUsing(area => area.Name ?? string.Empty);
}
}
******SectionMapper*******************
public class SectionMappers
{
public static void SettingMappingSectionToSectionDto()
{
Mapper.CreateMap<Section, SectionDto>()
.ForMember(sectionDto => sectionDto.LocalsDtosList, mc => mc.MapFrom(section => section.LocalsCollection))
.ForMember(sectionDto => sectionDto.AreaDto, mc => mc.MapFrom(section => section.Area));
}
public static void SettingMappingSectionDtoToSection()
{
Mapper.CreateMap<SectionDto, Section>()
.ForMember(section => section.LocalsCollection,
mc => mc.MapFrom(sectionDto => sectionDto.LocalsDtosList))
.ForMember(section => section.Area, mc => mc.MapFrom(sectionDto => sectionDto.AreaDto));
}
public static void SettingMappingSectionToString()
{
Mapper.CreateMap<Section, string>().ConvertUsing(section => section.Name ?? string.Empty);
}
}
******LocalMapper (the main course)******
public static class LocalMappers
{
public static void SettingMappingLocalToLocalDto()
{
Mapper.CreateMap<Local, LocalDto>()
.ForMember(localDto => localDto.PositivesLocalsDtos,
mc => mc.MapFrom(local => local.PositivesLocals)
)
.ForMember(localDto => localDto.NegativesLocalsDtos,
mc => mc.MapFrom(local => local.NegativesLocals)
)
.ForMember(localDto => localDto.SectionDto, mc => mc.MapFrom(local => local.Section));
}
public static void SettingMappingLocalDtoToLocal()
{
Mapper.CreateMap<LocalDto, Local>()
.ForMember(local => local.PositivesLocals,
mc => mc.MapFrom(localDto => localDto.PositivesLocalsDtos)
)
.ForMember(local => local.NegativesLocals,
mc => mc.MapFrom(localDto => localDto.NegativesLocalsDtos)
)
.ForMember(local => local.Section, mc => mc.MapFrom(localDto => localDto.SectionDto));
}
public static void SettingMappingLocalToString()
{
Mapper.CreateMap<Local, string>().ConvertUsing(local => local.Number ?? string.Empty);
}
}
Well, this's a service method for Local update:
public AppOperationResult Update(int id, LocalDto localDto)
{
var appOperationResult = CommunValidations.IsDtoNull(localDto);
if (appOperationResult != null) return appOperationResult;
var tupleValidation = localDto.IsModelDtoValidateForUpdate(id);
var isValidate = tupleValidation.Item1;
if (isValidate)
{
if (TryUpdateLocalFromLocalDto(id, localDto)) return AppOperationResult.Successful();
}
string messageError = tupleValidation.Item2;
return AppOperationResult.WithError(messageError);
}
And these are the methods I did to add positive and negative locals (i call them AdjacentLocals):
public AppOperationResult AddAdjacentLocalsToLocal(AdjacentLocalsToLocalDto adjacentLocalsToLocal)
{
var localDto = adjacentLocalsToLocal.LocalToModify;
var appOperationResult = CommunValidations.IsDtoNull(localDto);
if (appOperationResult != null) return appOperationResult;
var tupleValidation = localDto.IsModelDtoValidate();
var isValidate = tupleValidation.Item1;
if (isValidate)
{
if (TryToAddAdjacentLocalsToLocal(adjacentLocalsToLocal, localDto))
return AppOperationResult.Successful();
}
string messageError = tupleValidation.Item2;
return AppOperationResult.WithError(messageError);
}
private bool TryToAddAdjacentLocalsToLocal(AdjacentLocalsToLocalDto adjacentLocalsToLocal, LocalDto localDto)
{
var positiveLocals = adjacentLocalsToLocal.PositiveLocals;
var negativeLocals = adjacentLocalsToLocal.NegativeLocals;
var positiveslocalsRepeated = positiveLocals.Intersect(localDto.PositivesLocalsDtos);
positiveLocals.RemoveAll(x => positiveslocalsRepeated.Contains(x));
var negativeslocalsRepeated = negativeLocals.Intersect(localDto.NegativesLocalsDtos);
negativeLocals.RemoveAll(x => negativeslocalsRepeated.Contains(x));
localDto.PositivesLocalsDtos = new List<LocalDto>(positiveLocals);
localDto.NegativesLocalsDtos = new List<LocalDto>(negativeLocals);
return TryUpdateLocalFromLocalDto(localDto.Id, localDto);
}
private bool TryUpdateLocalFromLocalDto(int idLocal, LocalDto localDto)
{
var local = _localServices.GetById(idLocal);
local.PositivesLocals.Clear();
local.NegativesLocals.Clear();
_localServices.Update(local);
if (local != null)
{
localDto.Id = idLocal;
var localUpdated = _mappingServices.Map(localDto, local);
_localServices.Update(localUpdated);
return true;
}
return false;
}
********LocalDto*************
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public SectionDto SectionDto { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
I'm working using ASP.NET WEB API philosophy,that's why I pass the list of adjacent places with a JSON (correctly), because I think the relationship between the objects in the lists with the database record is lost, but I do not understand why, since these local DTOs they are mapped correctly and return the corresponding local object. However, when I update a local with out a any list of positives or negatives locals, no problem.. so i think that problem is with the self many to many relationship.
I have traced the code several times, I check if all the entities have their relationships and everything seems to be fine, but when I try to update the Local entity inserting adjacents locals(positive and negative local) gives me the error that I mentioned above. So, i . I await your answers.Regards
I think what is happening is the following some entity entity framework that is not linking the existing sections in your database when you use the service of automapper, so I suggest that in your Dto not use the relationships for the other dto, for example:
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public SectionDto SectionDto { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
change it to :
public class LocalDto
{
public int Id { get; set; }
public string Number { get; set; }
public float Volumen { get; set; }
public int NumberMaxPeople { get; set; }
public int SectionId { get; set; }
public List<LocalDto> PositivesLocalsDtos { get; set; }
public List<LocalDto> NegativesLocalsDtos { get; set; }
}
and you must also change the mapper associated with these entities,this must be removed from the class LocalMappers,
.ForMember(localDto => localDto.SectionDto, mc => mc.MapFrom(local => local.Section));
This solution is for all your Dtos that have relations, link them to the id of the entity with which it is related not with the Dtos
I hope I've helped
I just upgraded my EF5 projects to EF6.
When I make an automatic migration file I see that it removes cascade deletes from entities. Even if I state it through fluentApi it still doesn't get configured the right way
In Example:
public class Publication
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity), Key]
public int Id { get; set; }
public int? EventId{ get; set; }
public virtual Event EventId { get; set; }
}
public class Event
{
public int Id { get; set; }
public virtual ICollection<Publication> Publications { get; set; }
}
with the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Using this:
modelBuilder.Entity<Publication>()
.HasRequired(p => p.Event)
.WithMany()
.WillCascadeOnDelete(true);
//Or using this:
modelBuilder.Entity<Event>()
.HasMany(e => e.Publications)
.WithRequired(p => p.Event)
.HasForeignKey(p => p.EventId)
.WillCascadeOnDelete(true);
//Or even using both, doesn't work
base.OnModelCreating(modelBuilder);
}
And what happens in my migration file (db was set up with EF5):
public override void Up()
{
DropForeignKey("dbo.Publications", "EventId", "dbo.Events");
DropIndex("dbo.Publications", new[] { "EventId" });
CreateIndex("dbo.Publications", "EventId");
AddForeignKey("dbo.Publications", "EventId", "dbo.Events", "Id");
}
public override void Down()
{
DropForeignKey("dbo.Publications", "EventId", "dbo.Events");
DropIndex("dbo.Publications", new[] { "EventId" });
CreateIndex("dbo.Publications", "EventId");
AddForeignKey("dbo.Publications", "EventId", "dbo.Events", "Id", cascadeDelete: true);
}
So what is going wrong after I upgraded to EF6? When I was using EF5 my cascadeDelete was automatically set to true, so no problems there. I can imagine that cascadeDelete is now default false, but still using 1 of the above stated fluentApi expressions must do the trick?
I have an issue when using TPH and WillCascadeOnDelete(true). When I have the value set on true for cascade on delete my database is not created. The exception message is the following one:
Introducing FOREIGN KEY constraint 'FK_dbo.MembersProfiles_dbo.Contacts_ContactId' on table 'MembersProfiles' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
In order to clear things out here is my model and the mapping used for it.
public class MemberProfile
{
public Guid MemberProfileId { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
public abstract class Contact
{
public Guid ContactId { get; set; }
public Guid AddressId { get; set; }
public Address Address { get; set; }
}
public class PersonContact : Contact
{
public string Profession { get; set; }
public string OrganizationName { get; set; }
}
public class OrganizationContact : Contact
{
public string SalesPhone { get; set; }
public string ServicePhone { get; set; }
}
public class ContactMap : EntityTypeConfiguration<Contact>
{
public ContactMap()
{
ToTable("Contacts");
HasKey(c => c.ContactId);
Property(c => c.ContactId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(c => c.Name).IsRequired().HasMaxLength(50);
Property(c => c.Email).IsRequired().HasMaxLength(150);
Property(c => c.MobilePhone).IsRequired().HasMaxLength(15);
Property(c => c.Description).IsOptional().HasMaxLength(500);
Property(c => c.FixPhone).IsOptional().HasMaxLength(15);
Property(c => c.FaxNumber).IsOptional().HasMaxLength(15);
HasRequired(mp => mp.Address).WithMany().HasForeignKey(mp => mp.AddressId);
HasRequired(mp => mp.Link).WithMany().HasForeignKey(mp => mp.LinkId);
HasRequired(mp => mp.Image).WithMany().HasForeignKey(mp => mp.MediaId);
}
}
public class PersonContactMap : EntityTypeConfiguration<PersonContact>
{
public PersonContactMap()
{
Property(pc => pc.Profession).IsOptional().HasMaxLength(150);
Property(pc => pc.OrganizationName).IsOptional().HasMaxLength(150);
Map(pc => pc.Requires("Discriminator").HasValue("PersonContact").HasColumnType("nvarchar(max)")); }
}
public class OrganizationContactMap : EntityTypeConfiguration<OrganizationContact>
{
public OrganizationContactMap()
{
Property(oc => oc.SalesPhone).IsOptional().HasMaxLength(15);
Property(oc => oc.ServicePhone).IsOptional().HasMaxLength(15);
Map(oc => oc.Requires("Discriminator").HasValue("OrganizationContact").HasColumnType("nvarchar(max)"));
}
}
public class MemberProfileMap : EntityTypeConfiguration<MemberProfile>
{
public MemberProfileMap()
{
ToTable("MembersProfiles");
HasKey(mp => mp.MemberProfileId);
Property(mp => mp.MemberProfileId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(mp => mp.Name).IsRequired().HasMaxLength(50);
Property(mp => mp.DateOfBirth).IsRequired();
Property(mp => mp.Email).IsRequired().HasMaxLength(150);
Property(mp => mp.MobilePhone).IsRequired().HasMaxLength(15);
Property(mp => mp.Summary).IsOptional();
HasRequired(mp => mp.Address).WithMany().HasForeignKey(mp => mp.AddressId).WillCascadeOnDelete(true);
Property(mp => mp.AddressId).HasColumnName("AddressId");
HasOptional(mp => mp.Media).WithMany().Map(mp => mp.MapKey(new[] { "MediaId" })).WillCascadeOnDelete(true);
HasOptional(mp => mp.Tags).WithMany().Map(mp => mp.MapKey(new[] { "TagId" })).WillCascadeOnDelete(true);
HasOptional(mp => mp.Contacts).WithMany().Map(mp => mp.MapKey(new[] { "ContactId" })).WillCascadeOnDelete(true);
}
}
Unfortunately I am not able to realize what am I doing wrong... so any clue would be much appreciated.
P.S: I am using EF 5.0 Code First
See if this BlogPost helps., another option i would try to start adding relation one by one to see where it breaks rather than stacking everything at once. And sometimes it helps to just use individual property mappings rather than Fluent API like you have done. Check out different links on that blog i mentioned above.
Ok, found the issue. I have two tables referring addresses table. In my case both Contacts and MemberProfile hold a reference to Addresses table and in both cases the cascade delete was turned on. Once I turned off the cascade delete on one of the relations everything worked just fine.
I am working with NHibernate and I get this code below:
public class User
{
public User()
{
public virtual int Id { get; set; }
public virtual IList<UserConfirmation> UserConfirmation { get; set; }
public virtual string Email;
}
public User()
{
UserConfirmation = new List<UserConfirmation>();
}
}
public class UserConfirmation
{
public virtual int Id { get; set; }
public virtual User { get; set; }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Email);
HasMany(x => x.UserConfirmation)
.Inverse()
.Cascade.All();
Table("user");
}
}
public class UserConfirmationMap : ClassMap<UserConfirmation>
{
public UserConfirmationMap()
{
Id(x => x.Id);
References(x => x.User);
Table("user_confirmation");
}
}
But when I try to query over like this:
QueryOver<UserConfirmation>().Where(x => x.User.Email).Take(1).SingleOrDefault()
Than it says I do not have the property User.Email.
How can I solve this problem?
Problem is you are using x => x.User.Email
this should be done with another alias, which would be user
UserConfirmation userConfirmationAlias;
User userAlias;
QueryOver<UserConfirmation>(() => userConfirmationAlias)
.joinAlias(() => userConfirmationAlias.User , () => userAlias)
.Where(() => userAlias.Email).Take(1).SingleOrDefault()
something like above should do the trick
I think you want something like this.
QueryOver<User>().Where(x => x.Email).Take(1).SingleOrDefault()
x should be a user already. If it's not I would use the intellisense to see what type it thinks it is.
Try doing:
IQueryOver<UserConfirmation,User> qo = _Session.QueryOver<UserConfirmation,User>().Where(x => x.User.Email).Take(1).SingleOrDefault();
You could use LINQ instead of QueryOver:
Query<UserConfirmation>().Where(x => x.User.Email).Take(1).SingleOrDefault()
BTW, .Take(1).SingleOrDefault() is probably not needed. If Email is unique, .SingleOrDefault() will be enough. Otherwise, you can use .FirstOrDefault()