I cannot get the foreign key in my Entity Framework 4.3 code first database to be updated to null.
My view model:
public class AccountViewModel
{
public int Id { get; set; }
public int? CorporationId { get; set; }
public CorporationModel Corporation { get; set; }
}
var corporation = db.Corporation.Where(x => x.Id == model.CorporationId).FirstOrDefault(); // shows as null
account.Corporation = corporation; // sets the value to null
db.Entry(account).State = EntityState.Modified;
db.SaveChanges(); // does not save the null value in the FK field!!!
Any assistance would be GREATLY appreciated.
You must set the foreign key property to null. Setting the state to Modified only affects scalar properties (and the foreign key property is one of them but not the navigation property):
account.CorporationId = null;
db.Entry(account).State = EntityState.Modified;
db.SaveChanges();
If you don't have a foreign key property on Account you must load the account including the corporation:
var account = db.Account.Include(a => a.Corporation)
.Where(a => a.Id == accountId)
.SingleOrDefault();
if (account != null)
{
account.Corporation = null;
db.SaveChanges();
}
Related
When I query the database to retrieve a record, and the model includes a property which is a foreign key to the same type, that property returns null. What did I do wrong?
I have this model class:
public class Customer
{
public int ID { get; set; }
[ForeignKey("ParentID")]
public virtual Customer Parent { get; set; }
public List<Contact> Contacts { get; set; }
[Required]
public string Name { get; set; }
}
with the underlying table:
CREATE TABLE [dbo].[Customer]
(
[ID] INT IDENTITY (1, 1) NOT NULL,
[ParentID] INT NULL,
[Name] NVARCHAR(MAX) NOT NULL,
CONSTRAINT [PK_Customer]
PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Customer_Customer_ParentID]
FOREIGN KEY ([ParentID]) REFERENCES [dbo].[Customer] ([ID])
)
which was generated using EF Core code first migrations.
When the CustomersController attempts to:
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
// returns the correct customer with null Parent property which causes...
var customer = await _context.Customer
.FirstOrDefaultAsync(m => m.ID == id);
if (customer == null)
{
return NotFound();
}
var vm = new CustomerViewModel(customer); // ... NRE to be thrown here
return View(vm);
}
Even though this data exists in the table:
ID ParentID Name
--------------------------
5 1 ParentCo
7 5 ChildCo
try this
var result = await _context.Customer.Include(x => x.Parent).FirstOrDefaultAsync(x => x.Id == id);
but if that does not work
var customer = await _context.Customer.FirstOrDefaultAsync(
.FirstOrDefaultAsync(m => m.ID == id); );
if (customer != null)
{
var parent = await _context.Customer.FirstOrDefaultAsync(x => x.ParentId == customer.Id);
if (parent != null)
{
customer.Parent = parent;
}
}
else
{
return NotFound();
}
I am trying to insert and update table using Entity Framework 6. I am not able to do it.
When i insert, I get an error
Unable to update the EntitySet 'SampleV2' because it has a DefiningQuery and no element exists in the element to support the current operation
When I update, the error thrown is:
The property 'Name' is part of the object's key information and cannot be modified.
My code:
//Table script
CREATE TABLE [dbo].[SampleV2](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](150) NOT NULL,
[DateOfBirth] [datetime] NOT NULL,
[Status] [bit] NOT NULL
) ON [PRIMARY]
// SampletV2.cs
public partial class SampleV2
{
public int Id { get; set; }
public string Name { get; set; }
public System.DateTime DateOfBirth { get; set; }
public bool Status { get; set; }
}
// Utilities.cs
public static Dictionary<bool,string> SavePlayerDetails(SampleV2 model)
{
Dictionary<bool, string> dicInfo = new Dictionary<bool, string>();
if (model.Id == 0)
{
try
{
SampleV2 sam = new SampleV2
{
Name = model.Name,
DateOfBirth = model.DateOfBirth,
Status = model.Status
};
using (var _context = new ExamEntities1())
{
_context.SampleV2.Add(sam);
_context.SaveChanges(); // getting error here
}
}
catch (Exception e)
{
throw;
}
}
else
{
try
{
using (var _context = new ExamEntities1())
{
SampleV2 data = _context.SampleV2.SingleOrDefault(a => a.Id == model.Id);
data.DateOfBirth = model.DateOfBirth;
data.Name = model.Name;
data.Status = model.Status;
_context.Entry(data).State = System.Data.Entity.EntityState.Modified; // getting error here
_context.SaveChanges();
}
}
catch (Exception e)
{
throw;
}
}
return dicInfo;
}
If you using Database First, the error indicates that the SampleV2 entity or table missing a primary key field or it is not being set in model class.
Try adding KeyAttribute on id entity to mark it as primary key field:
public partial class SampleV2
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public System.DateTime DateOfBirth { get; set; }
public bool Status { get; set; }
}
Additionally, you need to add primary key in DB using table designer with Set Primary Key and setting StoreGeneratedPattern as Identity in EDMX file, then update generated model from database to ensure mapped entity class has identity primary key field.
NB: The KeyAttribute can be generated automatically by editing T4 template (.tt) file like this:
<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
using System.ComponentModel.DataAnnotations;
// -- other stuff
var simpleProperties = typeMapper.GetSimpleProperties(entity);
if (simpleProperties.Any())
{
foreach (var edmProperty in simpleProperties)
{
if (ef.IsKey(edmProperty)) {
#> [Key]
<# }
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
}
Similar issues:
Unable to update the EntitySet - because it has a DefiningQuery and no <UpdateFunction> element exist
Entity Framework 4 Error: Unable to update the EntitySet because it has a DefiningQuery
Why am I getting a "Unable to update the EntitySet because it has a DefiningQuery..." exception when trying to update a model in Entity Framework?
I have looked for alot of different approaches to achive this. But I have yet to find a good and simple exmaple on how to do this without alot of 3 party instalations wich focuses on performance logging and debugging.
I am looking for a way to easily log ALL changes to my database and also when new rows are added. I want to have a own table that stores which action in which controller was called / or simply the database table would be enough to track it. And the which fields where updated or added.
I am picturing a table something like this:
ID - ACTION/TABLE/METHOD - ID - TYPE - DETAILS - CREATED BY - TIMESTAMP
x - TableName/ActionResult/JsonResult/ - ID of the new or updated item - updated or new - details on what have changed or created - user.identity - timestamp
So i can view the log table in each spesific view and i can see the history for that item and which fields where changed etc.
I looked at the bottom suggestion here: How to implement a MVC 4 change log? since my SQL database does not support the SQL Service Broker and I dont really want to start with adding Triggers in SQL.
I am using MVC 5.2 and EF 6.0 so I have looked at the Database.Log property but I really need some guidance on how to set up a good method to achive what I want.
I found a solution i am currently modifying to my needs.
Here is the code:
Overriding SaveChanges Class in
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
and adding theese methods:
public async Task SaveChangesAsync(string userId)
{
// Get all Added/Deleted/Modified entities (not Unmodified or Detached)
foreach (var ent in this.ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified))
{
// For each changed record, get the audit record entries and add them
foreach (Log x in GetAuditRecordsForChange(ent, userId))
{
this.Log.Add(x);
}
}
// Call the original SaveChanges(), which will save both the changes made and the audit records
await base.SaveChangesAsync();
}
private List<Log> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userId)
{
List<Log> result = new List<Log>();
DateTime changeTime = DateTime.Now;
// Get the Table() attribute, if one exists
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
// Get primary key value (If you have more than one key column, this will need to be adjusted)
string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
if (dbEntry.State == EntityState.Added)
{
// For Inserts, just add the whole record
// If the entity implements IDescribableEntity, use the description from Describe(), otherwise use ToString()
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "A", // Added
TableName = tableName,
RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(), // Again, adjust this if you have a multi-column key
ColumnName = "*ALL", // Or make it nullable, whatever you want
NewValue = (dbEntry.CurrentValues.ToObject() is IDescribableEntity) ? (dbEntry.CurrentValues.ToObject() as IDescribableEntity).Describe() : dbEntry.CurrentValues.ToObject().ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
else if (dbEntry.State == EntityState.Deleted)
{
// Same with deletes, do the whole record, and use either the description from Describe() or ToString()
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "D", // Deleted
TableName = tableName,
RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
ColumnName = "*ALL",
NewValue = (dbEntry.OriginalValues.ToObject() is IDescribableEntity) ? (dbEntry.OriginalValues.ToObject() as IDescribableEntity).Describe() : dbEntry.OriginalValues.ToObject().ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
else if (dbEntry.State == EntityState.Modified)
{
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
// For updates, we only want to capture the columns that actually changed
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName)))
{
result.Add(new Log()
{
LogID = Guid.NewGuid(),
EventType = "M", // Modified
TableName = tableName,
RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
ColumnName = propertyName,
OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? null : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(),
NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString(),
Created_by = userId,
Created_date = changeTime
}
);
}
}
}
// Otherwise, don't do anything, we don't care about Unchanged or Detached entities
return result;
}
public DbSet<Log> Log { get; set; }
And here is the Log class
[Table("N_Log")]
public class Log
{
[Key]
public Guid LogID { get; set; }
[Required]
public string EventType { get; set; }
[Required]
public string TableName { get; set; }
public string ActionID { get; set; }
[Required]
public string RecordID { get; set; }
[Required]
public string ColumnName { get; set; }
public string OriginalValue { get; set; }
public string NewValue { get; set; }
[Required]
public string Created_by { get; set; }
[Required]
public DateTime Created_date { get; set; }
}
From my controller I have passed value module to view
public ActionResult Details(long id, string owner)
{
var module = _ownedModuleRepository.GetModuleDetails(id, owner);
return View(module);
}
I've shown the value it contains in view as follows
<dt>ID</dt>
<dd>#Model.Id</dd>
<dt>Module ID</dt>
<dd>#Model.ModuleId</dd>
<dt>Owner</dt>
<dd>#Model.Owner</dd>
<dt>Module Type</dt>
<dd>#Model.TypeName</dd>
<dt>Module Kind</dt>
<dd>#Model.KindName</dd>
<dt>Ownership Start Date</dt>
<dd>#Model.Start</dd>
<dt>Ownership End Date</dt>
<dd>#Model.End</dd>
#foreach (var properties in Model.Properties)
{
<dt>Property Name</dt>
<dd>#properties.Name</dd>
<dt>Property Value</dt>
<dd>#properties.Value</dd>
}
Currently #Model.End is null, it is of DateTime type and I had set it to be nullable in viewmodel.
Since it is null, this is what i'm getting in view
As you can see, the value of Ownership End Date is taking the value of Property Name from below. How can I set it to empty if the #Model.End is null?
Edit 1:
My model
public class OwnedModuleDetails
{
public long Id { get; set; }
public string ModuleId { get; set; }
public string Owner { get; set; }
public string KindName { get; set; }
public string TypeName { get; set; }
public DateTime Start { get; set; }
public DateTime? End { get; set; }
public IEnumerable<Property> Properties { get; set; }
}
Method from the repository
public OwnedModuleDetails GetModuleDetails(long id, string owner)
{
// ReSharper disable ImplicitlyCapturedClosure
var module = (_dbSis.OwnedModules.Where(t => t.Id == id).Select(m => new OwnedModuleDetails
// ReSharper restore ImplicitlyCapturedClosure
{
Id = id,
ModuleId = m.ModuleId,
TypeName = m.ModuleType.TypeName,
KindName = m.ModuleType.ModuleKind.KindName,
Owner = owner,
Start = m.Start,
End = m.End,
Properties = m.PropertyConfiguration.PropertyInstances.Select(
x => new Property { Name = x.Property.Name, Value = x.Value })
}));
return (module.FirstOrDefault());
}
Try adding a space:
<dt>Ownership End Date</dt>
<dd>
#if (Model.End != null)
{
#Model.End
}
else
{
#:
}
</dd>
I had this issue once, when user inserted null value in date column.
Which lead to this error:
System.InvalidOperationException: Nullable object must have a value
To solve this instead of DisplayFor just use below code in your view , so if the column value is null it will display Date is Empty
<td>
#(item.Startdate.HasValue ? item.Startdate.Value.ToString("dd/MM/yyyy") : "Date is Empty")
</td>
Further reading this and this for model
Hope helps someone.
you can use C# null coalescing operator:
#(Model.End?.ToString("dd/MM/yyyy") ?? "Date is Empty")
Won't a ternary operation work here:
#(Model.End != null ? Model.End.ToString() : "")
I have two model classes - Parent and Child which are only linked via typed navigational properties.
public class Parent {
[Key]
[Required]
public int Id { get; set; }
[Required]
public string ParentName { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child {
[Key]
[Required]
public int Id { get; set; }
[Required]
public string ChildName { get; set; }
[Required]
public virtual Parent Parent { get; set; }
}
Now I want to create a new Child for a parent using ASP.Net MVC. First, I need to show a view to the user. I need to somehow pass the parent object key to the view. I also want to show the ParentName. I just fetch the Parent object from the database, create a new Child object, set its Parent property to the fetched parent object.
public ActionResult Create(int parentId) {
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
var child = new Child() { Parent = parent};
return View(child);
}
After the user fills the form, the data is sent to the Create action using HTTP POST.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Child child)
{
if (ModelState.IsValid)
{
//db.Parents.Attach(child.Parent); //Added later
db.Children.Add(child);
db.SaveChanges();
return RedirectToAction("Index", new { parentId = child.Parent.Id });
}
}
Here I've hit my first problem. The child.Parent was not null and child.Parent.Id was correct, but EF trashed it and created a new empty parent (with a different key) in the database and linked the child to it. I've fixed this problem by attaching the child.Parent to the data context before adding the child (db.Parents.Attach(child.Parent)).
But then I was hit with another problem. At first, my model classes were wrong and didn't have the [Required] attributes thus creating nullable database table columns. I've added the attribute and the code stopped working. The code doesn't work because ModelState.IsValid is false which happens because child.Parent.Name of the passed child is null.
How can the problem of adding the child to the parent be solved? I'm interested in solution which:
Uses EF Code-First and ASP.Net MVC
Doesn't involve fetching the child.Parent from the database just to make the model validator happy.
Doesn't involve adding explicit foreign key (ParentId) to the model.
Is this possible?
I think attempting to attach a parent to the child is a little backwards. Typically you would attach a child to a parent. A new parent is being created most likely because you are not including an input element with the parent id in your child model. So when Child child is ModelBound coming into the POST, parent id is probably null. EF sees this and thinks you want to create a new parent too.
Also, since your parentId is part of your route, you don't need to specify it in your view model unless you are doing special things to your Html.BeginForm() in your view. Meaning, if you just use Html.BeginForm, it will post with the same URL values that you sent to the GET request.
Create Method
public ActionResult Create(int parentId) {
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
return View(new Child());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int parentId, Child child)
{
if (ModelState.IsValid)
{
//Probably not a bad idea to check again...just to be sure.
//Especially since we are attaching a child to the parent object anyways.
var parent = db.Parents.Find(parentId);
if (parent == null) {
return HttpNotFound();
}
parent.Childern.Add(child);
db.SaveChanges();
return RedirectToAction("Index", new { parentId = parentid });
}
}
Here is a link to a full answer to your question. The short answer is, that when you work with disconnected entities, EF will not respect already set entity Ids, and will mark the whole entity graph as new(e.g Added).
I personally don't like it, and simply overrride the SaveChanges(), though it works as below when you have an EnityBase base class with an int(or long) Id property (which I find extremely convenient)
public override int SaveChanges()
{
this.ChangeTracker.Entries()
.Where(x => x.Entity is EntityBase && x.State == EntityState.Added && ((EntityBase) x.Entity).Id > 0)
.ForEach(x => x.State = EntityState.Unchanged);
return base.SaveChanges();
}
Think this should work for Create method:
public ActionResult Create([Bind(Exclude="Parent")]Child child)
public class Menu
{
//public Menu()
//{
// {
// this.Templates = new HashSet<MenuTemplate>();
// }
//}
[Key]
public int MenuId { get; set; }
[Column("MenuCaption")]
[Display(Name = "Menu Caption")]
[StringLength(100)]
public string MenuCaption { get; set; }
[Display(Name = "Parent Menu")]
public int? ParentMenuId { get; set; }
public virtual Menu ParentMenu { get; set; }
[Display(Name = "Is Group")]
public bool IsGroup { get; set; }
[Display(Name = "Menu Order")]
public int MenuOrder { get; set; }
[Display(Name = "Visibility")]
public bool Visibility { get; set; }
[Display(Name = "Visibility Main Menu")]
public bool VisibilityMM { get; set; }
[Column("Controller")]
[Display(Name = "Controller")]
[StringLength(100)]
public string Controller { get; set; }
[Column("Action")]
[Display(Name = "Action")]
[StringLength(150)]
public string Action { get; set; }
[Display(Name = "Icon")]
public int? IconID { get; set; }
[ForeignKey("IconID")]
public virtual Icon Icon { get; set; }
public virtual ICollection<MenuTemplate> Templates { get; set; }
}
var dataList = db.Menus.Include(X => X.Icon).ToList();
var ViewModellist = dataList.Join(dataList,
a => a.ParentMenuId,
b => b.MenuId,
(_menu, _parent) => new MenuView
{
MenuId = _menu.MenuId,
Action = _menu.Action,
Controller = _menu.Controller,
IsGroup = _menu.IsGroup,
MenuCaption = _menu.MenuCaption,
MenuOrder = _menu.MenuOrder,
ParentMenuId = _menu.ParentMenuId,
Visibility = _menu.Visibility,
VisibilityMM = _menu.VisibilityMM,
PMenuName = _parent.MenuCaption
}).ToList();
if (PId == 0)
{
var hierarchyList = ViewModellist.Where(x => x.ParentMenuId == null).OrderBy(x => x.MenuOrder).
Select(x => new MenuView
{
MenuId = x.MenuId,
Action = x.Action,
Controller = x.Controller,
IsGroup = x.IsGroup,
MenuCaption = x.MenuCaption,
MenuOrder = x.MenuOrder,
ParentMenuId = x.ParentMenuId,
PMenuName = x.PMenuName,
Visibility = x.Visibility,
VisibilityMM = x.VisibilityMM,
ChildList = GetChildMenulist(x.MenuId, ViewModellist)
}).FirstOrDefault();
return View(hierarchyList);
}
else
{
var hierarchyList = ViewModellist.Where(x => x.MenuId == PId).OrderBy(x => x.MenuOrder).
Select(x => new MenuView
{
MenuId = x.MenuId,
Action = x.Action,
Controller = x.Controller,
IsGroup = x.IsGroup,
MenuCaption = x.MenuCaption,
MenuOrder = x.MenuOrder,
ParentMenuId = x.ParentMenuId,
PMenuName = x.PMenuName,
Visibility = x.Visibility,
VisibilityMM = x.VisibilityMM,
ChildList = GetChildMenulist(x.MenuId, ViewModellist)
}).FirstOrDefault();
return PartialView("_Index", hierarchyList);
}