I have a base class Person:
[KnownType(typeof(Doctor))]
public abstract class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public DateTime BirthDate { get; set; }
public string Email { get; set; }
public string MobilePhoneNumber { get; set; }
public string HomePhoneNumber { get; set; }
public bool IsGlobal { get; set; }
public bool IsDeleted { get; set; }
public bool IsApproved { get; set; }
public int? FacilityId { get; set; }
public int? AddressId { get; set; }
public virtual FacilityView Facility { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Organization> Organizations { get; set; }
public virtual ICollection<TenantEntity> TenantEntities { get; set; }
}
And have nested class Doctor:
public class Doctor : Person
{
public string Speciality { get; set; }
}
Table Person script
CREATE TABLE [core].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[Discriminator] [nvarchar](255) NOT NULL,
[FirstName] [nvarchar](255) NOT NULL,
[MiddleName] [nvarchar](255) NULL,
[LastName] [nvarchar](20) NOT NULL,
[Gender] [nvarchar](20) NOT NULL,
[BirthDate] [date] NOT NULL,
[Email] [nvarchar](250) NULL,
[MobilePhoneNumber] [nvarchar](250) NULL,
[HomePhoneNumber] [nvarchar](250) NULL,
[IsGlobal] [bit] NOT NULL,
[IsDeleted] [bit] NOT NULL,
[IsApproved] [bit] NOT NULL,
[FacilityId] [int] NULL,
[AddressId] [int] NULL,
[Speciality] [nvarchar](250) NULL,
And when i try to save new Doctor entity i have an error:
Cannot insert the value NULL into column 'Discriminator'
What i do wrong in this situation?Why EF not save "Doctor" value in Discriminator field?
UPDATE:
part from DBContext:
public DbSet<Person> Persons { get; set; }
#region Person
modelBuilder.Entity<Person>()
.ToTable("Person", "core")
.HasKey(t => t.PersonId);
modelBuilder.Entity<Person>()
.HasOptional(t => t.Facility);
modelBuilder.Entity<Person>()
.HasOptional(t => t.Address);
modelBuilder.Entity<Person>()
.HasMany(x => x.Organizations)
.WithMany()
.Map(x =>
{
x.MapLeftKey("PersonId");
x.MapRightKey("OrganizationId");
x.ToTable("PersonOrganization", "core");
});
modelBuilder.Entity<Person>()
.HasMany(x => x.TenantEntities)
.WithMany()
.Map(x =>
{
x.MapLeftKey("PersonId");
x.MapRightKey("TenantEntityId");
x.ToTable("PersonTenantEntity", "core");
});
#endregion
The default inheritance mapping does not define a discriminator column if only one derived class exists.
Your code will work if you define a second class that derives from Person:
public class Nurse : Person
{
public string Whatever {get;set;}
}
Or add the discriminator explicitly to your model.
modelBuilder.Entity<Doctor>().Map(p => p.Requires("Discriminator").HasValue("Doctor"));
Related
I have tried various answers to try and get this fixed. I have tried to do a refresh but with no luck.
All my tables are currently empty. I already have #Html.HiddenFor~ in the view
My controller save action:
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "AdminManager")]
public ActionResult Save(Member member)
{
if (!ModelState.IsValid)
{
var viewModel = new MemberFormViewModel
{
Member = member,
Subjects = _context.Subjects.ToList()
};
return View("MemberForm", viewModel);
}
if (member.Id == 0)
_context.Members.Add(member);
else
{
var memberInDb = _context.Members.Single(m => m.Id == member.Id);
Mapper.Map(memberInDb, member);
}
_context.SaveChanges();
return RedirectToAction("Index", "Member");
}
Specifically the source error shown is:
_context.SaveChanges();
It seems that the code errors when adding a member:
_context.Members.Add(member);
Below is my table code:
CREATE TABLE [dbo].[Members] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (255) NOT NULL,
[DateOfBirth] DATETIME NULL,
[Gender] NVARCHAR (MAX) NULL,
[Email] NVARCHAR (MAX) NOT NULL,
[WorkLocation] NVARCHAR (MAX) NOT NULL,
[Address] NVARCHAR (255) NOT NULL,
[ContactNumber] NVARCHAR (MAX) NOT NULL,
[Biography] NVARCHAR (MAX) NULL,
[MoreInfo] NVARCHAR (MAX) NULL,
[SubjectId] INT NOT NULL,
[ReportId] INT NULL,
CONSTRAINT [PK_dbo.Members] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Members_dbo.Reports_ReportId] FOREIGN KEY ([ReportId])
REFERENCES [dbo].[Reports] ([Id]),
CONSTRAINT [FK_dbo.Members_dbo.Subjects_SubjectId] FOREIGN KEY ([SubjectId])
REFERENCES [dbo].[Subjects] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_SubjectId]
ON [dbo].[Members]([SubjectId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_ReportId]
ON [dbo].[Members]([ReportId] ASC);
Here is my model that was used for the migration:
public class Member
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Display(Name = "Date of birth")]
public DateTime? DateOfBirth { get; set; }
public string Gender { get; set; }
[Required]
public string Email { get; set; }
[Required]
[Display(Name = "Work Location")]
public string WorkLocation { get; set; }
[Required]
[StringLength(255)]
public string Address { get; set; }
[Required]
[Display(Name = "Contact Number")]
public string ContactNumber { get; set; }
public string Biography { get; set; }
[Display(Name = "More Information")]
public string MoreInfo { get; set; }
public Subject Subject { get; set; }
[Required]
[Display(Name = "Subject")]
public int SubjectId { get; set; }
public Report Report { get; set; }
[Key]
[ForeignKey("Report")]
[Display(Name = "Report")]
public int? ReportId { get; set; }
}
I am using an existing database to create an Project Reporting System. And I want the Primary Key, which is the Project id value of the project table to be automatically created and it is not setting a new value, as I have to input the Id, but I don't want the user to do that. Can anyone help me with this problem please?
Patrick
Controller
` //
// GET: /Project/Create
public ActionResult Create()
{
ViewBag.Status = new SelectList(db.pjt_Statuses, "pjt_Status_ID", "StatusName");
ViewBag.SubCategoryID = new SelectList(db.pjt_SubCategories, "pjt_SubCat_ID", "SubCatName");
return View();
}
//
// POST: /Project/Create
[HttpPost]
public ActionResult Create(pjt_Projects pjt_projects)
{
if (ModelState.IsValid)
{
pjt_projects.CreationDate = DateTime.Now;
db.pjt_Projects.Add(pjt_projects);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Status = new SelectList(db.pjt_Statuses, "pjt_Status_ID", "StatusName", pjt_projects.Status);
ViewBag.SubCategoryID = new SelectList(db.pjt_SubCategories, "pjt_SubCat_ID", "SubCatName", pjt_projects.SubCategoryID);
return View(pjt_projects);
}`
Database Table Script
`CREATE TABLE [dbo].[pjt_Projects](
[Pjt_Id] [int] IDENTITY(1,1) NOT NULL,
[ProjectName] [nchar](100) NULL,
[Status] [int] NULL,
[Start_Date] [date] NULL,
[Estimated_End_Date] [date] NULL,
[Documents] [nvarchar](max) NULL,
[Notes] [nvarchar](max) NULL,
[Budget] [smallmoney] NULL,
[Current_Spending] [smallmoney] NULL,
[ProjectOwner] [nvarchar](50) NULL,
[Active] [bit] NULL,
[Actual_End_date] [date] NULL,
[CreationDate] [date] NULL,
[CreatedByEmpNo] [nchar](10) NULL,
[UpdateDate] [date] NULL,
[UpdatedByEmpNo] [nchar](10) NULL,
[SubCategoryID] [int] NULL,`
**Generate Context Db Code**
public int Pjt_Id { get; set; }
public string ProjectName { get; set; }
public Nullable<int> Status { get; set; }
public Nullable<System.DateTime> Start_Date { get; set; }
public Nullable<System.DateTime> Estimated_End_Date { get; set; }
public string Documents { get; set; }
public string Notes { get; set; }
public Nullable<decimal> Budget { get; set; }
public Nullable<decimal> Current_Spending { get; set; }
public string ProjectOwner { get; set; }
public Nullable<bool> Active { get; set; }
public Nullable<System.DateTime> Actual_End_date { get; set; }
public Nullable<System.DateTime> CreationDate { get; set; }
public string CreatedByEmpNo { get; set; }
public Nullable<System.DateTime> UpdateDate { get; set; }
public string UpdatedByEmpNo { get; set; }
public Nullable<int> SubCategoryID { get; set; }
Simply exclude the ID field from the Razor view. (Self explanatory unless you're using EditorForModel, in which case you'd need to modify the Display annotation for the ID field, so that the Razor engine knows to exclude it from the view.)
Once excluded, its value will not get posted back to the server. That way it will get the default value when created in the MVC controller; and the database will auto-generate the new ID.
I have a legacy SQL database that has a linked table to store specifications for order lines. The application designers use an image field in SQL to store the text as binary data. I mocked up an example below:
public class OrderLine
{
public int ID { get; set; }
public int OrderID { get; set; }
public int LineNo { get; set; }
public string Product { get; set; }
public decimal Quantity { get; set; }
public decimal UnitPrice { get; set; }
public string Status { get; set; }
}
public class OrderLineSpecifications
{
public int ID { get; set; }
public int OrderID { get; set; }
public int OrderLineNo { get; set; }
public Image Bits { get; set; } // <--- This Line
public int Bit_Length { get; set; }
}
SQL Table Definition
[ID] [int] IDENTITY(1,1) NOT NULL,
[OrderID] [varchar](15) NOT NULL,
[OrderLineNo] [smallint] NOT NULL,
[Bits] [image] NULL,
[Bit_Length] [int] NOT NULL
Currently I have to use SQL with
cast(cast(Bits as varbinary(max)) as varchar(max))
to extract the text and then perform the reverse to return it to the database. Is it possible to have the conversion done in EF? Perhaps at the property level { get; set;} ?
The solution was easier than I thought. I changed what was identified as an Image in SQL to a byte array (byte[]) and created a property (Specs) that processed the BITS values. Entity Framework was happy and it works in both directions. Surprisingly easy.
public virtual byte[] BITS { get; set; }
public virtual int BITS_LENGTH { get; set; }
[NotMapped]
public virtual string Specs
{
get
{
UTF8Encoding enc = new UTF8Encoding();
string str = enc.GetString(BITS);
return str;
}
set
{
UTF8Encoding encoding = new UTF8Encoding();
BITS = encoding.GetBytes(value);
}
}
What is wrong with my class mappings below?
public class Foo
{
public Foo()
{
ActualCurve = new List<Point>();
TargetCurve = new List<Point>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Point> ActualCurve { get; set; }
public virtual IList<Point> TargetCurve { get; set; }
}
public class Point
{
public virtual int Id { get; set; }
public virtual double X { get; set; }
public virtual double Y { get; set; }
}
public class FooMapping() : ClassMap<Foo>
{
Table("Foos");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name).Not.Nullable();
HasMany(x => x.ActualCurve ).Cascade.All();
HasMany(x => x.TargetCurve ).Cascade.All();
}
public class PointMapping() : ClassMap<Point>
{
Table("Points");
Not.LazyLoad();
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.X).Not.Nullable();
Map(x => x.Y).Not.Nullable();
}
These mappings produce
CREATE TABLE [dbo].[Foos](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar] NOT NULL,
[RecipeRevisionId] [int] NOT NULL
CREATE TABLE [dbo].[Points](
[Id] [int] IDENTITY(1,1) NOT NULL,
[X] [float] NOT NULL,
[Y] [float] NOT NULL,
[FooId] [int] NOT NULL
Essentially, the problem is that when I am pulling a persisted Foo object back out of the database, the Foo.ActualCurve and Foo.TargetCurve lists both get populated with the combined contents of both lists. There clearly isn't a column that keys which set of points belong to the correct curve within Foo, but I am not sure how to change the mapping to maintain the two distinct sets of Points.
I think you will need to specify a separate column for the references:
HasMany(x => x.ActualCurve ).Cascade.All().KeyColumn("ActualCurveId");
HasMany(x => x.TargetCurve ).Cascade.All().KeyColumn("TargetCurveId");
EDIT: Column->KeyColumn
How do I map these existing tables to the classes below?
I have the following tables:
CREATE TABLE dbo.UserContact (
UserContactId int NOT NULL IDENTITY (1, 1),
UserId int NOT NULL,
ContactId int NOT NULL,
UserContactTypeId int NOT NULL,
FromDt datetime NULL,
ThruDt datetime NULL,
CreateDt datetime NOT NULL,
UpdateDt datetime NULL,
IsDeleted bit NULL,
CanSolicit bit NOT NULL
)
CREATE TABLE dbo.Contact (
ContactId int NOT NULL IDENTITY (1, 1),
CreateDt datetime NOT NULL,
UpdateDt datetime NULL
)
CREATE TABLE dbo.Electronic (
ContactId int NOT NULL,
URL nvarchar(512) NOT NULL,
ElectronicType smallint NULL
)
CREATE TABLE dbo.Phone (
ContactId int NOT NULL,
AreaCode nchar(3) NOT NULL,
PhoneNb nchar(7) NOT NULL,
Extension nchar(6) NULL,
PhoneType smallint NULL
)
CREATE TABLE dbo.Postal
(
ContactId int NOT NULL,
Street nvarchar(256) NOT NULL,
Specifier nvarchar(256) NULL,
GeocodeId int NULL
)
The tables Electronic, Phone and Postal are in a one-to-one relationship with Contact. The table UserContact is in a many-to-one with Contact; UserContact is an association table between User and Contact.
I also have the following classes:
public class Electronic : IntegerKeyEntity
{
public virtual ContactId { get; set; }
public virtual DateTime CreateDt { get; set; }
public virtual DateTime? UpdateDt { get; set; }
public string Url { get; set; }
public ElectronicType Type { get; set; }
}
public class Postal : IntegerKeyEntity
{
public virtual ContactId { get; set; }
public virtual DateTime CreateDt { get; set; }
public virtual DateTime? UpdateDt { get; set; }
public string Street { get; set; }
public string Specifier { get; set; }
public Geocode Geocode { get; set; }
}
public class Phone : IntegerKeyEntity
{
public virtual ContactId { get; set; }
public virtual DateTime CreateDt { get; set; }
public virtual DateTime? UpdateDt { get; set; }
public string AreaCode { get; set; }
public string PhoneNb { get; set; }
public string Extension { get; set; }
public PhoneType Type { get; set; }
}
public class UserContact : IntegerKeyEntity
{
private ICollection<Electronic> electronics = new HashSet<Electronic>();
private ICollection<Phone> phones = new HashSet<Phone>();
private ICollection<Postal> postals = new HashSet<Postal>();
// props
public virtual IEnumerable<Electronic> Electronics { get { return electronics; } }
public virtual IEnumerable<Phone> Phones { get { return phones; } }
public virtual IEnumerable<Postal> Postals { get { return postals; } }
}
So, I do I get from the four Contact tables (parent and child) down to the three classes? And, how do I map those three classes to the UserContact table. I'm assuming I can have three ILists, one for each class.
I think you're modeling this incorrectly. It appears to me that Electronic, Phone, and Postal extend (inherit from) Contact and this should be expressed in your domain model. Those classes are not related to Contact by one-to-one, they are concrete types that extend the abstract Contact type. If you model it this way you can map Contact using table-per-subclass inheritance mapping.
User would then have a many-to-many-relationship with Contact, and the user's Contacts collection would contain Contacts of any type.
Personally I would put all the Contact types into one table and use the simpler table-per-class mapping.