asp.net Detail Model logic - asp.net-mvc

I've got a programm logic problem..
Ok, let's start.
I have 2 classes in a model.
namespace Korbball.Models
{
public class Clubs
{
public int id { get; set; }
public string name { get; set; }
public string state { get; set; }
public string description { get; set; }
}
public class ClubDetails : Clubs
{
public string website { get; set; }
public string banner { get; set; }
}
}
In a first step, i will call an overview of all Clubs with the entries of the Club Class.
If i click on a Club, there is a link to -> Website/Clubs/Details/Id
Now, i will show on this view all the data from Clubs and ClubDetails.
The Data for this is stored on a database.
private static List<Clubs> clubs = new List<Clubs>();
KorbballEntities db = new KorbballEntities();
public KorbballRepository()
{
var clubsdata = db.Korbball_Clubs.ToArray();
foreach (var c in clubsdata)
{
clubs.Add(new Clubs { id = c.ClubId, name = c.Name, state = c.State });
}
}
So i get the Data for ONLY the club information.
For Details, should i generate a new LIST with all the informations?
Hope anybody know what i mean...can't declare it much better..
thanks!
Update:
Ok, i think the problem is, that i cant find someone who can teach me the mvc logic =D
I have many clubs. This clubs has basic informations (name,id,state)
This clubs have also detailed informations like description,banner,website etc.
I made a ModelClass
ClubModel.cs
public class Clubs
{
public int id { get; set; }
public string name { get; set; }
public string state { get; set; }
public string description { get; set; }
}
public class ClubDetails : Clubs
{
public string website { get; set; }
public string banner { get; set; }
}
I will show a list of all Clubs with the basic informations with site/Clubs
If i go to the detailed view, Clubs/Details/Id i will show all the information(Basic + detailed)
Your solution seems to be cool. I cant find any ideas on the internet how to build the "best practice" method on such a situation.

Is the relationship between Club and Details one-to-one? It sounds like it based on how you described everything but it doesn't make as much sense because like someone mentioned you inherited from Club, so you could just load Club_Details and you'll automatically get Club columns.
If your relationship were one-to-many the easiest way is to build the association into your model (foreign key relationship on the database) and then use the "Include" to tell Entity Framework that you want associated entities. I put this together quickly so syntax may not be perfect the basic idea is to look into the "Include".
This logic is flawed because it is loading many clubs and then loading club details, you'd want to fix that.
private static List<Clubs> clubs = new List<Clubs>();
private List<Club_Details> clubDetails = new List<Club_Details>();
// make a view model class that contains club and club details (one to many)
private ClubData clubdata = new ClubData();
public KorbballRepository()
{
var clubsdata = db.Clubs.Include("Club_Details").ToArray();
foreach (var c in clubsdata)
{
clubs.Add(new Clubs { id = c.ClubId, name = c.Name, state = c.State });
/* sample of how to reference the Included entities use the association name
of Club_Details if it were a one to many relationship, ideally you
would create a view model or something with the two classes defined
and one club and list of club details, then define a list of the view
model for all Clubs */
clubDetails = new List<Club_Details>();
foreach (var d in c.Club_Details)
{
clubDetails.Add(new Club_Details {
website = d.website.ToString(),
banner = d.banner.ToString()
});;
}
/* load the data to the view model class and loop again */
}
}

Related

Entity Framework - Modeling multiple roles for same entity

