C# MVC EF - Set a foreign key to NULL? - c#

I am trying to set a foreign key to "Null".. Using the SQL Management Studio it works fine, but trying it using C#, I am getting a exception:
Exception:
Value cannot be null. Parameter name: entity.
Code:
var entity = _db.AlleBenutzer.FirstOrDefault(p => p.Id == id);
if (entity != null)
{
var abteilungObjekt = _db.AlleAbteilungen.FirstOrDefault(p => p.Abteilungsname == abteilung);
var etageObjekt = _db.AlleEtagen.Include(p => p.Abteilung).FirstOrDefault(p => p.Etagenname == etage);
entity.Abteilung = abteilungObjekt;
entity.Etage = etageObjekt;
_db.Entry(entity.Abteilung).State = EntityState.Modified;
_db.Entry(entity.Etage).State = EntityState.Modified;
_db.Entry(entity).State = EntityState.Modified;
_db.SaveChanges();
}
My both models look like that: (cut some pieces)
public class Benutzer
{
public int Id { get; set; }
public Abteilung Abteilung { get; set; }
public Etage Etage { get; set; }
}
public class Abteilung
{
public int Id { get; set; }
public string Abteilungsname { get; set; }
}
Can anyone help me out? How do I modify my model to be able to make my Foreign Keys nullable? Thanks in advance :)

In your model the ID that is acting as your FK should be an int? to make it nullable.

Try:
var entity = _db.AlleBenutzer.FirstOrDefault(p => p.Id == id);
if (entity != null)
{
var abteilungObjekt = _db.AlleAbteilungen.FirstOrDefault(p => p.Abteilungsname == abteilung);
if (abteilungObjekt != null)
{
entity.Abteilung = abteilungObjekt;
_db.Entry(entity.Abteilung).State = EntityState.Modified;
}
var etageObjekt = _db.AlleEtagen.Include(p => p.Abteilung).FirstOrDefault(p => p.Etagenname == etage);
if (etageObjekt != null)
{
entity.Etage = etageObjekt;
_db.Entry(entity.Etage).State = EntityState.Modified;
}
_db.Entry(entity).State = EntityState.Modified;
_db.SaveChanges();
}
As it looks like either abteilungObjekt or etageObjekt may be null when you try to assign them to the entity. You should always perform a null check when using FirstOrDefault() on reference types.

Related

Linq to SQL column computed from query to different table?

Is it possible to define a column in one of my Linq to SQL entities such that it's calculated as part of the SQL query?
Here is my Entity class so far:
[Table(Name = "Products")]
public class ProductEntity
{
[Column(Name = "ModelNumber", IsPrimaryKey = true)]
public string ModelNumber { get; set; }
[Column(Name = "SerialNumber", IsPrimaryKey = true)]
public string SerialNumber { get; set; }
}
I want to add a new property like:
[Column]
public bool ActiveListing { get; set; }
Except its value should be calculated based on a complicated Linq query which I am executing elsewhere right now (not in SQL!):
context.GetTable<SalesRecords>().
Where(ah => ah.Date <= GlobalCoordinatedDateTime.Local).
Where(ah => ah.ProductModelNumber == ModelNumber && ah.ProductSerialNumber == SerialNumber).
OrderByDescending(ah => ah.Date).
FirstOrDefault().Status == Statuses.Active;
My best guess is that I could use AssociationAttribute but I don't know how to do that when there could be more than one match in the "SalesRecords" table and I want to get the first after applying some ordering.
EDIT: My question is not the same as a simple "computed column" as seen in this question, I need to compute the value using data stored in different tables, outside of the entity its self.
if you don't need to store the column at database you can use:
[Table(Name = "Products")]
public class ProductEntity
{
// your other columns...
[NotMapped]
public bool ActiveListing {
get
{
bool result = false;
// your logic to calculate then set to "result" variable
return result;
}
}
}
but if you need to store it, change the name of ActiveListing property, then manually assign to the final ActiveListing property before you will create or update the record. Example:
[Table(Name = "Products")]
public class ProductEntity
{
// your other columns...
[NotMapped]
public bool CalculateActiveListing
{
get
{
bool result = false;
// your logic to calculate then set to "result" variable
return result;
}
}
public bool ActiveListing { get; set; }
}
here an example if you have a navigation property to SalesRecords. important have Lazy Loading enabled, or use the Include() method.
[NotMapped]
public bool CalculateActiveListing
{
get
{
bool result = false;
// your logic to calculate then set to "result" variable.
// for example:
// validate SalesRecords has data
if (this.SalesRecords != null)
{
var sale = this.SalesRecords
.Where(ah => ah.Date <= GlobalCoordinatedDateTime.Local)
.Where(ah => ah.ProductModelNumber == ModelNumber && ah.ProductSerialNumber == SerialNumber)
.OrderByDescending(ah => ah.Date)
.FirstOrDefault();
// sale exists
if (sale != null)
{
result = sale.Status == Statuses.Active;
}
}
return result;
}
}
another example using your DbContext:
[NotMapped]
public bool CalculateActiveListing
{
get
{
bool result = false;
// your logic to calculate then set to "result" variable.
// for example:
using (var context = new MyDbContext())
{
var sale = context.SalesRecords
.Where(ah => ah.Date <= GlobalCoordinatedDateTime.Local)
.Where(ah => ah.ProductModelNumber == ModelNumber && ah.ProductSerialNumber == SerialNumber)
.OrderByDescending(ah => ah.Date)
.FirstOrDefault();
// sale exists
if (sale != null)
{
result = sale.Status == Statuses.Active;
}
}
return result;
}
}
sorry, my bad english.

