Using DBContext to Select Fields - asp.net-mvc

I've got a couple questions regarding DBContext and achieveing my desired Results.
I can retrieve data like this
---Model
public class InvoiceModel
{
[Key]
public int? Invoice_Number { get; set; }
public decimal Amt_Total { get; set; }
public decimal Amt_Due { get; set; }
public decimal Amt_Paid { get; set; }
public List<InvoiceModel> GetInvoice()
{
using (VuittonEntities db = new VuittonEntities())
{
return (from inv in db.Invoice
select new InvoiceModel
{
Invoice_Number = inv.Invoice_,
Amt_Total = inv.AmountTotal,
Amt_Due = inv.AmountDue,
Amt_Paid = inv.AmountPaid
}).ToList();
}
}
--- Controller
public ViewResult Index()
{
var data = new InvoiceModel().GetInvoice();
return View(data);
}
This is pretty much standard LinQ and it returns my Invoice table with the 4 fields I selected. But now I want to achieve this using DB Context. So I added this new Class to my controller and called it in the view like this.
Controller
public class VuittonEntities : DbContext
{
public DbSet<InvoiceModel> Invoice { get; set; }
}
View
public ViewResult Index()
{
VuittonEntities db = new VuittonEntities();
return View(db.Invoice.ToList());
}
It returns the Entire table and I have to comment out my List'InvoiceModel' class
Assumptions:
VuittonEntities is my Connection String Name,
Invoice is the table
Vuitton is my .edmx class --
I heard that this can be achieved without having to use a Select Statement. It appears I am not linking my model class to a LinQ entity but when I add my context class, it throws errors on my inv."FieldName" columns in my List{InvoiceModel} Class.
TLDR Version:
How Can I return selected fields from DbContext Class instead of entire table
Why does adding a DBContext class raise errors on my List(ModelClass) fields

If you don't want the entire table, then don't just call ToList() on the DbSet, as that will just grab all of the table as you have seen.. You had it right when using Select, since the Select statement is meant to create a projection from one set of data into a new form (new class, model, anonymous object).

Related

Query result of a child collection of Breeze entity

I am trying to perform a query using Breeze that will return a filtered selection of child entities. I have two custom dtos defined as follows:
#region Dto Models
public class ProductDto   {
public int ProductDtoId { get; set; }
public int ProductClassId { get; set; }
public ICollection<ProductRequiredInputDto> RequiredInputs { get; set; }  
}
public class ProductRequiredInputDto
{
public int ProductRequiredInputDtoId { get; set; }
public string Product { get; set; }
public string Capacity { get; set; }
public string Electrical { get; set; }
//Navigation properties
public virtual ProductDto ProductDto { get; set; }
}
#endregion
My first query is to simply return a populated ProductDto model.
var query1a = this.entityQuery.from('ProductModel')
return this.entityManager.executeQuery(query1a) // returns a promise
.then(data => { this.product = data.results}
When I make a call to my web api controller everything works as expected as I receive a singular ProductDto model populated with a collection of ProductRequiredInputDto models. Here is a sample:
0: ProductDto__IPE_Data_DtoModels
ProductClassId: 1
ProductDtoId: 1
RequiredInputs: Array[40]
_backingStore: Object
ProductClassId: 1
ProductDtoId: 1
RequiredInputs: Array[40]
Now, what I am trying to achieve is to perform a second query on the ProductDto model that will return a filtered array of ProductRequiredDto models from the RequiredInputs property. I have looked over the Breeze examples and samples but have not been able to find a solution to this particular question.
Short answer: No I don't think you can filter on ICollection Navigation Properties from the EntityQuery.
Longer answer: You can write a custom method on the controller that uses .Include("RequiredInputs") and you can use LINQ to perform the filtering you want on the controller.
Aside: I find it peculiar that you don't have a ProductDtoID property on the ProductRequiredInputDto object.
Is it absolutely necessary to call the function that retrieves ProductDto? Because it doesn't sound logical to me. I would create a controller function:
[HttpGet]
public IQueryable<ProductRequiredInputDto> ProductRequiredInputDtos()
{
return _contextProvider.ProductRequiredInputDto;
}
And use a client side query in the lines of:
var idPredicate = breeze.Predicate.create('id', '==', yourSelectedProductDtoId);
var yourPredicate = breeze.Predicate.create('yourProductRequiredInputDtosProperty, 'yourOperator, 'yourValue');
var query = entityQuery.from('ProductRequiredInputDtos').where(idPredicate).and(yourPredicate);
Jonathan's method would also work, but then you have a specialized controller function for one type of call and those pile up quickly (unless you make them general by receiving params but that's another story). This way you can do any query on this model from your client without cluttering the controller up.

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.

MVC - Multiple models in a view

I'm using MVC (for the first time) with Entity framework, Database first
What I want to do is display data from a database in a single view. I created the database first, then I made a ADO.NET Entity Data Model based from the database that contains all the tables. I then created a Index view that was strongly typed with my Entity Data Model as model.
In my Index I have at the top
#model IEnumerable<Forum6.Models.Forum>
This allows me to get the rows from the table "Forum" from my database. If I try to add an extra model I get I get this error message when I run:
Line 1: #model IEnumerable<Forum6.Models.Forum>
Line 2: #model2 IEnumerable<Forum6.Models.Post>
Parser Error Message: Only one 'model' statement is allowed in a file.
After searching for an answer I found this: Two models in one view in ASP MVC 3
The answer was to create a ViewModel (ParentModel) that contained all the Models (Tables).
This is the ViewModel I created:
public class ViewModel
{
public IEnumerable<Forum6.Models.Forum> Forum { get; set; }
public IEnumerable<Forum6.Models.Post> Post { get; set; }
public IEnumerable<Forum6.Models.Topics> Topics { get; set; }
public IEnumerable<Forum6.Models.Users> Users { get; set; }
public IEnumerable<Forum6.Models.PrivMsg> PrivMsg { get; set; }
public IEnumerable<Forum6.Models.Permission> Permission { get; set; }
}
I edited my controller to look like this:
public class HomeController : Controller
{
// ForumDBEntities old_db = new ForumDBEntities();
ViewModel db = new ViewModel();
public ActionResult Index()
{
return View(db);
}
}
Then replaced the old Index view with a new strongly typed view that used the ViewModel as model. Which contains:
#model IEnumerable<Forum6.Models.ViewModel>
Trying to run this gives me this error:
The model item passed into the dictionary is of type 'Forum6.Models.ViewModel', but this dictionary requires a model item
of type
'System.Collections.Generic.IEnumerable`1[Forum6.Models.ViewModel]
How do I make the "ViewModel" enumarable? Or is my error elsewhere?
You'll need to change #model IEnumerable<Forum6.Models.ViewModel> to #model Forum6.Models.ViewModel as you're wrapping your IEnumerables inside a single ViewModel.
A good rule of thumb is to have a 1:1 relationship between your ViewModel and View.
This might be a good read for you: http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/ (just ignore the automapper part if you don't want to go that route)
You'll also need to put in actual data in your ViewModel since
ViewModel db = new ViewModel();
public ActionResult Index()
{
return View(db);
}
will just give your view an empty ViewModel.
One way to do it would be.
public ActionResult Index()
{
var model = new ViewModel
{
Forum = db.GetForum(),
Post = db.GetPost(),
Topic = you get the idea
};
return View(model);
}
One last thing when naming properties or variables in general you should use the plural verb when it contains a list. So your ViewModel would be.
public class ViewModel
{
public IEnumerable<Forum6.Models.Forum> Forums { get; set; }
public IEnumerable<Forum6.Models.Post> Posts { get; set; }
public IEnumerable<Forum6.Models.Topics> Topics { get; set; }
public IEnumerable<Forum6.Models.Users> Users { get; set; }
public IEnumerable<Forum6.Models.PrivMsg> PrivMsgs { get; set; }
public IEnumerable<Forum6.Models.Permission> Permissions { get; set; }
}
Change #model IEnumerable<Forum6.Models.ViewModel> to #model Forum6.Models.ViewModel as you are passing a single instance of a ViewModel class and not a collection of them.
All your collections are passed in a single instance of a view model.

Adding the results of two queries to ViewData.Model

I have the following code in my controller:
public ActionResult Details(int id)
{
var dataContext = new BuffetSuppliersDBDataContext();
var supplier = (from m in dataContext.BO_Suppliers
where m.SupplierID == id
select m).FirstOrDefault();
ViewData.Model = supplier;
return View();
}
This renders a view which contains the properties returned from the linq to sql query. What I need to do now is add another query which will return x amount of ratings for each supplier, i will then loop through the records in the view and display the ratings.
How can I push the results of my ratings query into the view along with what is already there?
Your best option would to be create a class that you can pass into your view.
public class SupplierDetail
{
public Supplier { get; set; }
public SupplierRating { get; set; }
}
public class SupplierDetailViewData
{
public IEnumerable<SupplierDetail> SupplierDetails { get; set; }
}
Then in your controller action use a join and select a new SupplierDetail class in the LINQ query. After that you can create a strongly-typed view by using the code-behind and changing it to this...
public partial class Details : ViewPage<SupplierDetailViewData>
{
}
After that, in your view -- ViewData.Model will be SupplierDetailViewData. Of course the second part is optional but it does make for better compile-time validation.

Resources