//EDMX File
http://pastebin.com/btTCRMf7
I have 2 tables Customers and Sites
//Site
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int CustomerID { get; set; }
public int CityID { get; set; }
public int CountryID { get; set; }
public int EncodedBy { get; set; }
public System.DateTime DateEncoded { get; set; }
public virtual City City { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
public virtual User User { get; set; }
public virtual Customer Customer { get; set; }
//Customer
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int CityID { get; set; }
public int CountryID { get; set; }
public int CreditTermID { get; set; }
public int EncodedBy { get; set; }
public System.DateTime DateEncoded { get; set; }
public virtual City City { get; set; }
public virtual Country Country { get; set; }
public virtual CreditTerm CreditTerm { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//Country
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//City
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<Site> Sites { get; set; }
//SiteModel
private static IQueryable<Site> Build(this DbSet<Site> query)
{
return query.Include("User").Include("City").Include("Country").Include("Customer");
}
public static Site Find(int siteID)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
Site result = context.Sites.Build().SingleOrDefault(s => s.ID == siteID);
return result;
}
}
public static Site Update(Site _updatedSite)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
context.Sites.Attach(_updatedSite);
context.Entry(_updatedSite).State = EntityState.Modified;
context.SaveChanges();
return Find(_updatedSite.ID);
}
}
Site test = SiteModel.Find(1);
test.City = null;
test.CityID = 1;
test.Country = null;
test.CountryID = 1;
test.Customer = null;
test.CustomerID = 1;
SiteModel.Update(test);
i am getting A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
however, adding test.Customer.City = null; before updating the object would work. it seems like Customer.City and Site.City is conflicting. can somebody explain why? or any workaround?
I can explain why. Include persists entity to load all the include objects.So our site object has all references to your clusses (City, Country,User Customer). I think that is the problem. The solve can be to load only site object:
Site result = context.Sites.SingleOrDefault(s => s.ID == siteID);
so it would load only ids of the site object. Than you can load refered objects by it ids in runtime where you need.
I think it is because when you use include, you operate objects and collections of objects dbContext tracks this changes and saves them when you call
context.SaveChanges();
a bit refactored code btw:
public static Site Update(Site _updatedSite)
{
using (DragonRentalsEntities context = new DragonRentalsEntities(new ConfigurationManager().ConnectionString))
{
if (context.Entry(_updatedSite).State == EntityState.Detached)
context.Entry(_updatedSite).State = EntityState.Modified;// attaches entity and marks it as modified if it is detached
context.SaveChanges();
return _updatedSite; //after save changes u have the same object as u send in your Update function
}
}
Comment answer
Slauma if i don't set it to null, i won't be able to attach them in the update method, the same error would be triggered
Answer:
Because when you include all entities you have got already attached to your context objects.
Btw Include transforms on sql inner join statement so may be your snapshot of db objects doesnt contain City with that ID.
Related
I am working on Asp.net core 5 Web-api, I am using many to many relationship between User entity and Permission entity, and I need to load users permission from the database using eager loading but the data is returning a looped information including users full information but I only load the permissions. I am using a Dto to return the Json data. Here is my models and my code
Permission Model
public class Permission
{
public int Id { get; set; }
public string ClaimName { get; set; }
public IList<UserPermission> UserPermissions { get; set; }
}
UserPermission model
public class UserPermission
{
public int UserId { get; set; }
public AppUser AppUser { get; set; }
public int PermissionId { get; set; }
public Permission Permission { get; set; }
}
User model
public class AppUser
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Photo> Photos { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public DateTime LastActive { get; set; }
public IList<UserPermission> UserPermissions { get; set; }
public bool Status { get; set; }
}
My user Dto
public class UserDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Photo> Photos { get; set; }
public ICollection<UserPermission> UserPermissions { get; set; }
public string Token { get; set; }
}
My function
var sss = _context.UserPermissions
.Include(l => l.Permission)
.Where(v => v.UserId == user.Id)
.ToList();
Use [JsonIgnore] on the properties that we want to exclude.
note that we should use "using Newtonsoft.Json" name space not using "System.Text.Json.Serialization"
I have the following POCO classes:
public class Employees
{
public int EmployeeId
{
get;
set;
}
public int EmpImageId
{
get;
set;
}
public string EmployeePhotoUrl
{
get;
set;
}
public string EmpAddress
{
get;
set;
}
}
public class Images
{
public int ImageId
{
get;
set;
}
public string ImageUrl
{
get;
set;
}
public string ImgCode
{
get;
set;
}
}
public class EmployeeDTO
{
public int EmployeeId
{
get;
set;
}
public int EmpImageId
{
get;
set;
}
public string EmployeePhotoUrl
{
get;
set;
}
public string EmpAddress
{
get;
set;
}
}
While getting the list of Employees present in the system, for each Employee, the photourl (EmployeePhotoUrl) is fetched from the Images table using the EmpImageId property.
// Get the list of Employees
var employees = await _dbContext.Employees
.Select(ef => new EmployeeDTO
{
EmployeeId= ef.EmployeeId,
EmployeePhotoUrl = images.FirstOrDefault(im => im.ImageId.Equals(ef.EmpImageId)).EmployeePhotoUrl,
EmpAddress = ef.EmpAddress
}).Skip((pageNo - 1) * 100).Take(pageSize).ToListAsync();
I want to leverage Automapper in this case. The issue I see here is the assignment of EmployeePhotoUrl, since this property is fetched from another entity: Images
Can anyone help me to know how to leverage Automapper in this case.
I'm writing a simple .net core application with Entity Framework. I have a situation where reference of related entity is null after saving the record. Please see below code and screen shots for more understanding.
As you can see in the below pic that reference is created with the Organization Entity.
But when I get all the Listing from the context then reference is gone.
Organization Entity
public class Organization
{
public Organization()
{
this.Listings = new List<Listing>();
this.ContactRoles = new List<ContactRole>();
}
[Key]
public int Id { get; set; }
public string OrgName { get; set; }
public string OrgWebsite { get; set; }
public string OrgMissionStatement { get; set; }
public string OrgOwner { get; set; }
public string OrgDescription { get; set; }
public bool IsVerified { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
//public string Country { get; set; }
//ForeignKey
[ForeignKey("Contact")]
public int ContactId { get; set; }
public virtual Contact Contact { get; set; }
public virtual List<ContactRole> ContactRoles { get; set; }
public virtual List<Listing> Listings { get; set; }
}
Listing Entity
[Table("Listing")]
public class Listing
{
[Key]
public int Id { get; set; }
public string ListingTitle { get; set; }
public string ListingDescription { get; set; }
[ForeignKey("Organization")]
public int OrgId { get; set; }
public virtual Organization Organization { get; set; }
}
Controller
public IEnumerable<Listing> GetAll()
{
return _context.Listings;
}
public Listing GetById(int id)
{
return _context.Listings.Find(id);
}
public Listing Create(Listing listing)
{
try
{
var org = _context.Organizations.SingleOrDefault(x => x.Id == listing.OrgId);
org.Listings.Add(listing);
_context.Listings.Add(listing);
_context.SaveChanges();
return listing;
}
catch (Exception ex)
{
throw ex;
}
}
Currently im using Include in the Linq to get the related entities but i need all related entities without using Include.
This is because references are not loaded by default in EF Core.
when you want them, You have to force Include.
_context.Listings.Include(m=>m.Organization)
You can repeat this for any other field that you want to load.
I have to classes, VisitModel and VisitorModel, A visitModel can have many VisitorModel and vice versa. After adding both relationship to the modelBuilder, its still not saving to the third auto-generated table. It created the table, but when I try to save, it does not save both Ids. Here is my code:
public class VisitorModel
{
public int VisitorModelId { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public bool IsDeleted { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string PhotoUrl { get; set; }
public string Address { get; set; }
public int BranchId { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
public virtual ICollection<VisitModel> VisitModels { get; set; }
}
and here is the VisitModel class:
public class VisitModel
{
public VisitModel()
{
VisitorModels = new List<VisitorModel>();
}
public int VisitModelId { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public bool IsDeleted { get; set; }
public string UserToVisit { get; set; }
public virtual User User { get; set; }
public string HostEmail { get; set; }
public int UserId { get; set; }
public int VisitorTypeId { get; set; }
public int PurposeOfVisitId { get; set; }
public string OtherPurposeOfVisit { get; set; }
public DateTime ArrivalDate { get; set; }
public DateTime ArrivalTime { get; set; }
public DateTime DepartureTime { get; set; }
public DateTime DepartureDate { get; set; }
public bool IsCheckedIn { get; set; }
public bool IsCheckedOut { get; set; }
public string BookedBy { get; set; }
public bool IamHost { get; set; }
public virtual ICollection<VisitorModel> VisitorModels { get; set; }
}
And this is the modelBuilder:
modelBuilder.Entity<VisitModel>()
.HasMany(up => up.VisitorModels)
.WithMany(course => course.VisitModels)
.Map(mc =>
{
mc.ToTable("VisitVisitorModel");
mc.MapLeftKey("VisitModelId");
mc.MapRightKey("VisitorModelId");
});
When I try to save, it does not save both Ids to the VisitVisitorModel table. The table is always empty. Please what am I doing wrong?
Because you followed the entity framework many-to-many conventions, you don't need a fluent API part to design a junction table.
Entity framework knows, just by looking at your class definitions, that you designed a many-to many relation. Entity Framework will design a proper junction table for you. You should not even mention this junction table in your dbContext:
class MyDbContext : DbContext
{
public DbSet<VisitModel> VisitModels {get; set;}
public DbSet<VistitorModel> VistiroModels {get; set}
}
But if I have no DbSet for my junction table, how can I do my joins on VisitModel and VisitorModel?
The answer is: don't use joins, use the ICollections;
Give me Names and Address of all VisitorModels that are not deleted togeter with all the arrival times and departure times of their VisitModels that have checked in Today
Normally you would join VisitorModel with the junction table VisitVisitorModel on VisitModelId and join again on VisitModel on VisitorModelId.
But you could also use the ICollectioins:
var result = myDbContext.VisitorModels
.Where(visitorModel => !visitorModel.IsDeleted)
.Select(visitorModel => new
{
// select only the properties of visitorModel you will be using:
FullName = visitorModel.FullName,
Address = visitorModel.Address,
Phone = visitorModel.Phone,
... // other properties
// get ArrivalTime and DepartureTime of all VisitModels that are checked in
VisitModels = visitorModel.VisitModels
.Where(visitModel => visitModel.IsCheckedInd)
.Select(visitModel => new
{ // again: select only the properties you will be using:
ArrivalTime = visitModel.ArrivalTime,
DepartureTime = visitModel.DepartureTime,
... // other properties
})
.ToList(),
}
}
Because of the proper many-to-many configuration you won't need the junction table, and you won't need to do joins with primary keys and foreign keys. Entity framework will know which joins to do.
I work with EF for the first time so I don't know is situation like this normal or I have serious performance issues.
I have following situation:
Bellow are the classes that I have. Item is the main object here. So when I pull a list of Items from database I get for example 1000 items. And now each of this item has all properties filed with data. City contains Country, Country contains list of cities, User has list of created items, each item all data again, city, city has country, country list of cities etc etc...
Maybe I am worrying too much and I don't know should this object's contain all those data and does this make performance issues, or I am doing something wrong here?
public abstract class Item
{
[Key]
public int ItemId { get; set; }
public int ItemTypeId { get; set; }
public Guid UserId { get; set; }
public DateTime CreatedOnDate { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int? MediaId { get; set; }
public int CityId { get; set; }
public virtual City City { get; set; }
public virtual User User { get; set; }
public virtual ICollection<ItemInBoard> ItemsInBoard { get; set; }
public virtual ICollection<Like> Likes { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string Name { get; set; }
public string CountryCode { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
public class User
{
public Guid UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Gender { get; set; }
public DateTime? BirthDay { get; set; }
public string AboutMe { get; set; }
public int? MediaId { get; set; }
public int CityId { get; set; }
public virtual City City { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Board> Boards { get; set; }
public virtual ICollection<Like> Likes { get; set; }
}
It is up to you. This is a concept called lazy loading. You can enable or disable lazy loading with this code:
context.Configuration.LazyLoadingEnabled = false;
context.Configuration.LazyLoadingEnabled = true;
When enabling this option none of the dependent entities will be loaded. To enforce dependent entities to load you can use the Include lambada expression like this:
var test = context.Tests.Include("SomeOtherDependentEntity");
Hope I got you and this is what you meant.
I would say that what you have is fine for general business logic.
When I have to do a lot of time sensitive processing in a read-only fashion I use SQL commands like this to get exactly and only exactly what I want.
public class myQueryClass
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
var context = new MyDbContext();
context.Database.SqlQuery<myQueryClass>("SELECT Property1 = acolumn, Property2 = acolumn2 FROM myTable WHERE something = somestate");