Soft Delete the childs of my entity

I have a sample with sampleItems.
I delete a sampleItem from the list of sampleItems in sample.
I save the DBContext.
I want to SoftDelete the sampleItem.
My code:
public class Entity: {
public Guid Id { get; set; }
public bool IsDeleted { get; set; }
}
public class Sample : Entity{
public string Text { get; set; }
public List<SampleItem> SampleItems { get; set; } = new List<SampleItem>();
}
public class SampleItem :Entity{
public string Text { get; set; }
public virtual Sample Sample { get; set; }
}
When I get the entity from the DB:
var sample = context.Samples.First(s=>s.Id == myId);
sample.SampleItems.RemoveAt(index);
context.SaveChanges();
In MyDbContext I have:
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
foreach (var entry in ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Added:
//Do stuff
break;
case EntityState.Modified:
//Do stuff
break;
case EntityState.Deleted:
if (entry.Entity is Entity entity)
{
entry.State = EntityState.Unchanged;
entity.IsDeleted = true;
}
break;
}
}
return base.SaveChangesAsync(cancellationToken);
}
So I expected to have the SampleItem I deleted with an entryState to Deleted, but not, the entryState is set to Modified. But When I look in the database after operations done, the sampleItem is deleted.
How can I softDelete the sampleItem in this situation?
EDIT:
I read here: https://github.com/aspnet/EntityFrameworkCore/issues/11240, the answer from ajcvickers that I could use "entry.Navigations".
I notice that SampleItem has NavigationEntries, all SampleItems are in it except the one I deleted.
So maybe we can imagine a linq function to the one who are not in the list? I m trying with no success currently. Is there someone who knows how to do it?
var x = ChangeTracker.Entries()
.Where(e => !e.Navigations
.Where(n => !n.Metadata.IsDependentToPrincipal())
.Where(n => n is CollectionEntry)
.Select(n => n as CollectionEntry)
.SelectMany(n => n.CurrentValue.Cast<object>())
.Select(Entry)
.Contains(e)
)
.ToList();
EDIT 2
Here: https://github.com/aspnet/EntityFrameworkCore/issues/3815, I found a way to disabled Cascade delete.
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
Now I have an error when I execute the SaveChanges:
"The association between entities 'Sample' and 'SampleItem' with the
key value '{Id: 1c41c336-b75b-4f6b-6057-08d5f3d981ae}' has been
severed but the relationship is either marked as 'Required' or is
implicitly required because the foreign key is not nullable. If the
dependent/child entity should be deleted when a required relationship
is severed, then setup the relationship to use cascade deletes."
(more details about Cascade configuration: https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete)
Which is normal. Now I need to find to disable the delete of the SampleItem and enable the SoftDelete.
Do you have an idea?
You are probably solved this already BUT remove the sample.SampleItems.RemoveAt(index); and replace with:
// Do the soft delete
db.IsSoftDelete = true;
db.Entry(activity).State = EntityState.Modified;
db.UserId = userId;
await db.SaveChangesAsync();
Then on your Override SaveChanges:
public bool IsSoftDelete { get; set; }
public override int SaveChanges()
{
...
// Modified State
var modified = this.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Modified)
.Select(t => t.Entity)
.ToArray();
foreach (var entity in modified)
{
if (entity is ITrack)
{
var track = entity as ITrack;
Entry(track).Property(x => x.CreatedDate).IsModified = false;
Entry(track).Property(x => x.CreatedBy).IsModified = false;
track.ModifiedDate = DateTime.UtcNow;
track.ModifiedBy = UserId;
if (IsSoftDelete)
{
track.IsDeleted = true;
track.DeletedDate = DateTime.UtcNow;
track.DeletedBy = UserId;
}
}
}

