The conflict occurred in database "X", table "dbo.Y", column 'ld' - asp.net-mvc

I'm trying to get a certain amount of animals that have the most comments once I try to delete one of them so I'm getting an error of:
SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK__Comments__Animal__2EDAF651". The conflict occurred in database "PetShop", table "dbo.Comments", column 'AnimalId'. The statement has been terminated.
I want to make it possible that if I delete then you will move on to the next in line
My Controller for disply:
public async Task<IActionResult> Index()
{
var animal = _context.Animals.Include(c => c.Comments).OrderByDescending(c => c.Comments.Count).Take(2);
return View(await animal.ToListAsync());
}
My Controller for Delete:
public async Task<Animal> DeleteAnimal(int id)
{
var comment = await _context.Comments.FindAsync(id);
_context.Comments.Remove(comment!);
var animal = await _context.Animals.FindAsync(id);
_context.Animals.Remove(animal!);
await _context.SaveChangesAsync();
return animal!;
}
My Context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Comment>(entity =>
{
entity.HasOne(d => d.Animal)
.WithMany(p => p.Comments)
.HasForeignKey(d => d.AnimalId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK__Comments__Animal__2EDAF651");
});
modelBuilder.Entity<Category>(entity =>
entity.HasData(
new { CategoryId = 1, Name = "Dogs" },
new { CategoryId = 2, Name = "Cats" },
new { CategoryId = 3, Name = "Birds" },
new { CategoryId = 4, Name = "Rabbits" },
new { CategoryId = 5, Name = "Hamsters" }
)
);
modelBuilder.Entity<Animal>(entity =>
{
entity.HasData(
new { AnimalId = 1, Name = "Shoko", BirthDate = DateTime.Now.AddYears(-1).AddMonths(-1).AddDays(-12), Description = "Friendly and loyal", CategoryId = 1, PhotoUrl = "ShokoDog.jpg" },
new { AnimalId = 2, Name = "Bamba", BirthDate = DateTime.Now.AddYears(-2).AddMonths(-2).AddDays(-3), Description = "Furry and neutered", CategoryId = 2, PhotoUrl = "BambaCat.jpg" },
new { AnimalId = 3, Name = "Regev", BirthDate = DateTime.Now.AddYears(-1).AddMonths(-3).AddDays(-3), Description = "Speak", CategoryId = 3, PhotoUrl = "RegevBird.jpg" },
new { AnimalId = 4, Name = "Humi", BirthDate = DateTime.Now.AddYears(-3).AddMonths(-4).AddDays(-7), Description = "Cute and furry", CategoryId = 4, PhotoUrl = "HumiRabbit.jpg" },
new { AnimalId = 5, Name = "Tommy", BirthDate = DateTime.Now.AddYears(-1).AddMonths(-7).AddDays(-9), Description = "Love to play in the facilities", CategoryId = 5, PhotoUrl = "TommyHamster.jpg" });
});
OnModelCreatingPartial(modelBuilder);
}

