C# - Entity aktualizacja rekordu w tabeli

0

Witam,
Rozwijam swoją aplikację bazodanową w ASP.NET MVC3. W dużym skrócie, chodzi o wypożyczanie książek. Książka może być wypożyczona lub nie. Jeśli jest wypożyczona nie można zrobić tego ponownie (działa walidacja). Poniższy model skutecznie pobiera z obcej tabeli (książka) informację o tym czy jest wypożyczona i jeśli tak informuje o błędzie.
To już działa, tyle że mam ręcznie wpisane w bazie, że isAvailable = false. Teraz pracuje nad tym by było to interaktywne tzn. aby po wypożyczniu pole w tabeli isAvailable = true.

Problem:
W jaki sposób zaktualizować informację po dodaniu nowego wypożyczenia o statusie książki tzn. aby nie mogły ją wypożyczyć dwie osoby na raz?
Czy jest jakaś funkcja, która zawsze wykonuje się po zmodyfikowaniu wiersza tabeli, bo jakoś nie znalazłem jej w dokumentacji.

Chcę po prostu ustawić na true wartość kolumny IsAvailable po dodaniu nowego wpisu do tabeli Hire.

Stworzyłem następujący model:

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

namespace Firmowa.Models.HireModels
{
public class Hire : IValidatableObject
{
private BookContext context = new BookContext();

    public int HireID { get; set; }
    [Required(ErrorMessage = "Czytelnik jest wymagany!")]
    [Display(Name = "Czytelnik")]
    public int ReaderID { get; set; }
    [Required(ErrorMessage = "Książka jest wymagana!")]
    [Display(Name = "Książka")]
    public int BookID { get; set; }
    public DateTime HireDate { get; set; }
    public DateTime? ReturnDate { get; set; }
    [Display(Name = "Data zwrotu")]
    [Required(ErrorMessage = "Data zwrotu jest wymagana!")]
    public DateTime DeadlineDate { get; set; }

    public virtual Reader Reader { get; set; }
    public virtual Book Book { get; set; }

    public Hire()
    {
        this.HireDate = DateTime.Now;
    }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        
        List<ValidationResult> errors = new List<ValidationResult>();

        if(DeadlineDate <= HireDate)
            errors.Add(new ValidationResult("Termin zwrotu musi być wcześniej niż termin wypożyczenia!"));

        // check whether book is hired
        var row = context.Books.SingleOrDefault(r => r.BookID == this.BookID);
        if (!row.IsAvailable)
            errors.Add(new ValidationResult("Książka jest już wypożyczona!"));

        return errors;
    }
}

}

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

namespace Firmowa.Models.HireModels
{
public class Book : IValidatableObject
{
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 int PublisherID { get; set; }
[Display(Name = "Kategoria")]
[Required(ErrorMessage = "Kategoria jest wymagana")]
public int CategoryID { get; set; }
public bool IsAvailable { get; set; }

    public virtual Author Author { get; set; }
    public virtual Publisher Publisher { get; set; }
    public virtual Category Category { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> errors = new List<ValidationResult>();

        if (year > 2050 || year < 1910)
        {
            errors.Add(new ValidationResult("Nieprawidłowy rok"));
        }

        return errors;
    }
}

}

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.HireModels;
using Firmowa.Models.HireModels.DAL;

namespace Firmowa.Models.HireModels
{
public class BookContext : DbContext
{
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Publisher> Publishers { get; set; }
public DbSet<Reader> Readers { get; set; }
public DbSet<Hire> Hires { 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);
        modelBuilder.Entity<Book>().HasRequired(x => x.Publisher);
        modelBuilder.Entity<Hire>().HasRequired(x => x.Book);
        modelBuilder.Entity<Hire>().HasRequired(x => x.Reader);
    }
}

}


Próbowałem użyć następującego kodu konstruktora tabeli Hire:
<code class="c#">
        public Hire()
        {
            this.HireDate = DateTime.Now;
            // mark book as hired
            var query = from r in context.Books where r.BookID == this.BookID select r;
            foreach (Book r in query)
            {
                r.IsAvailable = false;
            }
            // submit the changes to the database
            context.SaveChanges();
        }

To nie działa. Wartość isAvailable wciąż pozostaje true i można wypożyczyć pozycję jeszcze raz.

Pozdrawiam,

0

Napisałem:

To już działa, tyle że mam ręcznie wpisane w bazie, że isAvailable = false. Teraz pracuje nad tym by było to interaktywne tzn. aby po wypożyczniu pole w tabeli isAvailable = true.