ASP.NET MVC - Attaching an entity of type 'X' failed

This is a model;
public class Urunler
{
public int UrunlerID { get; set; }
public virtual Urunler Urun { get; set; }
[NotMapped]
public List<int> SelectedEtiketIds { get; set; }
public virtual List<UrunEtiketTablo> Etiketler { get; set; }
}
This is a code ;
if (model.SelectedEtiketIds != null)
{
if (model.Etiketler != null)
{
if (model.Etiketler.Count > 0)
{
model.Etiketler.Where(i => !model.SelectedEtiketIds.Contains(i.UrunEtiket.EtiketID)).ToList().ForEach(i => model.Etiketler.Remove(i));
List<int> existlbl = model.Etiketler.Select(i => i.UrunEtiket.EtiketID).ToList();
db.Etikets.Where(i => model.SelectedEtiketIds.Except(existlbl).Contains(i.EtiketID)).ToList().ForEach(i => model.Etiketler.Add(new UrunEtiketTablo { UrunEtiket = i }));
}
else
{
db.Etikets.Where(i => model.SelectedEtiketIds.Contains(i.EtiketID)).ToList().ForEach(i => model.Etiketler.Add(new UrunEtiketTablo { UrunEtiket = i }));
}
}
else
{
model.Etiketler = db.Urunlers.Where(i => i.UrunlerID == model.UrunlerID).Select(i => i.Etiketler).FirstOrDefault();
db.Etikets.Where(i => model.SelectedEtiketIds.Contains(i.EtiketID)).ToList().ForEach(i => model.Etiketler.Add(new UrunEtiketTablo { UrunEtiket = i }));
}
}
else
{
if (model.Etiketler !=null && model.Etiketler.Count > 0)
{
model.Etiketler.Clear();
}
}
db.Entry(model).State = EntityState.Modified; //error line
}
db.SaveChanges();
return RedirectToAction("Urunler", "DaimiPanel");
When i add item to etiket list , i getting this error.
Attaching an entity of type 'Tasarito.Models.UrunEtiketTablo' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
This is cross-table;
public class UrunEtiketTablo
{
public int UrunEtiketTabloID { get; set; }
public virtual Etiket UrunEtiket { get; set; }
}
Where can I make mistakes? ty.
I found the same problem and in my case, I just change the code
db.Entry(model).State = EntityState.Modified; //error line
into
db.Set<YourModel>().AddOrUpdate(model);
and it works fine.
Hopefully it can works in your problem as well.

How to update foreign key using entitystate.modified?

I am new to MVC3 im trying to perform update function after edit which contents two foreign keys (BRANCH_ID,ITEM_MASTER_ID).
The problem im facing easy when branch_id or Item_master_id are not changed the row gets updated but if the foreigns keys change its throwing me an error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
Here is my model
public partial class MATERIAL
{
public int ID { get; set; }
public int ITEM_MASTER_ID { get; set; }
public int BRANCH_MASTER_ID { get; set; }
public string NAME { get; set; }
public string ADDRESS_DETAILS { get; set; }
public virtual BRANCH_MASTER BRANCH_MASTER { get; set; }
public virtual ITEM_MASTER ITEM_MASTER { get; set; }
}
My edit function code
[HttpPost]
public ActionResult Edit(MATERIAL material)
{
if (ModelState.IsValid)
{
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();
int tempid = material.ID;
return RedirectToAction("listcontinue", new { id = tempid });
}
return View(material);
}
Help me in perform update even if my foreigns keys are changed.
here is my improved edit code
public ActionResult Edit(MATERIAL material)
{
var temp = Convert.ToString(material.ITEM_NAME);
using (var context = new material_managementEntities())
{
var temp1 = (from cs in context.ITEM_MASTER
where cs.ITEM_NAME == temp
select cs.ID).FirstOrDefault();
material.ITEM_MASTER_ID = temp1;
}
var temp2 = Convert.ToString(material.ITEMGROUP);
using (var context = new material_managementEntities())
{
var temp3 = (from cs in context.ITEM_GROUP_MASTER
where cs.ITEM_GROUP_NAME == temp2
select cs.ID).FirstOrDefault();
material.ITEM_MASTER_ITEM_GROUP_MASTER_ID = temp3;
}
if (ModelState.IsValid)
{
db.MATERIALs.Attach(material);
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();
int tempid = material.ID;
return RedirectToAction("listcontinue", new { id = tempid });
}
return View(material);
}
I think you forgot to attach the object:
db.Materials.Attach(material);
db.Entry(material).State = EntityState.Modified;
db.SaveChanges();
I think in the context EF doesn't know about the foreign keys, I had to use an includes first before I update the foreign key:
db.Entry(team).State = EntityState.Modified;
db.SaveChanges();
var updatedTeam = db.Teams.Include(x=> x.GameType).Where(x=> x.ID == team.ID).Single();
updatedTeam.GameType = db.GameTypes.Find(GameTypeId);
db.SaveChanges();