You want to remove a parent Animal and related child Comments
To expand on Karlis' suggestions:
You have models and context as posted in the question (it's a bit problematic because you say to EF to set null, but the code and DB won't accept null) but you can do:
var a = context.Animals.Include(a => a.Comments).Find(id):
context.Comments.RemoveRange(a.Comments);
context.Animals.Remove(a);
context.SaveChanges();
This explicitly removes the comments then the animal
Change the context to use .OnDelete(DeleteBehavior.ClientCascade) then you can do:
var a = context.Animals.Include(a => a.Comments).Find(id):
context.Animals.Remove(a);
context.SaveChanges();
This causes EF to implicitly remove the comments it knows about when you tell it explicitly to remove the animal
Change the DB's foreign key to do an ON DELETE CASCADE, and change .OnDelete(DeleteBehavior.Cascade) then you can skip downloading the Comments (no include):
var a = context.Animals.Find(id):
context.Animals.Remove(a);
context.SaveChanges();
This causes the DB to remove the comments (EF doesn't know about them) when EF instructs to delete the animal
Broadly speaking, these are in order of "how bad of a mistake could you make" from "not very" to "quite a lot"

The error message reads that you are deleting Animal, which has comments associated. You should do one of the following:
Remove comments associated with a particular Animal before deleting the Animal.
Check EF configuration for cascade on delete
Alter FK to have cascade on delete (it depends on whether you are using a database-first or code-first approach)
I would go for the first approach because cascade on delete may be dangerous and silently remove unintentionally referenced data.

Related

Setting DropDownListFor default text MVC

I am new to MVC, and am trying to do something that should be easy, but it is eluding me. I am using a DropDownListFor in a partial view to display a list of product names. That part works fine. What I am having trouble with is setting the initial value of the DropDownListFor to display "Select one". The code I am working with is as follows:
#Html.DropDownListFor(model => model.ProductId, new SelectList(ViewBag.ProductData, "ProductId", "Name"), "---Select one---", new { htmlAttributes = new { #id = "ProductName" } });
The controller which sets the ViewBag is as follows:
public ActionResult AddProduct(int quoteId, int quoteDetailId)
{
var items = db.Products.ToList();
ViewBag.ProductData = items;
ViewData["QuoteId"] = quoteId;
ViewData["QuoteDetailId"] = quoteDetailId;
return PartialView("EditQuoteDetail", new QuoteDetail { QuoteId = quoteId, QuoteDetailId = quoteDetailId, ProductId = 1, ProductName = " ", Amount = 1, ListPrice = 0, Discount = 0, Price = 0 });
}
Once I hit the dropdown arrow on the DropDownListFor, "Select one" appears at the top of the list. But when the DropDownListFor is first displayed, the first product name appears in the box, instead of "Select one". How can I fix this? Any help would be much appreciated.
Don't specify a value for the id property in your model:
public ActionResult AddProduct(int quoteId, int quoteDetailId)
{
var items = db.Products.ToList();
ViewBag.ProductData = items;
return PartialView("EditQuoteDetail", new QuoteDetail { QuoteId = quoteId, QuoteDetailId = quoteDetailId, ProductId = 0, ProductName = " ", Amount = 1, ListPrice = 0, Discount = 0, Price = 0 });
}

seeded data is duplicated code first migration

I have seeded the database using code first migration, however I noticed when I view the seeded data in index.html, the data is replicated.
This is the configuration file were I seeded the data:
internal sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(OnlineBookStore.Models.OnlineBookStoreDB context)
{
var books = new System.Collections.Generic.List<Book>
{
new Book {
BookStatus = new BookStatus { Status = "New" },
Genre = new Genre { Name = "Thriller" },
Author = new Author { Name = "Paula Hawkins" },
Title = "The Girl On The Train",
Description = "Rachel catches the same commuter train morning. ",
ISBN = 0552779776,
},
new Book
{
BookStatus = new BookStatus { Status = "Best Seller" },
Genre = new Genre { Name = "Childrens" },
Author = new Author { Name = "Roald Dahl" },
Title = "The Witches",
Description = "Beware. Real witches dress in ordinary clothes",
ISBN = 0141365471,
},
},
};
books.ForEach(s =>context.Books.AddOrUpdate(p => new { p.ISBN, p.Title } ));
context.SaveChanges();
}
}
}
I am really unsure of were I am going wrong, spent days on this!
Really appreciate anyones help! thanks!
You need to specify the key in AddOrUpdate to prevent duplicates since Seed() runs with every update-database issued.
// build your books collection
var books = new []
{
new Book {
BookStatus = new BookStatus { Status = "New" },
Genre = new Genre { Name = "Thriller" },
Author = new Author { Name = "Paula Hawkins" },
Title = "The Girl On The Train",
Description = "Rachel catches the same commuter train morning. ",
ISBN = 0552779776,
},
new Book
{
BookStatus = new BookStatus { Status = "Best Seller" },
Genre = new Genre { Name = "Childrens" },
Author = new Author { Name = "Roald Dahl" },
Title = "The Witches",
Description = "Beware. Real witches dress in ordinary clothes",
ISBN = 0141365471,
},
},
};
context.Books.AddOrUpdate(p => new { p.ISBN, p.Title }, books);
context.SaveChanges();
See http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/

ASP.Net MVC 4 Kendo UI Scheduler multiple events get added when update event