Powinno być:

To już działa, tyle że mam ręcznie wpisane w bazie, że isAvailable = false. Teraz pracuje nad tym by było to interaktywne tzn. aby po wypożyczeniu pole w tabeli isAvailable = false.

Przepraszam za błąd.

0

Sprawdź czy po przypisaniu "r.IsAvailable = false;" wartość r ma EntityState na Modified:
http://msdn.microsoft.com/en-us/library/system.data.entitystate.aspx

0
siararadek napisał(a):

Sprawdź czy po przypisaniu "r.IsAvailable = false;" wartość r ma EntityState na Modified:
http://msdn.microsoft.com/en-us/library/system.data.entitystate.aspx

Zastanawiam się jak to zrobić. Właściwość EntityState dla zmiennej r nie jest dostępna.

Co oznacza:

[BindableTypeAttribute(IsBindable = false)]
[FlagsAttribute]
public enum EntityState;

W szczególności Bindable. Mam to dopisać do klas Book i Hire?
0
  • Problem nierozwiązany **

Zmodyfikowałem tak funkcję konstuktora:

public Hire()
{
this.HireDate = DateTime.Now;
// mark book as hired
context.Database.ExecuteSqlCommand("UPDATE Book SET IsAvailable = 0 WHERE BookID = 1");
context.SaveChanges();
}


Jak widać użyłem natywnego SQLa, który w tym przypadku może być wygodniejszy. Wpisałem na sztywno numer książki. Operacja powiodła się.

Dlaczego to jest słabe?
Ponieważ w momencie tworzenia książka zostaje wypożyczona i nie możemy jej dodać, bo walidator się buntuje. Szukam lepszego pomysłu.

Może jest jakaś funkcja, którą mógłbym utworzyć na zasadzie zdarzenia, która wykona się tuż przed dodaniem wypożyczenia i pozwoli w ten sposób ominąć walidator?
0
  • PROBLEM NIEROZWIĄZANY **
    Usunąłem natywny SQL z konstruktora klasy Hire i umieściłem go w kontrolerze:

    [HttpPost]
    public ActionResult Create(Hire hire)
    {
    if (ModelState.IsValid)
    {
    db.Hires.Add(hire);
    db.SaveChanges();
    }

         ViewBag.ReaderID = new SelectList(db.Readers, "ReaderID", "FullName", hire.ReaderID);
         ViewBag.BookID = new SelectList(db.Books, "BookID", "Title", hire.BookID);
    
         // mark Book as hired
         db.Database.ExecuteSqlCommand("UPDATE Book SET IsAvailable = 0 WHERE BookID = 1");
         db.SaveChanges();
    
         return View(hire);
     }
    
</ul>
Działa tak samo jak umieszczenie w kontrolerze, czyli walidacja modelu nie dodaje nowego rekordu Hire mimo zmodyfikowania wartości rekordu w tabeli Book. Co oznacza, że muszę raczej zrezygnować z walidacji tego typu i wymyślić coś innego.
0
  • OBEJŚCIE **

    [HttpPost]
    public ActionResult Create(Hire hire)
    {
    if (ModelState.IsValid)
    {
    var row = db.Books.SingleOrDefault(r => r.BookID == hire.BookID);
    if (!row.IsAvailable)
    {
    // prompt error view
    return View("HireError");
    }
    else
    {
    db.Hires.Add(hire);
    db.SaveChanges();
    // mark book as unavailable
    db.Database.ExecuteSqlCommand("UPDATE Book SET IsAvailable = 0 WHERE BookID = {0}", hire.BookID);
    return RedirectToAction("Index");
    }
    }

         ViewBag.ReaderID = new SelectList(db.Readers, "ReaderID", "FullName", hire.ReaderID);
         ViewBag.BookID = new SelectList(db.Books, "BookID", "Title", hire.BookID);
         return View(hire);
     }
    
</ul>

Wyrzuciłem walidację odnośnie czy książka jest wypożyczona, zrobiłem ją w kontrolerze tak jak widać wyżej. Rezultat jest taki, że nie można wypożyczyć książki wypożyczonej przez kogoś innego, więc cel osiągnięty. Jako komunikat o błędzie zwracam widok ze stroną błędu informujący o tym, że książka jest wypożyczona. Może nie jest to najpiękniejsze rozwiązanie, ale najbrzydsze chyba też nie.

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