I have a contact class
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
I have two classes inheriting from Contact:
[Table("Employees")]
public class Employee : Contact
{
public decimal Salary { get; set; }
}
[Table("Suppliers")]
public class Supplier : Contact
{
public string TIN { get; set; }
}
I am using Entity Framework 6.3 as my ORM.
Can I model something where the same contact can be both Employee and
Supplier with the same unique Id.
Employee emp = new Employee();
emp.Id = "C1";
emp.Name = "Employees";
emp.Salary = 10000;
emp.TrackingState = TrackingState.Added;
Supplier sup = new Supplier();
sup.Id = "C1";
sup.Name = "Employees";
sup.TIN = "ABC";
sup.TrackingState = TrackingState.Added;
When I do:
context.Employee.Add(emp);
context.Supplier.Add(sup);
context.Save();
Obviously, it will not allow me to add the record.
I get an error:
"Violation of PRIMARY KEY constraint 'PK_dbo.Contacts'. Cannot insert duplicate key in object 'dbo.Contacts'. The duplicate key value is (C1).\r\nThe statement has been terminated."
Can I make the Supplier also share the same Id as that of employee and
enable insert / update of employee and supplier?
Thanks
I assume you are not doing any specific inheritance mapping which by default will map this as Table per Hierarchy (TPH) inheritance. With this mapping you get a single Db table that contains the aggregate of all the fields in the base type and the dependent types with a discriminator field to know which type the row should be materialized. This allows for doing a query where you ask for a type.
With TPH each row can only be a single type, so you can not have the same row be both an Employee and Supplier.
There is another type of mapping you can do called Table Per Type (TPT) which will create a table for each type, so in your case 3, one for the shared fields, and one for each dependent type. This should allow for what you are asking about. (YMMV)
However, it seems to be that Employee and Supplier would be used in much different domains so I would suggest that you create your Contact table and relate it to both your employee and supplier.
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
[Table("Employees")]
public class Employee
{
public string Id { get; set; }
public string ContactId { get; set; }
public decimal Salary { get; set; }
public Contact Contact { get; set; }
}
[Table("Suppliers")]
public class Supplier
{
public string Id { get; set; }
public string ContactId { get; set; }
public string TIN { get; set; }
public Contact Contact { get; set; }
}
Now you can query for an employee:
db.Employees.Include(e => e.Contact).First();
Or for a Supplier:
db.Employees.Include(e => e.Contact).First();
Which might be cleaner than the inheritence query you would need:
db.Contacts.OfType<Employee>().First();
In both the Has A modeling I show above and the Is A Modeling with TPT you are getting three tables. You just have the FK in what I show rather than the same ID in 3 tables with TPT.
There is also Table Per Class you can look at, which is similar to TPT but you don't get a table for the abstract/parent class, rather you get table for each dependent type with all the fields in it. I don't think this is what you want because it would have duplicate data, however, it does mean less joins.

How to implement ViewModels for asp.net MVC 5?

I am new to asp.net, there are some questions on stack overflow but they don't fulfill my purpose. My question is..
How would I implement view model for the following two models?
public class model1
{
int student-id{ get;set}
string student-name{get; set;}
}
public class model2
{
int course-code{get; set;}
string course-name{get; set;}
}
Now I want to write a view model that could pass to a view and this view displays student-name and corresponding course-names.
Note: a student can enrolled in more than one course.
First of all you should modify your model. Student and courses have to be related. You can implement these relations like:
public class Student
{
public int Id { get; set }
public string Name { get; set; }
public ICollection<Course> Courses { get; set; }
}
public class Course
{
public int Code { get; set; }
public string Name{ get; set; }
public ICollection<Student> Students { get; set; }
}
After - you create view model. View model class must contain only what you actually need in you view. In you case - student and courses names. You can consider several options here. If you want just to display all course names in one line you can build you StudentViewModel like this:
public class StudentViewModel
{
public string Name { get; set; }
// In this case you can just join all courses' names to one string using string.Join(", ")
public string Courses { get; set; }
}
... or like this - if you want courses' names separated (to use them in some select or list html element). But you can create JoinedCources property which will return courses' names joined into one string.
public class StudentViewModel
{
public string Name { get; set; }
public ICollection<string> Courses { get; set; }
public string JoinedCources {
get {
return string.Join(", ", Courses);
}
}
}
Note: this is view model for only one student! If you want to display view which shows you the list of students and their courses you should either create new view model with property which is collection of StudentViewModel or in your view define model like #model ICollection<StudentViewModel> instead of #model StudentViewModel.
Now you have to map your model to view model. For example in your controller action when you get your student from database (or any other data source - file or web service):
public ActionResult StudentDetails(int studentId)
{
var student = _dataSource.GetStudent(studentId);
var model = AutoMapper.Mapper.Map<StudentViewModel>(student);
return View(model);
}
Now few words about mapping. AutoMapper is external class library you should definitely get to learn about if you want to work with view models and mapping in the future. It will help you simplify action method code and make it more readable. But since you're new to ASP. Net you can implement mapping by your self for the first time. For example like below:
public ActionResult StudentDetails(int studentId)
{
var student = _dataSource.GetStudent(studentId);
var model = new StudentViewModel()
{
Name = student.Name,
Courses = student.Courses.Select(c => c.Name)
}
return View(model);
}
public class StudentViewModel
{
public string Name{get;set;}
public int StudentId{ get;set}
public List<model2> Courses
}
You can consider combining the two models into the above view model if the goal is to display a student with course info.