I'm new to kendo Ui. I've used the scheduler control in my ASP.Net MVC 4 project and implemented using Ajax calls as defined in following tutorial.
http://docs.telerik.com/kendo-ui/getting-started/using-kendo-with/aspnet-mvc/helpers/scheduler/ajax-editing.
My problem is when I try to edit an event, it will edit the event, plus creates one or more duplicate records in the database with the same data. How should I avoid it.
Following is my Index.cshtml
#(Html.Kendo().Scheduler<Portal.Presentation.Web.BoundedContext.QC.MVC.Areas.Razor.Models.LeavePlan>()
.Name("scheduler")
.Date(DateTime.Now)
.Height(600)
.AllDaySlot(true)
.Views(views =>
{
views.DayView();
views.WeekView(weekView => weekView.Selected(true));
views.MonthView();
})
.Timezone("Etc/UTC")
.Resources(resource =>
{
resource.Add(m => m.PlanTypeId)
.Title("Plan Type")
.DataTextField("Text")
.DataValueField("Value")
.DataColorField("Color")
.BindTo(new[] {
new { Text = "Annual Leave", Value = 1, Color = "#f8a398" } ,
new { Text = "Casual Leave", Value = 2, Color = "#51a0ed" } ,
new { Text = "Sick Leave", Value = 2, Color = "#55a098" } ,
new { Text = "Travel", Value = 3, Color = "#56ca85" }
});
})
.DataSource(d => d
.Model(m => {
m.Id(f => f.TaskID);
m.Field(f => f.ResourceId).DefaultValue(1);
//Set the recurrence ID field from the model:
//m.RecurrenceId(f => f.RecurrenceID);
})
.Read("Tasks_Read", "LeavePlan")
.Create("Tasks_Create", "LeavePlan")
.Destroy("Tasks_Destroy", "LeavePlan")
.Update("Tasks_Update", "LeavePlan")
))
Following is my update method.
public ActionResult Tasks_Update([DataSourceRequest]DataSourceRequest request, LeavePlan leave)
{
if (ModelState.IsValid)
{
// Create a new Task entity and set its properties from the posted TaskViewModel
var entity = new ResourceLeavePlan
{
StartDate = leave.Start
,Title = leave.Title
,EndDate = leave.End.ToUniversalTime()
,ResourceLeavePlanId = (int)leave.TaskID
,IsAllDay=leave.IsAllDay
,RecurrenceId=leave.ResourceId
,Description=leave.Description
,RecurrenceException=leave.RecurrenceException
,LeaveTypeId=(int)leave.PlanTypeId
,ResourceId = resourceId
};
_resourceLeavePlanService.Update(entity);
}
return Json(new[] { leave }.ToDataSourceResult(request, ModelState));
}
And help on this would be appreciated.
The model is same as the model in the tutorial in the above link.
and also would be grateful if some one can explain me the use of "RecurrenceId" in the scheduler
You are just missing unique TaskID in your entity object.
var entity = new ResourceLeavePlan
{
TaskID = leave.TaskID // Just try adding this line please and it would work
,StartDate = leave.Start
,Title = leave.Title
,EndDate = leave.End.ToUniversalTime()
,ResourceLeavePlanId = (int)leave.TaskID
,IsAllDay=leave.IsAllDay
,RecurrenceId=leave.ResourceId
,Description=leave.Description
,RecurrenceException=leave.RecurrenceException
,LeaveTypeId=(int)leave.PlanTypeId
,ResourceId = resourceId
};

Some errors in controller (asp.net mvc)