C# Entity Framework with linq returns null reference

I have a problem with entity framework in C#.
I have 2 entities, User and UserRole. They are bond by relationships User *->1 UserRole
Whenever I use this query in a function:
User user = context.User.Where(i => i.id == id).FirstOrDefault();
return user.UserRole.accessLevel;
The query returns user, but UserRole is null. The User table has roleId which is related to id of UserRole, and the value of roleId when debugging is correct, although UserRole entity is null. This is strange as it never happened before...
I already made sure that my relationships in model and database are correct. I have correct rows added to database.
EDIT:
Sorry, I should've mentioned I use custom testable database controller:
public class DBController : IUnitOfWork
{
readonly ObjectContext context;
const string ConnectionStringName = "MarketPlaceDBEntities";
public DBController()
{
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;
context = new ObjectContext(connectionString);
}
public void Commit()
{
context.SaveChanges();
}
public IObjectSet<Category> Category
{
get { return context.CreateObjectSet<Category>(); }
}
public IObjectSet<ItemComment> ItemComment
{
get { return context.CreateObjectSet<ItemComment>(); }
}
public IObjectSet<ItemRating> ItemRating
{
get { return context.CreateObjectSet<ItemRating>(); }
}
public IObjectSet<Item> Item
{
get { return context.CreateObjectSet<Item>(); }
}
public IObjectSet<ItemSale> ItemSale
{
get { return context.CreateObjectSet<ItemSale>(); }
}
public IObjectSet<ItemScreenshot> ItemScreenshot
{
get { return context.CreateObjectSet<ItemScreenshot>(); }
}
public IObjectSet<UserRole> UserRole
{
get { return context.CreateObjectSet<UserRole>(); }
}
public IObjectSet<User> User
{
get { return context.CreateObjectSet<User>(); }
}
}
And I do operations via it. Maybe it has to do something with my prob.
interface IUnitOfWork
{
IObjectSet<Category> Category { get; }
IObjectSet<ItemComment> ItemComment { get; }
IObjectSet<ItemRating> ItemRating { get; }
IObjectSet<Item> Item { get; }
IObjectSet<ItemSale> ItemSale { get; }
IObjectSet<ItemScreenshot> ItemScreenshot { get; }
IObjectSet<UserRole> UserRole { get; }
IObjectSet<User> User { get; }
void Commit();
}
I had this whole thing working before, but don't know why it went wrong..
EDIT2:
Solved! Thanks RicoSuter.
Enabling lazy loading in constructor of my db controller solved the problem. I thought it was already enabled, because it was set to true in database model, but it looks like that when creating a new context, you have to enable it manually again.
public DBController()
{
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;
context = new ObjectContext(connectionString);
context.ContextOptions.LazyLoadingEnabled = true;
}
try to eagerly load UserRole (join):
context.User.Include("UserRole").Where(i => i.id == id).FirstOrDefault();
or enable lazy loading first:
context.ContextOptions.LazyLoadingEnabled = true;
context.User.Where(i => i.id == id).FirstOrDefault();
otherwise there is no relation to a UserRole in your database...
Try this
User user = context.User.Where(i => i.id == id).FirstOrDefault();
return user==null?null:user.UserRole.accessLevel;
Most simply u can do this:
UserRole user = context.User.Where(i => i.id == id).Select(i => i.UserRole);
return user.accessLevel;
Edit:
Assuming you already have a relation between User and UserRole
User user = context.User.Where(i => i.id == id).FirstOrDefault();
UserRole role = context.UserRole.Where(i => user.Contains(i.id)).FirstOrDefault();
return role.accessLevel;
Assuming you dont have a relation between User and UserRole
int roleid = Convert.ToInt32(context.User.Where(i => i.id == id).Select(i => i.roleid));
UserRole role = context.UserRole.Where(i => i.id == roleid).FirstOrDefault();
return role.accessLevel;
Also if you have relation but cant see UserRole under User than try adding this to your model
public IDisposable User()
{
YourDataContext context = new YourDataContext();
Type ct = context.User.GetType();
return (IDisposable)(ct);
}

Categories