In my project i need to add Sales leads to the data context. The sales person user adds the leads and I need to send the email to manager for the Lead.
public partial class Lead
{
public Lead()
{
this.LeadActivities = new HashSet<LeadActivity>();
}
public long LeadID { get; set; }
public System.DateTime CreatedAt { get; set; }
public long CompanyID { get; set; }
public long ProductID { get; set; }
public long CreatedByUserID { get; set; }
public string Remarks { get; set; }
public LeadStatusEnum StatusID { get; set; }
public virtual Company Company { get; set; }
public virtual Product Product { get; set; }
public virtual User User { get; set; }
public virtual ICollection<LeadActivity> LeadActivities { get; set; }
}
[Serializable]
public partial class Person
{
public Person()
{
this.Contacts = new HashSet<Contact>();
this.Users = new HashSet<User>();
}
public long PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Designation { get; set; }
public Nullable<int> Gender { get; set; }
public Nullable<int> Title { get; set; }
public int StatusID { get; set; }
public string Thumbnail { get; set; }
public string Email { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In the above entity, I have property UserID that is associated to Person table through CreatedByUserID. When I add the new lead, by following code, the User field remains null. Do I need to reconstruct it? if yes then how.
Edit1
Entity Creation is done by following code
Entity = new Model.Lead
{
CreatedAt = DateTime.Now,
CreatedByUserID = SessionManagement.GeneralSession.UserDetail.UserID
};
Entity.CreatedAt = Convert.ToDateTime(txtTimestamp.Value);
Entity.CompanyID = Convert.ToInt64(ddlCompany.SelectedValue);
Entity.CreatedByUserID = Convert.ToInt64(ddlUser.SelectedValue);
Entity.ProductID = Convert.ToInt64(lstProducts.SelectedValue);
Entity.Remarks = txtRemarks.Text;
DataSource.Leads.Add(Entity);
DataSource.SaveChanges();
Virtual lazy loading only works with proxy instances. Since you're explicitly constructing your Lead entity, lazy loading of the User navigation property after inserting the entity will not work.
Instead, you should use the DbSet.Create method to new up an instance of the derived proxy type. Then perform your insert, which will attach to the context, and lazy loading will subsequently work.
Alternatively, you can use your existing POCO, perform the insert and then fetch your inserted entity as its proxy from the DbSet by using the DbSet.Find method.
You should also check and make sure your foreign key id and navigation properties are correctly mapped, since properties CreatedByUserID and User would not be automatically associated by convention.
Related
I created local database using EF core and code-first method.
The db imitates library, so I have 3 simple tables: users, books and reservations.
Issue occurs when I want to get nested data like find one book and get its reservation.
I think I should be able to use
List<Reservation> reservations = book.Reservations;
but I have to use
List<Reservation> reservations = libraryContext.Reservations.
Where(r=> r.Book == book).ToList();
But the main reason I need help is this fragment
BookReservationsModel bookReservationsModel = new BookReservationsModel
{
BookTitle = book.Title,
Reservations = reservations
};
// I want to display emails in View.
for (int i = 0; i < bookReservationsModel.Reservations.Count; i++)
{
Debug.WriteLine(bookReservationsModel.Reservations[i].User.Email);
}
I cannot get access to users because they are nulls. In database everything is stored as it should be (correct ids). Of course I could copypaste certain emails to new created list but it's inefficient and I know I should be able to use it that way. I worked before with EF for Framework and I tried google the problem but couldn't find the solution.
Models and context code.
public class User
{
[Key]
public int UserID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Salt { get; set; }
[Required]
public string Password { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
public class Book
{
[Key]
public int BookID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Author { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
[Required]
public string Description { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
public class Reservation
{
[Key]
public int ReservationID { get; set; }
[Required]
public DateTime ReservationDate { get; set; }
[Required]
public int UserID { get; set; }
[ForeignKey("UserID")]
public User User { get; set; }
[Required]
public int BookID { get; set; }
[ForeignKey("BookID")]
public Book Book { get; set; }
}
public class LibraryContext : DbContext
{
public LibraryContext(DbContextOptions options) : base(options) { }
public DbSet<User> Users { get; set; }
public DbSet<Book> Books { get; set; }
public DbSet<Reservation> Reservations { get; set; }
}
Attempting to use a navigation property on the book entity without doing either of the following will result in the property being null.
Including the property before materializing the entity with .First()/.Single()
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager
Configuring EFCore to AutoInclude navigation properties by default.
https://dotnetcoretutorials.com/2021/03/07/eager-load-navigation-properties-by-default-in-ef-core/
I suggest when querying for the book to use the Include and ThenInclude methods so the Reservations and Users are populated.
var book = await libraryContext.Books
.Include(x => x.Reservations)
.ThenInclude(x => x.User)
.SingleAsync(x => x.BookID == myBookId);
I have created an MVC using Entity framework and I've encountered a situation which I don't know how to resolve.
I'm using the EF auto joins and relations (all my table models were created automatically by EF) .
Now for the problem - I have a table of customers, which has two(relavent) fields - personID and employerID . Only one of them contains data , the other will be null (a customer is either a person , or an employer) . When I try to include employer model in the result set, I'm getting thrown (without any message , when I debug I see that the content has data but the employeer is sometimes NULL) I'm also not sure about how the design should look like. This is my code :
Customer:
public partial class Customer
{
public Customer()
{
Account = new HashSet<Account>();
}
public long Id { get; set; }
public int? PersonId { get; set; }
public int Type { get; set; }
public int? EmployerId { get; set; }
public Employer Employer { get; set; }
public ICollection<Account> Account { get; set; }
}
Employer:
public partial class Employer
{
public Employer()
{
Customer = new HashSet<Customer>();
}
public int Id { get; set; }
public string Name { get; set; }
public int? IdType { get; set; }
public ICollection<Customer> Customer { get; set; }
}
Person:
public partial class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Sex { get; set; }
public DateTime? BirthDate { get; set; }
public int IdType { get; set; }
}
Now when I'm running in my repository:
var collectionBeforePaging = _context.Customer
Everything works, but Employer is NULL. If I use :
var collectionBeforePaging = _context.Customer.Include(a => a.Employer)
Then the project fails .
How can I make this joins?
Please define ForeignKey for Customer
public int? EmployerId { get; set; }
[ForeignKey(nameof(EmployerId))]
public virtual Employer Employer { get; set; }
What version of EF you use? I think you missing something like that :
In Employer :
[ForeignKey("EmployerId")]
[InverseProperty("Customers")]
public virtual Employer Employer { get; set; }
public int? EmployerId { get; set; }
In Customer :
[InverseProperty("Employer")]
public virtual ICollection<Customer> Customers { get; set; }
It can also be done in the Dbontext object
I've created a foreign key reference to the 'ApplicationUser' entity in my 'YogaSpace' entity seen below, but when I create a new YogaSpace and save it to the DB the column 'ApplicationUserRefId' is null? Shouldn't it contain the user id from application user without me inserting it into the entity before I save it? I know the answer would seem to be yes because I have another member in 'YogaSpace' called Images and it gets the 'YogaSpace' entity id automatically without me inserting it into the 'Images' entity before I save. So I don't have to insert the id there. How come it doesn't insert the user id with 'YogaSpace'?
public class YogaSpace
{
public int YogaSpaceId { get; set; }
public virtual ICollection<YogaSpaceImage> Images { get; set; }
[MaxLength(128)]
public string ApplicationUserRefId { get; set; }
[ForeignKey("ApplicationUserRefId")]
public virtual ApplicationUser ApplicationUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public DateTime MembershipCreated { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Birthdate { get; set; }
public Gender Gender { get; set; }
public virtual ICollection<YogaSpace> YogaSpaces { get; set; }
}
public class YogaSpaceImage
{
public int YogaSpaceImageId { get; set; }
public byte[] Image { get; set; }
public byte[] ImageThumbnail { get; set; }
public string ContentType { get; set; }
public int Ordering { get; set; }
[Index]
public int YogaSpaceRefId { get; set; }
[ForeignKey("YogaSpaceRefId")]
public virtual YogaSpace YogaSpace { get; set; }
}
Here is how I create a 'YogaSpace' entity. You can see I don't put anything into 'ApplicationUserRefId', BUT I expect it to autopopulate with user id from Application User, just like YogaSpacerefId gets auto populated when I create an image and save it.
var newSpace = new YogaSpace
{
Overview = new YogaSpaceOverview
{
Title = viewModel.Title,
Completed = ListingComplete.Incomplete
},
Listing = new YogaSpaceListing
{
Accommodation = (YogaSpaceAccommodation)viewModel.YogaSpaceAccommodation,
SpaceLocation = (YogaSpaceLocation)viewModel.YogaSpaceLocation,
SpaceType = (YogaSpaceType)viewModel.YogaSpaceType
},
Details = new YogaSpaceDetails
{
Completed = ListingComplete.Complete
},
Address = new YogaSpaceAddress
{
Completed = ListingComplete.Incomplete
},
DateCreated = DateTime.Now
};
yogaSpaceRepository.InsertOrUpdate(newSpace);
yogaSpaceRepository.Save();
I'm new to ASP.NET MVC and EF hopefully this is not a silly question
When i pass model to view i'm getting this error - Exception Details: System.Data.SqlClient.SqlException: Invalid column name 'Environment_Id'.
Model nor database table has a property by that name. Could any guide me on this?.
**Here is the Version Model Class**
public partial class Version
{
public Version()
{
this.ProfileVersions = new List<ProfileVersion>();
this.ServerInfoes = new List<ServerInfo>();
}
public int Id { get; set; }
public string Number { get; set; }
public string Tag { get; set; }
public string Owner { get; set; }
public string Approver { get; set; }
public string Description { get; set; }
public virtual ICollection<ProfileVersion> ProfileVersions { get; set; }
public virtual ICollection<ServerInfo> ServerInfoes { get; set; }
}
**Profile Version Class**
public partial class ProfileVersion
{
public ProfileVersion()
{
this.PlatformConfigurations = new List<PlatformConfiguration>();
}
public int Id { get; set; }
public int ProfileId { get; set; }
public int EnvironmentId { get; set; }
public int VersionId { get; set; }
public Nullable<bool> Locked { get; set; }
public string LockedBy { get; set; }
public string Comments { get; set; }
public Nullable<int> Active { get; set; }
public virtual Environment Environment { get; set; }
public virtual ICollection<PlatformConfiguration> PlatformConfigurations { get;
set; }
public virtual PlatformProfile PlatformProfile { get; set; }
public virtual Version Version { get; set; }
}
**ServerInfo**
public partial class ServerInfo
{
public ServerInfo()
{
this.PlatformConfigurations = new List<PlatformConfiguration>();
}
public int Id { get; set; }
public string ServerName { get; set; }
public int ProfileId { get; set; }
public int VersionId { get; set; }
public int EnvironmentId { get; set; }
public string ServerType { get; set; }
public Nullable<short> Active { get; set; }
public string Domain { get; set; }
public string Location { get; set; }
public string IP { get; set; }
public string Subnet { get; set; }
public string Gateway { get; set; }
public Nullable<int> VLan { get; set; }
public string DNS { get; set; }
public string OS { get; set; }
public string OSVersion { get; set; }
public string Func { get; set; }
public Nullable<short> IISInstalled { get; set; }
public string ADDomainController { get; set; }
public string ADOrganizationalUnit { get; set; }
public string ADGroups { get; set; }
public string LastError { get; set; }
public Nullable<System.DateTime> LastUpdate { get; set; }
public virtual Environment Environment { get; set; }
public virtual ICollection<PlatformConfiguration> PlatformConfigurations { get;
set; }
public virtual PlatformProfile PlatformProfile { get; set; }
public virtual Version Version { get; set; }
public virtual VMConfiguration VMConfiguration { get; set; }
}
**Controller Code-**
public ViewResult Index(string id )
{
var profileVerList = from ver in _context.Versions
where !(from pfv in _context.ProfileVersions
select pfv.VersionId).Contains(ver.Id)
select ver;
var bigView = new BigViewModel
{
VersionModel = profileVerList.ToList(),
};
return View(model: bigView);
}
**In the View where the exception is thrown**
#Html.DropDownList(
"SelectedVersionID",
new SelectList(
Model.VersionModel.Select(x => new { Value = x.Id, Text = x.Number}),
"Value",
"Text"
)
)
In your ProfileVersion and ServerInfo entities you have an Environment navigation property. By default, Entity Framework will try to create a database column called [Property Name]_[Referenced class PK]. In your scenario, that's Environment_Id. The problem, right now, is that you have not done a migration to have this database column created.
If I had to imagine what happened here, I'd say you first created the classes with EnvironmentId properties, migrated, then later decided to add the navigation properties, Environment to each, expecting EF to associate that with your existing EnvironmentId properties. That's where you went wrong. As I said above, EF convention is to look for a database column named Environment_Id, so if you want EF to use EnvironmentId instead, you just need to tell it so with the ForeignKey data annotation:
[ForeignKey("Environment")]
public int EnvironmentId { get; set; }
In My Case I have added My Primary Key Relationship to Same Key .. SO I have simply remove..
I realize this question is 3 years old now, but I saw a different reason for the error - both in the original question and in my own code that was pretty similar. And, in my case, I had the same error as stated above.
I had a "MY_ACTIONS" table with an ID and Name pair that I wanted to be added to a dropdown. Here's the model:
namespace TestSite.Models
{
public class MY_ACTIONS
{
//[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MY_ACTIONS()
{
this.o_actions = new HashSet<MY_ACTIONS>();
}
[Key]
public int action_id { get; set; }
[StringLength(100)]
public string action_name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MY_ACTIONS> o_actions { get; set; }
}
}
And to get an action to display on the dropdown it had an ID set in an int field called LASTACTION in my main table. In that model I had declared the ForeignKey relationship:
namespace TestSite.Models
{
[Table("MAIN_TABLE")]
public partial class MAIN_TABLE
{
[Key]
public int MAIN_TABLE_ID { get; set; }
public int LASTACTION { get; set; } // this would carry a number matching action_id
[ForeignKey("LASTACTION")]
public virtual MY_ACTIONS MY_ACTIONS { get; set; }
}
}
I had the error Invalid column name 'MY_ACTIONS_action_id' when loading this dropdown in my view:
#Html.DropDownList("lastaction", null, htmlAttributes: new { #class = "form-control" })
...for which I was using this ViewBag in my Controller function:
Model1 db = new Model1(); // database context
MAIN_TABLE o_main = new MAIN_TABLE();
o_main.lastaction = 2;
ViewBag.lastaction = new SelectList(db.MY_ACTIONS, "action_id", "action_name", o_main.lastaction);
If I did not have my FK relationship declared:
[ForeignKey("LASTACTION")]
public virtual MY_ACTIONS MY_ACTIONS { get; set; }
I probably also would've had the same issue. Having the representation of a virtual instance requires linking it with some physical property. This is similar to how this:
public virtual Environment Environment { get; set; }
Should be:
[ForeignKey("EnvironmentId")]
public virtual Environment Environment { get; set; }
in the ProfileVersion class, in the question, above, assuming that EnvironmentId is the Primary Key in a table called Environment (that model is not shown above).
For me, though, I already had that and I was still getting the error, so doing that still might not solve everything.
Turns out all I had to do was get rid of that ICollection<MY_ACTIONS> o_actions in the MY_ACTIONS model and the this.o_actions = new HashSet<MY_ACTIONS>(); line and it all went through fine.
There are many such lists and ICollections in play in the question above, so I would wager something is wrong with having them, as well. Start with just a plain model that represents the fields, then add in your virtual objects that represent tables linked to with foreign keys. Then you make sure your dropdown loads. Only after that should you start adding in your ICollections, HashSets, Lists<T> and other such amenities that are not actually physically part of the database - this can throw off Entity Framework into thinking it needs to do something with them that it doesn't need to do.
I have a working model, but have noticed that the relationship has been created twice in the database. Originally, it created two columns in the table, but with the addition of a specified foreign key attribute it has now just the one.
I have an Account class, which has many employers who can use the account. (one to many) Here are the classes:
public class Account
{
public int AccountId { get; set; }
[Required(ErrorMessage = Constants.ValidationMessages.FieldRequired)]
public string Name { get; set; }
public int? PrimaryUserId { get; set; }
[ForeignKey("PrimaryUserId")]
public Employer PrimaryUser { get; set; }
[ForeignKey("EmpAccountId")]
public ICollection<Employer> Employers { get; set; }
}
here is the inherited Employer class
public class Employer : User
{
public Employer()
{
DepartmentsToPost = new Collection<Department>();
Contacts = new Collection<Contact>();
}
[Display(Name = "Workplaces to advertise jobs")]
public virtual ICollection<Department> DepartmentsToPost { get; set; }
public int EmpAccountId { get; set; }
[ForeignKey("EmpAccountId")]
public virtual Account Account { get; set; }
public override string UserType
{
get { return "Employer"; }
}
}
User Table:
UserId
Username
FirstName
Surname
EmpAccountId
Discriminator
Account Table
AccountId
Name
PrimaryUserId
There is one link back to the User table - this is for the PrimaryUser field, and this is correct. There are two other relationships: Account -> Employers. EF has named them Account_Employers and Employer_Account. These are duplicates.
How can I prevent this occuring?
The Employers collection should be decorated with InversePropertyAttribute to point to the navigational property on the other side.
public class Account
{
public int AccountId { get; set; }
[Required(ErrorMessage = Constants.ValidationMessages.FieldRequired)]
public string Name { get; set; }
public int? PrimaryUserId { get; set; }
[ForeignKey("PrimaryUserId")]
public Employer PrimaryUser { get; set; }
[InverseProperty("Account")]
public ICollection<Employer> Employers { get; set; }
}