I am getting some errors in my controller.
At first, I got Suppliers List, then I got Id for all Suppliers, then I got all Users for every Supplier.
public ActionResult Grid(bool? active)
{
var suppliers = Context.Suppliers.AsNoTracking()
.WhereIf(active != null, e => e.Active == active)
.Select(e => new SupplierRow
{
Id = e.Id,
FullName = e.FullName,
Active = e.Active,
Visits = e.Visits,
})
.ToList();
List<int> supplierIds = new List<int>();
foreach (SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
var users = Context.Users.AsNoTracking()
.Where(e => supplierIds.Contains(e.SupplierId))
.Select(e => new UserRow
{
Id = e.Id,
FullName = e.FullName,
Email = e.Email,
Name = e.Name,
Status = e.Status,
Role = e.Role,
SupplierId = e.SupplierId
}).toList();
foreach (UserRow ur in users)
{
foreach (SupplierRow sr in supplier)
{
if (ur.SupplierId == sr.Id)
{
sr.Users.Add(ur);
}
}
}
return PartialView("_Grid", suppliers);
}
here
and here
What's wrong with my code? How to fix that?
The problem is that you are trying to add Guid object to a collection that only accepts int values. Your User.SupplierId is an object of type Guid? (or Nullable<Guid>), while Supplier.Id is Guid. Fix the collection by declaring it as:
List<Guid> supplierIds = new List<Guid>();
Then in you code use:
foreach(SupplierRow sr in suppliers)
{
supplierIds.Add(sr.Id);
}
Do the same thing for users except that you will have to use SupplierId.HasValue and SupplierId.Value to check whether it has a value and to read the value. This is because it is declared as nullable Guid.

MVC with Kendo treeview - remote data JSON or XML

I'm trying to create a Kendo treeview that gets populated from my controller (PropertyController).
The part I'm stuck at is how to format my data in the controller. How could I create a treeview three items deep and pass that to my view to be displayed in the treeview?
#(Html.Kendo().TreeView()
.Name("treeview")
.Events(events => events
.DragStart("PartnershipPage.OnDragStart")
.Drop("PartnershipPage.OnDrop")
.DragEnd("PartnershipPage.OnDragEnd")
)
.HighlightPath(true)
.DragAndDrop(true)
.DataSource(dataSource => dataSource
.Read(read => read
.Action("Index","Tree")
)
)
)
I've included the controller to see if I'm doing it correctly. All that is happening so far is the JSON is being displayed on the screen as text.
Controller:
public ActionResult Index()
{
var org = new Entities();
var eList = new List<Entity>();
var entity1 = new Entity
{
Id = 1,
Name = "LLC-A",
parentId = 0
};
eList.Add(entity1);
var entity2 = new Entity
{
Id = 2,
Name = "LLC-B",
parentId = 0
};
eList.Add(entity2);
var entity3 = new Entity
{
Id = 1,
Name = "LLC-C",
parentId = 2
};
eList.Add(entity3);
var entity4 = new Entity
{
Id = 1,
Name = "LLC-D",
parentId = 2
};
eList.Add(entity4);
org.Entity = eList;
var test = from x in org.Entity
where (x.Name != null)
select new
{
Id = x.Id,
Name = x.Name,
parentId = x.parentId
};
;
return Json(test, JsonRequestBehavior.AllowGet);
}
From: Kendo Site
<%= Html.Kendo().TreeView()
.Name("TreeView")
.BindTo(Model, mapping => mapping
.For<Customer>(binding => binding
.Children(c => c.Orders) // The "child" items will be bound to the the "Orders" property
.ItemDataBound((item, c) => item.Text = c.ContactName) // Map "Customer" properties to TreeViewItem properties
)
.For<Order<(binding => binding
.Children(o => null) // "Orders" do not have child objects so return "null"
.ItemDataBound((item, o) => item.Text = o.OrderID.ToString()) // Map "Order" properties to TreeViewItem properties
)
)
%>
You could put your entities into a list called for example, "myEntities" and return that from the controller to the view:
public ActionResult Index()
{
var ents = getMyEntities(); // some method you have to return the list of your entities
return ents;
}
Then in your view, you can loop through all the Entities in your Model:
#(Html.Kendo().TreeView()
.Name("TreeView")
.Items(treeview =>
{
foreach (var entity in Model)
{
var entityName = entity.Name;
var children = entity.Children;
treeview.Add().Text(entityName ).Expanded(false).Items(branch =>
{
if (children != null)
{
foreach (var child in children)
{
branch.Add().Text(child);
}
}
});
}
}
)
)
I used children because I found it easier to use than parent and so I would change my Entities to something like this:
var entity4 = new Entity
{
Id = 1,
Name = "LLC-D",
Children = <list of children names ... >
};
You can see how I did mine here: Populate KendoUI Treeview with RavenDB Documents
Hope this helps.
Edit in response to: How can I display children of children?
I ran into the same problem as you describe (displaying children of children). Here is how I did it after I had the problem of displaying children of children:
I used EntityFramework:
The DB context class:
public class EntityDBContext : DbContext
{
public DbSet<MyEntity> Entities { get; set; }
}
Controller:
public JsonResult EntitiesForTreeView(int? id)
{
// Here I am using EntityFramework
var entitiesContext = new EntityDBContext();
var myEntity= from e in entitiesContext.Entities
where (id.HasValue ? e.Parent == id : e.Parent == null)
select new
{
id = e.Id,
Name = e.Name,
hasChildren = e.Id
};
return Json(myEntity, JsonRequestBehavior.AllowGet);
}
And the View:
#(Html.Kendo().TreeView()
.Name("treeview")
.DataTextField("Name")
.LoadOnDemand(true)
.HighlightPath(true)
.DataSource(dataSource => dataSource
.Read(read => read
.Action("EntitiesForTreeView", "SiteMap")
)
)
.Checkboxes(true)
)
Hope this helps. Sorry if there is something stupid - I've only been doing web development for 3 months so unmark as answer if needed. Let me know if I can help further. PS: I should mention that after implementing the solution I have shown above, I noticed that the nodes on my tree all have the "+" sign... regardless of whether or not they have any children. If you know how to fix this, please let me know :)

Resources