Entity framework inserts new record to navigation table inappropriately

I'm facing an issue when inserting records with Many to Many navigation property,
I have these models
//ModelBase
public class ModelBase
{
[Key]
public long Id { get; set; }
}
//Book
public class Book : ModelBase
{
public string Title { get; set; }
public virtual ICollection<Author> Authors { get; set; }
public virtual ICollection<PublishedBook> PublishedBooks { get; set; }
}
//Author
public class Author : ModelBase
{
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
And DTOs called BookDto and AuthorDto for transfer data between layers
From the controller I fill data to DTOs and call Create method (which is in separate layer) to Save data to database,
BookDto bookDto = new BookDto()
{
Id = model.Id,
Title = model.Title,
Authors = model.AuthorIds.Select(c => new AuthorDto { Id = c }).ToList()
}
using (ServiceFactory facory = new ServiceFactory())
{
factory.Book.Create(bookDto);
}
In the Create method I map DTOs with POCOs using ValueInjector
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
bookRepository.Add(book);
unitOfWork.Commit();
}
And it calls Add method in Genaric Repository Base
public virtual void Add(T entity)
{
dbset.Add(entity);
}
This inserts data to Books table and BookAuthors tables appropriately BUT inserts new record into the Authors table even if I pass Authors which has existing AuthorIds for Book.Authors from the controller.
Any Idea how to fix this?
You are missing to attach the existing authors to the context. You could do it like so:
public void Create(BookDto bookDTO)
{
Book book = new Book();
book.InjectFrom<DeepCloneInjection>(bookDTO);
foreach (var author in book.Authors)
authorRepository.Attach(author);
bookRepository.Add(book);
unitOfWork.Commit();
}
where the generic Attach methods is just implemented calling dbset.Attach(entity);. I am assuming that all repositories (authorRepository and bookRepository) share the same context instance. Otherwise the above won't work.
The fact that the AuthorId already exists in the database doesn't matter. EF doesn't check if the Id exists by a query first. If you call Add on an object graph of detached entities it tries to insert the whole graph by generating INSERT statements for the parent and for all children. By attaching the children you effectively turn off the INSERT statements for those.

Remove() doesn't work with many-to-many relationship in Entity Framework

