I am working on a Entity Framework Code First project where I have many-to-many relationships: ServiceProvider and ServiceType
I am getting the following error when adding a new ServiceProvider into the repository: Cannot insert the value NULL into column 'ID', table 'ABCDE.dbo.ServiceTypes'; column does not allow nulls. INSERT fails.
The entity and controller classes are as follows:
ServiceProvider.cs:
public partial class ServiceProvider
{
public ServiceProvider()
{
ServiceTypes = new HashSet<ServiceType>();
}
[Key]
public int ServiceProviderID { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
public virtual ICollection<ServiceType> ServiceTypes { get; set; }
public virtual IEnumerable<Service> Services { get; set; }
}
ServiceType.cs:
public partial class ServiceType
{
public ServiceType(ServiceTypeEnum #enum)
{
ID = (int) #enum;
Name = #enum.ToString();
}
protected ServiceType() { } // For EF
//[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
public virtual ICollection<ServiceProvider> ServiceProviders { get; set; }
public static implicit operator ServiceType(ServiceTypeEnum #enum) => new ServiceType(#enum);
public static implicit operator ServiceTypeEnum(ServiceType serviceType) => (ServiceTypeEnum) serviceType.ID;
}
public enum ServiceTypeEnum
{
Ambulance = 1,
[Display(Name = "Cash Advance")]
CashAdvance = 2,
Hospitalization = 3,
Hotel = 4,
[Display(Name = "House Call")]
HouseCall = 5,
[Display(Name = "Medical Escort")]
MedicalEscort = 6,
Transfer = 7,
Repatriation = 8
}
and the ServiceProviderController.cs:
public ActionResult Create()
{
var _allServiceTypes = Enum.GetValues(typeof(ServiceTypeEnum))
.Cast<ServiceTypeEnum>()
.Select(t => new KeyValuePair<string, int>(t.ToString(), (int) t));
var viewModel = new ServiceProviderViewModel.CreateModel()
{
AllServiceTypes = _allServiceTypes
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(ServiceProviderViewModel.CreateModel viewModel)
{
if (ModelState.IsValid)
{
var serviceProvider = new ServiceProvider();
serviceProvider.Title = viewModel.Title;
repository.InsertServiceProvider(serviceProvider);
for (int i = 0; i < viewModel.SelectedServiceTypes.Length; i++)
{
var _serviceType = new ServiceType((ServiceTypeEnum)Enum.Parse(typeof(ServiceTypeEnum), viewModel.SelectedServiceTypes[i].ToString()));
serviceProvider.ServiceTypes.Add(_serviceType);
}
repository.Save();
return RedirectToAction("Index");
}
return View(viewModel);
}
I am not sure why the database table ServiceTypeServiceProviders (automatically generated by EF) is not populated with the correct data. Nothing gets stored into it. By looking at the serviceProvider object while debugging, I can see that everything seems to be fine (see the image below)
I filled the ServiceType table manually with the following data (matching the enum)
I'm assuming the "new" ServiceType object you are creating and adding to the associations for the ServiceProvider is a reference to the data you manually inserted into the ServiceType table and not a new object... If so you'll either want to retrieve the object from the DbContext rather than creating a new object or you'll have to attach the "new" ServiceType object to the current DbContext using either context.Set<ServiceType>.Attach(serviceType); or context.Entry(serviceType).State = EntityState.Attached;. These may require the assignment of the ID property for the ServiceType object to ensure it maps to the existing entities correctly. This will ensure you are not getting a null ID or duplicate entries in the ServiceType table on insert.
If this does not fix the issue, we would have to see the code for InsertServiceProvider to get a better idea of how you are adding the ServiceProvider entity to the DbContext
Related
I am new to C# and .NET, I am trying to implement CRUD functionality with local database. So I created a Product class which contains attributes and and foreign keys. But I get an error when I launch the application. The methods Update() and Delete() do not work, but Add() method works fine.
Here are my generic methods -
public class GenericRepository<T> where T : class
{
GPContext context;
IDbSet<T> dbSet;
public GenericRepository()
{
context = new GPContext();
dbSet = context.Set<T>();
}
public IEnumerable<T> getAll()
{
return dbSet.AsEnumerable();
}
public T GetById(int id)
{
return dbSet.Find(id);
}
public void Add(T t)
{
dbSet.Add(t);
context.SaveChanges();
}
public void Update(T t)
{
dbSet.Attach(t);
//on modifie la instance avant la inserer dans la bd
context.Entry(t).State = EntityState.Modified;
context.SaveChanges();
}
public void Delete(T t)
{
dbSet.Remove(t);
context.SaveChanges();
}
}
and here is how I call them in Program.cs -
GenericRepository<Product> productRepository = new GenericRepository<Product>();
Address adresse = new Address() { City = "Paris", StreetAdress = "Escalier 9" };
Category cat1 = new Category() { Name="cat1" };
Chemical p1 = new Chemical() { ProductId=26, DateProd = DateTime.Now , myAdress = adresse, Description = "CHemichal Product",
Price = 55,Name="PHARMA CHEMICH 1", Quantity = 100 , MyCategory = cat1};
Chemical p2 = new Chemical() {ProductId = 12 , DateProd = DateTime.Now , myAdress = adresse, Description = "CHemichal Update Product",
Price = 55,Name="PHARMA CHEMICH 1 Update", Quantity = 100 , MyCategory = cat1};
Product p3 = new Product() { ProductId = 11 };
productRepository.Add(p1);
productRepository.Update(p2);
productRepository.Delete(p3);
Following error occurs when I launch the application -
System.InvalidOperationException : 'A referential integrity constraint
violation occurred: The property value(s) of 'Category.CategoryId' on
one end of a relationship do not match the property value(s) of
'Product.categoryId' on the other end.'
Also I have this error in Delete method -
System.InvalidOperationException : 'The object cannot be deleted
because it was not found in the ObjectStateManager.'
The entity classes are below -
Product:
public class Product : Concept
{
[Display(Name ="Date de prod")]
[DataType(DataType.Date)]
public DateTime DateProd { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required(ErrorMessage ="Champ obligatoire")]
[StringLength(25)]
[MaxLength(50 , ErrorMessage ="taille max dans la base 50")]
public string Name { get; set; }
[DataType(DataType.Currency)]
public double Price { get; set; }
public int ProductId { get; set; }
[Range(0,int.MaxValue)]
public int Quantity { get; set; }
[DataType(DataType.ImageUrl) ,Display(Name ="Image")]
public string image { get; set; }
[ForeignKey("categoryId")]
public Category MyCategory { get; set; }
public int? categoryId { get; set; }
public List<Provider> Providers { get; set; }
public override void GetDetails()
{
Console.WriteLine("Product Name:, Price "+ Name + " " + Price);
}
public virtual void GetMyType()
{
Console.WriteLine("Uknown");
}
}
Chemical:
public class Chemical : Product
{
// public string City { get; set; }
public string LastName { get; set; }
// public string StreetAddress { get; set; }
public Address myAdress { get; set; }
public override void GetMyType()
{
System.Console.WriteLine("CHEMICAL");
}
}
Biological:
public class Biological : Product
{
public string Herbs { get; set; }
public override void GetMyType()
{
Console.WriteLine("Biological");
}
}
I am really a beginner and I have no idea about what I should do or change. I have read other answers on the same topic and I did not understand what to do.
First, you are creating 3 new entities and trying to Add, Update and Delete them. A new entity can be Added, but cannot be Updated or Deleted. Think about it, it is an object that you have just created, and it is not in the database yet. How do you expect to Update/Delete it? To Update or Delete an entity it must be an existing one.
Second, you are using newly created (without any Id) Address and Category objects to update myAdress and MyCategory properties. Those two are not primitive properties representing column values like the rest of them, they are related entities with foreign key constraints. If you really want to change/update them, you must set one that is already existing in database with Id. Otherwise, it will violate the foreign key constraint (or, referential integrity constraint), that's exactly is what the first error message is saying.
Try the following, for Update -
// retrieve an existing address from database
Address newAddress = new GenericRepository<Address>().GetById(3);
// retrieve an existing category from database
Category newCategory = new GenericRepository<Category>().GetById(5);
// retrieve an existing product from database
Chemical p2 = new GenericRepository<Product>().GetById(12);
// update/change properties, but don't change the ProductId
p2.DateProd = DateTime.Now;
p2.myAdress = newAddress;
p2.Description = "CHemichal Update Product";
p2.Price = 55;
p2.Name="PHARMA CHEMICH 1 Update";
p2.Quantity = 100;
p2.MyCategory = newCategory;
// now call for update
productRepository.Update(p2);
and for Delete -
// retrieve an existing product from database
Product p3 = productRepository.GetById(11);
// now call for delete
productRepository.Delete(p3);
I keep getting this error when I try to submit to the database:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'NPPRProvId'
Here's where it breaks:
public static bool Save(NPPR_Provider provider, State state, string filename, bool validateBeforeSave = true )
{
using (var db = new NPPRContext(state))
{
var prov = new NPPR_Provider()
{
First = provider.First,
Middle = provider.Middle,
Last = provider.Last,
DateOfBirth = provider.DateOfBirth,
DateOfDeath = provider.DateOfDeath,
Gender = provider.Gender,
SSN = provider.SSN,
DegreeCode = provider.DegreeCode,
BusinessName = provider.BusinessName,
DbaName = provider.DbaName,
Action = "A",
EffectiveDate = "20121212",
EndDate = "99991231",
NPPR_ServLocation = new NPPR_ServLocation()
{
EnrollmentType = provider.NPPR_ServLocation.EnrollmentType,
OrganizationType = provider.NPPR_ServLocation.OrganizationType,
ProviderTypeCode = provider.NPPR_ServLocation.ProviderTypeCode,
IRSTaxAssociations = provider.NPPR_ServLocation.IRSTaxAssociations,
NPIAssociations = provider.NPPR_ServLocation.NPIAssociations,
Address = provider.NPPR_ServLocation.Address
},
NPPR_Header = new NPPR_Header()
{
FileName = filename,
TransactionDate = Utilities.DateTimeToPRNDate(DateTime.Now),
FileLoadDate = DateTime.Now,
SubmitterId = "M00000503",
Purpose = "A",
Action = "A"
}
};
foreach(var npi in prov.NPPR_ServLocation.NPIAssociations)
{
npi.NPIType = prov.NPPR_ServLocation.OrganizationType == "I" ? "1" : "2";
}
prov.NPPR_ServLocation.Licenses = SegmentOrNull<NPPR_Licenses>(provider.NPPR_ServLocation, "Licenses", "LicenseNumber");
prov.NPPR_ServLocation.Certifications = SegmentOrNull<NPPR_Certifications>(provider.NPPR_ServLocation, "Certifications", "CertificationNumber");
prov.NPPR_ServLocation.Specialties = SegmentOrNull<NPPR_Specialties>(provider.NPPR_ServLocation, "Specialties", "SpecialtyCode");
prov.NPPR_ServLocation.Taxonomies = SegmentOrNull<NPPR_Taxonomies>(provider.NPPR_ServLocation, "Taxonomies", "TaxonomyCode");
prov.NPPR_ServLocation.OtherIds = SegmentOrNull<NPPR_OtherIds>(provider.NPPR_ServLocation, "OtherIds", "IdentifierTypeId");
prov.NPPR_ServLocation.GroupAssociations = SegmentOrNull<NPPR_GroupAssociations>(provider.NPPR_ServLocation, "GroupAssociations", "ProviderLocationId");
db.NPPR_Provider.Add(prov);
if (validateBeforeSave)
db.SaveChangesWithValidation();
else
db.SaveChanges();
return true;
}
}
db.SaveChanges() is where, according to the stacktrace, the exception is thrown. This function was originally written for EF Core, but due to issues with the server, I was forced to turn everything to EF6.4. Under Core, this method worked fine, but under EF6, it throws an exception. I've tried a few things I've read on other SO questions but so far no luck.
Specifically the exception throws on the NPPR_Provider primary key, NPPRProvId, which at no point in my code is ever read or written, except to be defined in the model.
In SQL, the NPPRProvId is PK, int, not null
The model involved:
public class NPPR_Provider : Provider
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int NPPRProvId { get; set; }
//[ForeignKey("NPPR_Header")]
public int? NPPRFileId { get; set; }
[ForeignKey("NPPRFileId")]
public NPPR_Header NPPR_Header { get; set; }
[ForeignKey("NPPRProvId")]
public NPPR_ServLocation NPPR_ServLocation { get; set; }
}
What am I doing wrong here and how might I fix it? I'm still fairly new to EF and this is my first major project in EF and MVC, and in the .NET framework in general.
In EF Core the NPPR_ServLocation could be an owned object but to my knowledge in EF 6 there are not owned objects; you need to define them explicitly in a separate table and give them keys.
public class NPPR_Provider : Provider
{
public int NPPR_ProviderId { get; set; }
public NPPR_Header NPPR_Header { get; set; }
public NPPR_ServLocation NPPR_ServLocation { get; set; }
… // data properties here
}
public class NPPR_Header {
public int NPPR_HeaderId {get;set;}
public int NPPR_ProviderId {get;set;}
public NPPR_Provider {get;set;}
… // data properties here
}
public class NPPR_ServLocation {
public int NPPR_ServLocationId {get;set;}
public int NPPR_ProviderId {get;set;}
public NPPR_Provider {get;set;}
… // data properties here.
}
I'm building Backend for Mobile Application with ASP.NET MVC Framework.
I have two Objects:
public class CarLogItem : EntityData
{
public CarLogItem(): base()
{
Time = DateTime.Now;
}
public DateTime Time { get; set; }
public int RPM { get; set; }
public int Speed { get; set; }
public int RunTime { get; set; }
public int Distance { get; set; }
public int Throttle { get; set; }
[ForeignKey("Trip")]
public String Trip_id { get; set; }
// Navigation property
public TripItem Trip { get; set; }
}
and
public class TripItem : EntityData
{
public TripItem() : base()
{
UserId = User.GetUserSid();
StartTime = DateTime.Now;
logItems = new List<CarLogItem>();
}
public string UserId { get; set; }
public List<CarLogItem> logItems {get;set;}
public DateTime StartTime { get; set; }
}
and I have controller, which add new CarLogItem to database.
public class CarLogItemController : TableController<CarLogItem>
{
// POST tables/CarLogItem
public async Task<IHttpActionResult> PostCarLogItem(CarLogItem item)
{
var lastItem = db.CarLogItems.OrderByDescending(x => x.Time).FirstOrDefault();
//lastItem = (Query().Where(logitem => true).OrderBy(logitem => logitem.Time)).Last();
//checking if lastItem.Trip isn't null because
// I have entities with Trip field is null, but all of them should have it.
if (lastItem != null && lastItem.Trip != null && item.RunTime > lastItem.RunTime)
{
item.Trip = lastItem.Trip;
}
//In order to test adding of new TripItem entity to database
// I compare item.RunTime with 120, so it always true
else if (lastItem == null || item.RunTime < 120) // < lastItem.RunTime)
{
var newTrip = new TripItem();
item.Trip = newTrip;
}
else
{
throw new ArgumentException();
}
CarLogItem current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
}
When I'm trying to add new CarLogItem with Trip = null it's ok, but when Trip is particular object it fails with following Exception:
The entity submitted was invalid: Validation error on property 'Id': The Id field is required
How properly to add new CarLogItem with nested TripItem?
I think that you need to populate the Id property on your TripItem, e.g.
var newTrip = new TripItem(){ Id = Guid.NewGuid() }
You need a primary key field in every entity class, like Id or CarLogItemId (ClassName + "Id"). Or just have a property with [Key] attribute:
[Key]
public string/int/Guid/any-db-supported-type MyProp { get; set; }
Entity Framework relies on every entity having a key value that it
uses for tracking entities. One of the conventions that code first
depends on is how it implies which property is the key in each of the
code first classes. That convention is to look for a property named
“Id” or one that combines the class name and “Id”, such as “BlogId”.
The property will map to a primary key column in the database.
Please see this for more details.
I also suspect this to be a problem:
public Lazy<CarLogItem> logItems { get; set; }
You don't have to mark navigation property as Lazy<>. It is already lazy (unless you have configuration that disables lazy loading). Please try to remove Lazy<> and see if it works this way.
I try to add view from this controller . I only need this view to show data not for insert or update or delete
public ActionResult Index()
{
var CartObj = ShoppingCart.GetCart(this.HttpContext);
var classshop = new New
{
CartItems = CartObj.GetCartItems(),
CartTotal = CartObj.GetSum()
};
return View(classshop);
}
namespace MusicStore.Models
{
public class ShoppingCart
{
MusicStoreEntities dbo = new MusicStoreEntities();
string ShoppingCartID { get; set; }
public const string CartSessionKey = "CartId";
public static ShoppingCart GetCart(HttpContextBase Context)
{
var cart = new ShoppingCart();
cart.ShoppingCartID = cart.GetCardId(Context);
return cart;
}
public static ShoppingCart GetCart(Controller controller)
{
return GetCart(controller.HttpContext);
}
public List<Cart> GetCartItems()
{
return dbo.Carts.Where(a => a.CartId == ShoppingCartID).ToList();
}
public decimal? GetSum()
{
decimal? Sum = (from items in dbo.Carts
where items.CartId == ShoppingCartID
select (int)items.Count * items.album.Price).Sum();
return Sum ?? decimal.Zero;
}
}
}
and then I got this error:
there was an error running the selected code generator:
'unable to retrieve metadata for 'Musicstore.Model.new'
one or more validation error were detected during model generation
musicstore,models.New :entity type'New' has no key defined .
define the key of entityType
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MusicStore.Models
{
public class New
{
public List<Cart> CartItems { get; set; }
public decimal? CartTotal { get; set; }
}
}
There are two options here. First, if this class is mapped to a table in your database, every model in entity framework requires a primary key. Add this into your model:
[Key]
public int Id { get; set; }
This creates a new property called Id and the [Key] attribute makes it a primary key. Technically you don't need the attribute as EF will pick up Id property and use it as a key, but I prefer to be explicit.
Alternatively, if you don't want this class to be a table in your database, add the NotMapped attribute to the class like this:
[NotMapped]
public class New
{
public List<Cart> CartItems { get; set; }
public decimal? CartTotal { get; set; }
}
I know this is old, but I just ran across this issue.
What happen is when I created a class, CreateEmployeeViewModel, inside the Models folder Visual Studio "smartly" put a line in my DB Context class
public System.Data.Entity.DbSet<eManager.Web.Models.CreateEmployeeViewModel>
CreateEmployeeViewModels { get; set; }
So a table was created on the next update-migration. Removing this line removed the requirement for a key field.
Note: You may also have to add the line AutomaticMigrationDataLossAllowed = true; to your DBMigrationConfiguration Class if the table was created.
I've got an entity that has a property that's an abstract type. This creates a one-to-one relationship that uses table-per-hierarchy inheritance. Everything seems like it's working correctly.
I can create an Item and set the Base property to ConcreteOne; everything saves correctly. However, when I try to update Base to ConcreteTwo, EF updates the Base record in the database with the new user value, it doesn't update the discriminator for the type. So the extra data for ConcreteTwo gets persisted, but the discriminator still says ConcreteOne.
The following is a simple example that exposes the problem
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
App_Start.EntityFrameworkProfilerBootstrapper.PreStart();
Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
// Create our item with ConcreteOne for Base
using (var context = new DataContext())
{
var item = new Item
{
Base = new ConcreteOne { Name = "Item", Data = 3 }
};
context.Items.Add(item);
context.SaveChanges();
}
// Update Base with a new ConcreteTwo
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Item = item,
Name = "Item 3",
User = new User { Name = "Foo" }
};
// If I don't set this to null, EF tries to create a new record in the DB which causes a PK exception
item.Base.Item = null;
item.Base = newBase;
// EF doesn't save the discriminator, but DOES save the User reference
context.SaveChanges();
}
// Retrieve the item -- EF thinks Base is still ConcreteOne
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
Console.WriteLine("{0}: {1}", item.Name, item.Base.Name);
}
Console.WriteLine("Done.");
Console.ReadLine();
}
}
public class DataContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Base Base { get; set; }
}
public abstract class Base
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Item Item { get; set; }
}
public class ConcreteOne : Base
{
public int Data { get; set; }
}
public class ConcreteTwo : Base
{
public virtual User User { get; set; }
}
}
When the changes are saved, EF generates the following SQL:
update [dbo].[Bases]
set [Name] = 'Item 3' /* #0 */,
[User_Id] = 1 /* #1 */
where (([Id] = 1 /* #2 */)
and [User_Id] is null)
So it's almost correct, but I'd expect to see [Discriminator] = 'ConcreteTwo' in the update statement. Are my expectations unfounded or am I doing something wrong?
As a test, I tried using table-per-type and the the entry was removed from the ConcreteOne table and added to the ConcreteTwo table as I would expect. So it works, but my real application has at least seven sub-types and the SQL statement to retrieve the Base property got really nasty. So I'd certainly like to accomplish this using TPH, if possible.
Update:
I've verified that the problem exists in EF5 as well as EF6.
This question is based on the expectation of an update taking place, which is seemingly a debatable expectation. Currently your best bet if the TPH hierarchy is not functioning as expected, and considering EF6 is currently in beta, is to start a discussion on the Codeplex forums.
Add this to your model:
public enum BaseType
{
ConcreteOne = 1,
ConcreteTwo = 2
}
public abstract class Base
{
...
public BaseType BaseType { get; set; }
...
}
And in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Base>()
.ToTable("Base");
modelBuilder.Entity<ConcreteOne>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteOne))
.ToTable("ConcreteOne");
modelBuilder.Entity<ConcreteTwo>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteTwo))
.ToTable("ConcreteTwo");
}
I would expect this to create a new instance (record) with a ConcreteTwo discriminator.
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Name = "Item 3",
User = new User { Name = "Foo" }
};
item.Base = newBase;
context.SaveChanges();
}