I am making Library using MVC and I nearly finished it all. Last thing i want to do is to let customers borrow books. In order to do that I want to find out how can I connect current logged user with certain book. I want user to be able to click on Borrow Book and he will have book available for him. Can you please explain me how may I connect these two?
Home Controller:
public ActionResult Borrow(string CustomerId, string Id)
{
Customer customer = customerContext.Find(CustomerId);
Book book = context.Find(Id);
if (customer != null && book != null)
{
customer.borrowedBooks.Add(book);
context.Insert(book);
context.Commit();
return RedirectToAction("Index", "Manage");
}
else
{
return HttpNotFound();
}
}
Customer class:
public class Customer : BaseEntity
{
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string County { get; set; }
public string PostCode { get; set; }
public List<Book> borrowedBooks { get; set; }
public Customer() {
}
}
Book class:
public class Book : BaseEntity
{
[StringLength(30)]
[DisplayName("Book Title")]
public String Title { get; set; }
[DisplayName("Writer First Name")]
public String WriterFirstName { get; set; }
[DisplayName("Writer Last Name")]
public String WriterLastName { get; set; }
[DisplayName("Released")]
public DateTime ReleaseDate { get; set; }
[DisplayName("Publisher")]
public String Publisher { get; set; }
[DisplayName("Number of Books")]
public int NumberOfBooks { get; set; }
public String Genre { get; set; }
public String Format { get; set; }
public String Description { get; set; }
public String Image { get; set; }
public static int Count { get; set; }
public Book()
{
}
}
IRepository:
public interface IRepository<T> where T : BaseEntity
{
IQueryable<T> Collection();
void Commit();
void Delete(string Id);
T Find(string Id);
void Insert(T t);
void Update(T t);
}
Thanks
If you are using Entity Framework , we need to config many to many relationship.
https://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
After that we can use : customer.borrowedBooks.Add(book) to add it to the many to many table.
Related
I have a question about the linq expression:
I do a IQueryable in a class/entity from my EDMX.
On each class I would like convert the potential class associated (Address, Contact) to another class (EntityLight).
When I query my class, I found in the parameters my differents class (Address and Contact).
Each one are MemberAccess when I get these with PropertyOrField and I would like convert/replace each one to an EntityLight and retrieve the data from Address and Contact in tha EntityLight on the same fields: Id, Name.
It's possible?
If it's possible how?
Thanks a lot
Sample :
public class House
{
public int Id { get; set; }
public Address Address { get; set; }
public Contact Owner { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class EntityLight
{
public int Id { get; set; }
public string Name { get; set; }
}
I have this model class:
public class MembershipSerial
{
[HiddenInput(DisplayValue=false)]
public int Id { get; set; }
[HiddenInput(DisplayValue=false)]
public string Serial { get; set; }
[Required]
[Display(Name="Membership Serial")]
public string SerialConfirmed { get; set; }
}
I am using EF code-first approach, I would like to check the value of the Serial vs SerialConfirmed and find any Serial which is equal to SerialConfirmed.
I tried below but i get a null exception and don't know how to solve this?
public ActionResult Checkout(UserDetails Details)
{
if (Details.MembershipSerial.Serial.Any().ToString() == Details.MembershipSerial.SerialConfirmed)
{
return View("UserSerial");
}
return View();
}
public class UserDetails : IdentityUser
{
public virtual DeliveryDetails DeliveryDetails { get; set; }
public virtual UserOrders UserOrders { get; set; }
public virtual MembershipSerial MembershipSerial { get; set; }
}
Edit:
public class MembershipSerial
{
[HiddenInput(DisplayValue=false)]
public int Id { get; set; }
[HiddenInput(DisplayValue=false)]
public string Serial { get; set; }
[Required]
[Display(Name="Membership Serial")]
public string SerialConfirmed { get; set; }
}
public class DeliveryDetails
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Company { get; set; }
public string Address { get; set; }
public string AddressLine2 { get; set; }
public string District { get; set; }
public string Province { get; set; }
}
public class UserOrders
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class MyDbContext : IdentityDbContext<UserDetails>
{
public MyDbContext()
: base ("EFDbContext")
{
}
public System.Data.Entity.DbSet<MembershipSerial> MembershipSerial { get; set; }
public System.Data.Entity.DbSet<DeliveryDetails> DeliveryDetails { get; set; }
public System.Data.Entity.DbSet<UserOrders> UserOrders { get; set; }
}
Any help is appreciated. Thanks in advance.
Your if statement should be like this:
if (Details.MembershipSerial.Serial == Details.MembershipSerial.SerialConfirmed)
Serial and Serial Confirmed are strings so you can just compare them
If you're trying to compare any Serial in the database with the SerialConfirmed that the user is Checking Out then you need a variable to hold a new instance of your connection string: assuming that MemberShipSerial is a table in your database...
using(var db = new ConnectionString())
{
if(db.MembershipSerial.Any(x => x.Serial.ToUpper() == Details.MembershipSerial.SerialConfirmed.ToUpper())
{
/*do something*/
}
}
if you just want to compare Serial and SerialConfirmed based on what the user typed in then use
if(Details.MembershipSerial.Serial.ToUpper() == Details.MembershipSerial.SerialConfirmed.ToUpper())
if the second option returns a null exception then you have to debug on the CheckingIn Action and see if what those properties are holding the values you are expecting
I have article and author classes.
Fetch articles like so and map entity to model:
public List<Model.Article> GetArticleList() {
using (var db = new ArticlesContext()) {
return db.Articles.Select(t => new Model.Article() {
Author = MapUserEntityToModel(db.Users.FirstOrDefault(u => u.UserID == t.UserID))
Title = t.Title,
Teaser = t.Teaser
// etc
}).ToList();
}
}
This doesn't work because LINQ can't run that function at run-time. What's the simplest and cleanest way to do the mapping?
Here are models:
namespace Model {
public class Article {
public string Title { get; set; }
public string Teaser { get; set; }
public User Author { get; set; }
public DateTime DateAdded { get; set; }
}
public class User {
public string DisplayName { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public string PasswordHash { get; set; }
}
}
Here are entities:
namespace MyProj {
public class Article {
[Key]
public int ArticleID { get; set; }
public string Title { get; set; }
public string Teaser { get; set; }
public int UserID { get; set; }
public DateTime DateAdded { get; set; }
}
public class User {
[Key]
public int UserID { get; set; }
public string DisplayName { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public string PasswordHash { get; set; }
}
public class ArticleContext : DbContext {
public ArticleContext() : base("name=conn") {
public DbSet<Article> Articles { get; set; }
public DbSet<User> Users { get; set; }
}
}
}
Before continue, map your relationship in a navigation property:
public class Article {
[Key]
public int ArticleID { get; set; }
public string Title { get; set; }
public string Teaser { get; set; }
[ForeignKey("UserID")]
public virtual User Author {get; set; } // navigation property
public int UserID { get; set; }
public DateTime DateAdded { get; set; }
}
And then just project your navigation property to his equivalent Model:
public List<Model.Article> GetArticleList() {
using (var db = new ArticlesContext()) {
return db.Articles.Select(t => new Model.Article() {
Author = new Model.User {
DisplayName = t.User.DisplayName,
Email = t.User.Email,
Website = t.User.Website,
PasswordHash = t.User.PasswordHash
},
Title = t.Title,
Teaser = t.Teaser
// etc
}).ToList();
}
}
You don't need to do anything, just return db.Articles directly:
using Model;
public List<Article> GetArticleList() {
using(var db = new ArticlesContext()) {
return db.Articles.ToList();
}
}
Assuming your EF model is set-up correctly with Foreign Keys, your Article type will have a lazily-evaluated Author property which will return a User object when accessed.
I have two entities Users and Addresses. Addresses is an ICollection property on the Users entities. However i am not able to access the individual addresses inside the ICollection. Is that possible and i just have the wrong syntax or does entity framework not allow that.
The Code:
Users:
public partial class User
{
public User()
{
Addresses = new HashSet<Address>();
Comments = new HashSet<Comment>();
Orders = new HashSet<Order>();
Posts = new HashSet<Post>();
}
public long Id { get; set; }
public bool Active { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime Created { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? Updated { get; set; }
public string Username { get; set; }
public long? UserTypeId { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual UserType UserType { get; set; }
}
Addresses:
public partial class Address
{
public long Id { get; set; }
public bool Active { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public DateTime Created { get; set; }
public string State { get; set; }
public DateTime? Updated { get; set; }
public long? UserId { get; set; }
public string Zip { get; set; }
public virtual User User { get; set; }
}
Repository (Parent):
public class Repository<T> where T : class
{
private DemoContext _context;
protected DbSet<T> DbSet;
public Repository(DemoContext context)
{
_context =context;
DbSet = _context.Set<T>();
}
public virtual T Get(int Id)
{
// TODO: Implement with return of DbSet.Find();
// DbSet.Find(Id);
return null;
}
public virtual List<T> GetAll()
{
return DbSet.ToList();
}
public virtual void Add(T entity)
{
DbSet.Add(entity);
}
public virtual void Update(T user)
{
_context.Entry<T>(user)
.State = EntityState.Modified;
}
public virtual void SaveChanges()
{
_context.SaveChanges();
}
public virtual void Delete(int Id)
{
// TODO: Implement with return of DbSet.Find();
// DbSet.Remove(Dbset.Find(Id));
}
UserRepository:
public class UserRepository : Repository<User>
{
public UserRepository(DemoContext context)
: base(context)
{
}
public override User Get(int Id)
{
return DbSet
.Where(o => o.Id == Id)
.Include(o => o.Orders)
.Include(o => o.Addresses)
.ToList()
.Single();
}
public override List<User> GetAll()
{
return DbSet
.Include(o => o.Orders)
.Include(o => o.Addresses)
.ToList();
}
public override void Delete(int Id)
{
DbSet.Remove(Get(Id));
}
}
The following code will not give me access to the first address in the ICollection property on the user entity
[HttpGet("[action]")]
public IActionResult Create()
{
var user = userRepository.Get(1);
var order = new Order
{
Address = user.Addresses[0].Address,
City = user.Addresses[0].City,
State = user.Addresses[0].State,
Zip = user.Addresses[0].Zip,
User = user,
SubTotal = 100,
Tax = 25,
Total = 125
};
orderRepository.Add(order);
orderRepository.SaveChanges();
return RedirectToAction("Index");
}
Please advise on how to correct my code so i have access to the entities in the collection.
Just in case anyone was wondering how i solved this here is the answer:
As mentiond by COLD TOLD ICollection does not support array like indexing. However Lists do so change it to a List instead of an ICollection and deleted the hashset that was created in the custructor
public partial class User
{
public User()
{
}
public long Id { get; set; }
public bool Active { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime Created { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? Updated { get; set; }
public string Username { get; set; }
public long? UserTypeId { get; set; }
public virtual List<Address> Addresses { get; set; }
public virtual List<Comment> Comments { get; set; }
public virtual List<Order> Orders { get; set; }
public virtual List<Post> Posts { get; set; }
public virtual UserType UserType { get; set; }
}
This allows me to access those nested entities as like this:
[HttpGet("/")]
public IActionResult Create()
{
var user = userRepository.Get(1);
var order = new Order
{
Address = user.Addresses[0].Address1,
City = user.Addresses[0].City,
State = user.Addresses[0].State,
Zip = user.Addresses[0].Zip,
User = user,
SubTotal = 100,
Tax = 25,
Total = 125
};
orderRepository.Add(order);
orderRepository.SaveChanges();
return RedirectToAction("Index");
}
I do not think it a good idea to have that many includes in your select it might cause problems in your performance , you might try to change the order of the query
return DbSet.Include(o => o.Orders)
.Include(o => o.Addresses)
.Where(o => o.Id == Id)
.FirstOrDefault();
My News.cs class has a one to many relationship with Comment.cs as defined below
public class News
{
public int NewsId { get; set; }
[Display(Name = "Title")]
public string Title { get; set; }
[Display(Name = "Details")]
public string Details { get; set; }
public DateTime DateCreated { get; set; }
public int AppUserId { get; set; }
[ForeignKey("AppUserId")]
public virtual AppUser AppUser { get; set; }
public ICollection<Comment> Comment { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
public string CommentText { get; set; }
public DateTime DateCreated { get; set; }
public int AppUserId { get; set; }
public int? NewsId { get; set; }
[ForeignKey("AppUserId")]
public virtual AppUser AppUser { get; set; }
[ForeignKey("NewsId")]
public virtual News News { get; set; }
}
I have a controller action where i am trying to fetch one News item alongside all its comments so i set up two viewModels like this
public class CommentVM
{
public string CommentText { get; set; }
public DateTime DateCreated { get; set; }
public string Author { get; set; }
}
public class NewsCommentsVM
{
[Display(Name = "Title")]
public string Title { get; set; }
[Display(Name = "Details")]
public string Details { get; set; }
public DateTime DateCreated { get; set; }
public string Author { get; set; }
public List<CommentVM> Comments { get; set; }
}
In my Controller action i have
public ActionResult Details(int? id)
{
UOW _unit = new UOW();
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
News news = _unit.NewsRepository.GetByID(id);
if (news == null)
{
return HttpNotFound();
}
var model = new NewsCommentsVM()
{
Title = news.Title,
Details = news.Details,
DateCreated = news.DateCreated,
Author = news.AppUser.FirstName
Comments = news.Comment.Select(c => new CommentVM()
{
CommentText = c.CommentText,
Author = c.AppUser.Email,
DateCreated = c.DateCreated
}).ToList()
};
return View(result);
}
The problem is that the debugger is showing that Comment is returning Null whereas in the database there are related comments to that particular news item so i'm getting the error
Value cannot be null. Parameter: source
I've been able to use this code in another project without issues.
I think the problem is because you need to change the Comments collection property as virtual. If you want that related entities be lazy loaded, you need to follow this requirements:
public class News
{
//...
public virtual ICollection<Comment> Comment { get; set; }
}
Now,If you have disabled lazy loading, another option could be using the Include extension method in your query when you need to find a particular news:
int id=3;
var specificNews=context.News.Include(n=>n.Comment).FirstOrDefault(n=>n.Id==id);
This way the related entity will be included in the query result