Entity - sprawdzenie spójności bazy danych przed usunięciem rekordu

0

Witam,
Mam prostą bazę danych stworzoną z trzech tabel (relacje wiele do 1) w Entity Framework obsługiwaną za pomocą interfejsu webowego MVC 3.0. Do tabel zostały wygenerowane kontrolery CRUD.

Problem: usunięcie wymaganego elementu np. kategorii powoduje usunięcie wszystkich książek, które należały do tej kategorii. Rozumiem, że Framework zachowuje w ten sposób spójność bazy, ale jest to dla mnie wysoce niepożądane.

Pytanie: Jak najprościej wymusić by operacja usuwania została zaniechana, gdy inne tabele wymagają istnienia danego rekordu?

Myślałem, żeby obejść to tworząc zapytania LINQ. Wydaje mi się to jednak nieco skomplikowane i pewnie da się prościej, w końcu to raczej elementarna funkcjonalność. Jeśli nie da się inaczej będę wdzięczny za przykład albo naprowadzenie, gdzie szukać.

Moja baza danych:

using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations;

namespace Firmowa.Models
{
public class Author
{
public int AuthorID { get; set; }
[Required(ErrorMessage = "Imię jest wymagane")]
[Display(Name="Imię")]
[MaxLength(45)]
public string Name { get; set; }
[Required(ErrorMessage = "Nazwisko jest wymagane")]
[Display(Name="Nazwisko")]
[MaxLength(45)]
public string Surname { get; set; }
}
}


<code class="c#">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace Firmowa.Models
{
    public class Book
    {
        public int BookID { get; set; }
        [Required(ErrorMessage = "Tytuł jest wymagany")]
        [Display(Name = "Tytuł")]
        public string Title { get; set; }
        [Required(ErrorMessage = "ISBN jest wymagany")]
        public string ISBN { get; set; }
        [Required(ErrorMessage = "Autor jest wymagany")]
        [Display(Name = "Autor")]
        public int AuthorID { get; set; }
        [Display(Name = "Rok")]
        [Required(ErrorMessage = "Rok jest wymagany")]
        public uint year { get; set; }
        [Display(Name = "Wydawca")]
        [Required(ErrorMessage = "Wydawca jest wymagany")]
        public string Publisher { get; set; }
        [Display(Name = "Kategoria")]
        [Required(ErrorMessage = "Kategoria jest wymagana")]
        public int CategoryID { get; set; }

        public virtual Author Author { get; set; }
        public virtual Category Category { get; set; }
    }
}
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; using Firmowa.Models; using Firmowa.Models.DAL;

namespace Firmowa.Models
{
public class BookContext : DbContext
{
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
public DbSet<Category> Categories { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Entity<Book>().HasRequired(x => x.Author);
        modelBuilder.Entity<Book>().HasRequired(x => x.Category);
    }
}

}


<code class="c#">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace Firmowa.Models
{
    public class Category
    {
        public int CategoryID { get; set; }
        [Required(ErrorMessage = "Nazwa kategorii jest wymagana")]
        [Display(Name = "Kategoria")]
        public string Name { get; set; }
        [Required(AllowEmptyStrings = true)]
        [Display(Name="Opis")]
        public string Description { get; set; }
    }
}

Pozdrawiam,

0

http://stackoverflow.com/questions/7430619/what-is-difference-between-hasrequired-and-hasoptional

Zmień HasRequired na HasOptional w modelBuilder.Entity<Book>().HasRequired(x => x.Category);

0

Dzięki za zainteresowanie, ale oczekuje czegoś innego.

Moim celem jest uzyskanie efektu, w którym nie mogę usunąć rekordu jeśli jest potrzebny (a nie wstawię NULL).

Jak zablokować usuwanie rekordu, gdy jest zależnością w innych tabelach?

0

Nie prościej dołożyć jakąś warstwę abstrakcji i zrobić tak:

public bool TryDeleteCategory(Category cat)
{
   if(context.Books.Any(b => b.Category == cat)
     return false;
   context.Category.Remove(cat);
   return true;
 }
 

Z głowy, więc o poprawność nie ręczę.

0

Pomysł wygląda prosto i fajnie.

Chwile myślałem gdzie go podłączyć.

Dorzuciłem taki kod do kontolera CRUD edycji kategorią:

namespace Firmowa.Controllers { public class CategoryAdminController : Controller { private BookContext db = new BookContext();
    private bool TryDeleteCategory(Category cat)
    {
        /*
        if (db.Books.Any(b => b.Category == cat))
            return false;
         */
        db.Categories.Remove(cat);
        return true;
    }

    //
    // GET: /CategoryAdmin/

    public ViewResult Index()
    {
        return View(db.Categories.ToList());
    }

...
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Category category = db.Categories.Find(id);
db.Categories.Remove(category);
db.SaveChanges();
return RedirectToAction("Index");
}


Niestety wykrzacza się na wyrażeniu Lambda (chyba to jest właśnie Lambda?, to będzie mój pierwszy program w C# - kod zakomentowany).

Kompiluje się, zwrócony wyjątek to:

> Unable to create a constant value of type 'Firmowa.Models.Category'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.


Czyli należy inaczej iterować. Idę szukać informacji jak.
0

Wedle tego co tutaj piszą, Linq to Entities nie umie przetworzyć porównywania klas. To znaczy że porównanie:

b => b.Category == cat

nie może zostać przetłumaczone do kodu SQL.
Zmień to na:

b => b.Category.CategoryID == cat.CategoryID

O więcej info:
http://www.matthidinger.com/archive/2008/02/26/entity-framework-comparison-frustration-explained.aspx

0

Solved, bardzo Ci dziękuje.

Kod kontrolera:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Firmowa.Models;

namespace Firmowa.Controllers
{
public class CategoryAdminController : Controller
{
private BookContext db = new BookContext();

    private bool TryDeleteCategory(Category cat)
    {
        if (db.Books.Any(b => b.Category.CategoryID == cat.CategoryID))
            return false;

        db.Categories.Remove(cat);
        return true;
    }

    //
    // GET: /CategoryAdmin/

    public ViewResult Index()
    {
        return View(db.Categories.ToList());
    }

    //
    // GET: /CategoryAdmin/Details/5

    public ViewResult Details(int id)
    {
        Category category = db.Categories.Find(id);
        return View(category);
    }

    //
    // GET: /CategoryAdmin/Create

    public ActionResult Create()
    {
        return View();
    } 

    //
    // POST: /CategoryAdmin/Create

    [HttpPost]
    public ActionResult Create(Category category)
    {
        if (ModelState.IsValid)
        {
            db.Categories.Add(category);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(category);
    }
    
    //
    // GET: /CategoryAdmin/Edit/5

    public ActionResult Edit(int id)
    {
        Category category = db.Categories.Find(id);
        return View(category);
    }

    //
    // POST: /CategoryAdmin/Edit/5

    [HttpPost]
    public ActionResult Edit(Category category)
    {
        if (ModelState.IsValid)
        {
            db.Entry(category).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(category);
    }

    //
    // GET: /CategoryAdmin/Delete/5

    public ActionResult Delete(int id)
    {
        Category category = db.Categories.Find(id);
        return View(category);
    }

    //
    // POST: /CategoryAdmin/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Category category = db.Categories.Find(id);
        if (TryDeleteCategory(category))
        {
            db.Categories.Remove(category);
            db.SaveChanges();
        }
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

}

1 użytkowników online, w tym zalogowanych: 0, gości: 1