I am trying to remove an object from a collection in entity framework, but unfortunately my code is failing. I would be grateful if you could have a look and let me know if you can figure out what I'm doing wrong. My objects are as follows:
Person <-> Badge (many-to-many relationship)
Badge <-> BadgeRequirement (one-to-many relationship)
Person contains an ICollection of Badges
Badge contains an ICollection of Person
BadgeRequirement contains a Badge Foreign Key
Adding and editing entries works absolutely fine.
However, when I try to remove a Badge from a Person using the code below, it doesn't work:
Postback event handler on example.aspx
****The person object has been loaded as part of the load event on the page****
Badge badge = BadgeHelper.getBadge(badgeID);
if (command == "Delete")
{
PersonHelper.removeBadgeFromPerson(badge, person);
}
Delete method on PersonHelper class (wrapper for all processing)
person.Badges.Remove(badge);
DbContext.SaveChanges();
The Remove(badge) returns false and I cannot profile this as I am using SQL Compact 4.0
Thanks in advance for your help!
This was actually resolved in one of the MSDN forums. The full details can be found on the link here
However, as a summary, to use the Remove() method, both collections in the many to many relationship need to be loaded before any changes take place. The code sample is attached below:
class Program {
static void Main(string[] args)
{
using (var context= new MyContext())
{
var post1 = context.Posts.Find(3);
var tag1 = context.Tags.Find(2);
context.Entry(post1).Collection("Tags").Load();
post1.Tags.Remove(tag1);
context.SaveChanges();
}
}
}
public class Post
{
public int PostId { get; set; }
public string PostContext { get; set; }
public ICollection<Tag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string TagContext { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class MyContext:DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
}
I hope that this helps somebody else with similar issues.
Had the same issue, I ended up just executing a raw SQL command against the join table:
DbContext.Database.ExecuteSqlCommand("DELETE FROM [dbo].[Badges_Persons] WHERE Badge_Id=5000 AND Person_Id=1000");
DbContext.SaveChanges();

How do I display data from multiple tables in a single MVC view

I am having a hard time solving the following with an MVC view.
My goal is to display data from multiple tables in a single MVC view. The bulk of the data comes from a table called Retailers. I also have another table called RetailerCategories which stores the retailerid from the Retailers table and also a categoryid linking to a Category table.
Note that there are multiple records for each retailerid in the RetailerCategories table.
In the view I want to show a list of retailers and with each retailer I want to show the list of categories applicable to them.
What would be the best way to accomplish this? Some of the things I have tried are covered in Can you help with this MVC ViewModel issue?
This however does not appear to be the right approach.
You need a view model specifically tailored to the needs of this view. When defining your view models you shouldn't be thinking in terms of tables. SQL tables have absolutely no meaning in a view. Think in terms of what information you need to show and define your view models accordingly. Then you could use AutoMapper to convert between your real models and the view model you have defined.
So forget about all you said about tables and focus on the following sentence:
In the view I want to show a list of
retailers and with each retailer I
want to show the list of categories
applicable to them.
This sentence is actually very good as it explains exactly what you need. So once you know what you need go ahead and modelize it:
public class CategoryViewModel
{
public string Name { get; set; }
}
public class RetailerViewModel
{
public IEnumerable<CategoryViewModel> Categories { get; set; }
}
Now you strongly type your view to IEnumerable<RetailerViewModel>. From here it is easy-peasy to do what you want in the view:
showing a list of retailers with each retail having a list of associated categories.
this could be also helpful;
video from chris pels
It is simple just do what I say step by step.
add connection string into web.config file
select models from solution explorer and add 4 classes as following
1st class for first table "i have employ table which have 3 columns
public class Employ
{
[Key]
public int Emp_id { get; set; }
public string Emp_name { get; set; }
public string Emp_city { get; set; }
}
2nd class for my tempo table
public class tempo
{
[Key]
public int ID { get; set; }
public int Emp_Id { get; set; }
public string subject { get; set; }
public string hobby { get; set; }
}
Now I create a third class in model folder which contain value that i want from employ table and tempo table
public class Alladd
{
public int ID { get; set; }
public int Emp_Id { get; set; }
public string subject { get; set; }
public string hobby { get; set; }
public string Emp_name { get; set; }
public string Emp_city { get; set; }
}
and the final class is datacontext class
public class DataContext:DbContext
{
public DataContext() : base("DefaultConn")//connection string
{
}
public DbSet<Employ> Empdata { get; set; }
public DbSet<tempo> Tempdata { get; set; }
}
now go to the Home controller and add code as below
public ActionResult file()
{
// IList<tempo> tempi=new List<tempo>();
IEnumerable<Alladd> model = null;
// model = getVerifydetails(id);
// return View(objcpModel);
List<Alladd> verify = new List<Alladd>();
cn.Open();
if (cn.State == ConnectionState.Open)
{
string query = "select Employ.Emp_name,Employ.Emp_id,Employ.Emp_city,tempo.hobby,tempo.id,tempo.subject from Employ inner join tempo on Employ.Emp_id=tempo.Emp_id;";//joining two table
SqlCommand cmd=new SqlCommand(query,cn);
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
verify.Add(new Alladd { Emp_name = dr[0].ToString(), Emp_Id= Convert.ToInt32(dr[1].ToString()), Emp_city = dr[2].ToString(), hobby = dr[3].ToString(),ID = Convert.ToInt32(dr[1].ToString()),subject= dr[4].ToString()});//filling values into Alladd class
}
cn.Close();
}
return View(verify);
}
now the final step is so simple
go to solution explorer
select views folder and left click on it and select add view
now name it as "file" which we give it into controller
check on create strongly type view
select model class from dropdown-> Alladd
select scaffold templet ->List
hit Add button
Now you're done
Happy coding...

Resources