How to convert ObjectSet from Entities to IEnumerable of Models? - asp.net-mvc-3

I am building a test application using MVC3, Razor, and Entity Framework 4.1 with a schema-first approach (as apposed to a code-first approach), in a repository pattern. I would like to avoid accessing data objects in my view, and access a model instead, but I am having a problem. As far as I can tell, the data objects are being returned from the data layer as ObjectSet, but my View needs IEnumerable, and I don't know how to cast one to the other.
Here is some code, to help clarify.
Model ...
namespace TestSolution.Models
{
public class ProjectModel
{
[HiddenInput]
public int Id { get; set; }
[Required]
[StringLength(255, ErrorMessage = "The name cannot be more than 255 characters long.")]
[Display(Name = "Name")]
public string Name { get; set; }
[Required]
[Display(Name = "Description")]
public string Description { get; set; }
}
}
Repository ...
public IQueryable<ProjectModel> GetProjects()
{
return Db.Project;
}
Entities ...
public ObjectSet<Project> Project
{
get
{
if ((_Project == null))
{
_Project = base.CreateObjectSet<Project>("Project");
}
return _Project;
}
}
Controller ...
public ActionResult Index()
{
IEnumerable<TestSolution.Models.ProjectModel> model = _projectRepository.GetProjects();
return View(model);
}
View ...
#model IEnumerable<TestSolution.Models.ProjectModel>
Error I am getting when building ...
Cannot implicitly convert type 'System.Data.Objects.ObjectSet<TestSolution.Project>' to 'System.Linq.IQueryable<TestSolution.Models.ProjectModel>'. An explicit conversion exists (are you missing a cast?)
Does this question make sense? I am just not sure where go from here ... any advise you guys can give me would be awesome. :)
EDIT: I was able to solve this with Kyle's suggestion by changing my Repository code to ...
public IQueryable<ProjectModel> GetProjects()
{
return Db.Project.Select(i => new ProjectModel() { Id = i.Id, Name = i.Name, Description = i.Description });
}

The problem isn't converting from ObjectSet<T> to IEnumerable<T> (ObjectSet<T> implements IEnumerable<T>).
The problem is converting from TestSolution.Project to TestSolution.Models.ProjectModel. You will need to write some conversion code, maybe something similar to the below:
model.Select(i => new ProjectModel() { /* Set properties here. */ });

Related

swagger swashbuckle does not support nested class as action method parameter

I am using asp.net 5
I have two model class, which are nested, both of the inner class are named Command
public class EditModel
{
public class Command
{
public int Id { get; set; }
public string Info { get; set; }
}
}
and
public class CreateModel
{
public class Command
{
public int Id { get; set; }
public string Info { get; set; }
}
}
In my Controller class has two methods
[HttpPost]
public IActionResult PutData(CreateModel.Command model)
{
return Ok();
}
[HttpPut]
public IActionResult PostData(EditModel.Command model)
{
return Ok();
}
Since for both Put and Post's query I am using nested class both name Command, Swagger will return the following error
An unhandled exception has occurred while executing the request.
Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Conflicting method/path combination "PUT Test" for actions -
TestSwagger.Controllers.TestController.PutData
(TestSwagger),TestSwagger.Controllers.TestController.PostData
(TestSwagger). Actions require a unique method/path combination for
Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable1 apiDescriptions, SchemaRepository schemaRepository) at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable1
apiDescriptions, SchemaRepository schemaRepository)
at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String
documentName, String host, String basePath)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext
httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
Swagger will work, if I change one of the Command model name to something different.
Yet, I believe this nested class model name is legit and should work with swagger also. If there a way to work around this. Thanks
By adding c.CustomSchemaIds(x => x.FullName);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestSwagger", Version = "v1" });
c.CustomSchemaIds(x => x.FullName);
});
solved the schemaId conflict. Thanks to this question

Web api Data Annotations cause 500 server error when passed

I am creating a web api project and want to add validation to my model, so I added a DataAnnotation attribute.
I then tested the project by trying to pass my object from a separate mvc project. I recieved a 500 server error.
Removing the DataAnnotation allows me to pass the object successfully. Why?
I have looked at a couple of tutorials such as this and this, they show how to handle validation errors, but this has not helped.
UPDATE
Removing the data annotation from MyProperty in Class1, solution B (but leaving it on the class in solution A) means values can be passed successfully! Is this a problem with deserilazing the object? If so how do I solve it?
B = My web service reciving the object
A = My mvc project sending the object
my code to send the resquest
public class Class1
{
[Required(ErrorMessage = "MyProperty value is required")]//remove this line to make it work
public int MyProperty { get; set; }
public Class2 MyOtherProperty { get; set; }
}
public class Class2
{
public string SomeProperty { get; set; }
}
async Task<string> Test2()
{
var form = new Class1();
form.MyProperty = 123;
form.Class2 = new Class2();
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:58814/api/");
var post = await client.PostAsJsonAsync<Class1>("Values", form);
var putt = await client.PutAsJsonAsync<Class1>("Values", form);
}
return "";
}
my code to recieve the request (the breakpoint applied is not being hit)
// POST api/values
public void Post([FromBody]Class1 value)
{
}
// PUT api/values/5
public void Put([FromBody]Class1 value)
{
}
I have resolve the issue thanks to the advice from this link - Scott Hanselman's blog
My sending project and receiving project had identical classes (including data annotations). Removing the annotations from the class in the receiving project resolved the binding issue but I still needed validation on the properties. Also changing the annotation from required to Range also resolved the binding issue (I only mention these as they were some steps I took to try and debug).
The final solution for me was to change this
public class Network
{
[Required(ErrorMessage = "NetworkID is required")]
public int NetworkID { get; set; }
}
to this
[DataContract]
public class Network
{
[DataMember(IsRequired = true)]
public int NetworkID { get; set; }
}

Problem passing data from repository to viewmodel

I have the following viewmodel,
public class SiteAdminCreateViewModel
{
public Customer Customer { get; private set; }
public CustomerSite CustomerSite { get; private set; }
public SelectList CustomerNames { get; private set; }
public SiteAdminCreateViewModel(CustomerSite customerSite, Customer customer)
{
CustomerSite = customerSite;
Customer = customer;
CustomerNames = new SelectList(customer.CustomerName);
}
}
And the following methods in my repository for returning a list of customers and a list of CustomerSites
public IQueryable<CustomerSite> GetCustomerSites
{
get { return context.CustomerSites; }
}
public IQueryable<Customer> GetCustomers
{
get { return context.Customers; }
}
When i instanitiate the viewmodel in my controller im wanting to return the list of customers to passs to the select list in the viewmodel.
public ViewResult Create()
{
CustomerSite customerSite = new CustomerSite();
var customer = repository.GetCustomers.ToList();
return View(new SiteAdminCreateViewModel(customerSite, customer));
}
But the return line throws the error
cannot convert from System.Collections.Generic.List' to 'CustomerOrders.Domain.Entities.Customer
I think this is because i have the customer variable defined in the Viewmodel of type Customer but im trying to pass a list of customers?
Can anyone offer any advice on where i am going wrong here?
Do i need to define both the Customer type and the CustomerNames select list type in the viewmodel, i defined the Customer Object only so i can use it to pass the Customers to the select list but im not sure if this is the best way to do this?
Any advice anyone can offer for a newbie, will be much appreciated.
Your SiteAdminCreateViewModel class' constructor is defined as follows:
public SiteAdminCreateViewModel(CustomerSite customerSite, Customer customer) {
...
}
Its second argument is of type Customer.
You're passing var customer = repository.GetCustomers.ToList() to it, whose type is List<Customer>.
When i instanitiate the viewmodel in my controller im wanting to
return the list of customers to passs to the select list in the
viewmodel.
If I understand what you're saying correctly, you're just trying to pass the customers list to build a SelectList.
First of all, you seem to be passing a string to the SelectList constructor. This would not even compile (read System.Web.Mvc.SelectList).
What you'd need to do is change SiteAdminCreateViewModel's constructor like
public SiteAdminCreateViewModel(CustomerSite customerSite, IEnumerable<Customer> customers) {
/* ... */
CustomerNames = new SelectList(customers, "CustomerId", "CustomerName");
}
CustomerId and CustomerName being properties of the Customer class.
it's simply because you are trying to pass IList into constructor of the SiteAdminCreateViewModel
var customer = repository.GetCustomers.ToList();
return View(new SiteAdminCreateViewModel(customerSite, customer));
using
var customer = repository.GetCustomers.ToList().FirstOrDefault();
return View(new SiteAdminCreateViewModel(customerSite, customer));
would work, also you may need to check if the customer is null

Implementing Unique Contraint with ValidateEntity gives "The given key was not present in the dictionary" error

While in search of trying to implement unique key validations for my db using EF CodeFirst/Mvc3 I came upon this post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx which gave an example on how to do it by using IValidateObject for my object model:
public class Category : IValidatableObject
{
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var testContext = (TestContext)validationContext.Items["Context"];
if (testContext.Categories.Any(
c => c.CategoryName == CategoryName && c.CategoryID != CategoryID))
{
yield return new ValidationResult("A category with the same name already exists!", new[] { "CategoryName" });
}
yield break;
}
}
and overriding DbEntityValidationResult ValidateEntity:
public class TestContext : DbContext
{
public DbSet<Test.Models.Category> Categories { get; set; }
protected override DbEntityValidationResult ValidateEntity( DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
}
And the action on the controller
[HttpPost]
public ActionResult Create(Category category)
{
if (ModelState.IsValid) {
categoryRepository.InsertOrUpdate(category);
categoryRepository.Save();
return RedirectToAction("Index");
} else {
return View();
}
}
But I get the error: "The given key was not present in the dictionary." for the line
var testContext = (TestContext)validationContext.Items["Context"];
It seems like Validate on the object is getting called which accesses "Context" before its set in the override ValidateEntity code.
At first I thought it could have been ModelState.Isvalid triggering validate too early but it wasn't.
Anyone know what I'm missing here or what I'm doing wrong? Thanks in advance.
Model.IsValid definitely triggers it too early and perhaps something else. IValidatableObject is global interface used by both MVC and EF but your method in DbContext is called only when you call SaveChanges on the context so any usage of IValidatableObject prior to calling SaveChanges will result in the exception. You must use another approach if you want to validate your entity this way. For example store context in HttpContext.Items - you can create custom action filter and instantiate and store the context before the operation call and dispose it after operation call - hopefully it will cover all problems.
I was facing the same problem... Then after a lot of Googling I finally found this:
Exercise 3: Using IValidatableObject Custom Validation
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
MusicStoreEntities storeDB = new MusicStoreEntities();
if (storeDB.Albums.Any(
a => a.Title.Trim().ToUpper() == this.Title.Trim().ToUpper() &&
a.ArtistId == (int)this.ArtistId))
{
yield return new ValidationResult("Existing Album", new string[] { "Title" });
}
}
As you see in their example, they instantiate a new Context and as such there's no need for validationContext.Items["Context"];. Doing so we won't get this error anymore.

ASP.NET MVC Patterns

I am fairly new to MVC, but after playing with it (MVC 3/Razor), I am hooked.
I have a few questions:
1) What is the best, or most widely used pattern to develop MVC apps in? Repository, DDD, UOW?
2) I am using the Entity Framework 4, so could some please explain to me or point me to a good source that will explain the Repository Pattern w/EF4? Doesn't EF4 take place as the business layer and the data access layer? Does the Repository Pattern even provide a benefit?
3) Also, one last question, could someone explain the whole relationship between the Controller, the Model and the View? I get the basics, but maybe a little more in depth of the correct way to use it. View Models - Say I have a view that displays customer info, and one that edits it, should I have a view model and an edit model, or can the be passed around?
4) Examples??
Thanks for the help up front,
$("Sam")
** EDIT **
Am I on the right track here:
Public Class HomeController
Inherits System.Web.Mvc.Controller
Function Index(ByVal id As Integer) As ActionResult
Return View(New HomeModel)
End Function
<HttpPost()> _
Function Index(ByVal Model As HomeModel) As ActionResult
Return View(Model)
End Function
End Class
Public Class HomeModel
Private _Repository As IRepository(Of Customer)
Public Property Customer As Customer
Public Sub New()
End Sub
Public Sub New(ByVal ID As Integer)
_Repository = New CustomerRepository
Customer = _Repository.GetByID(ID)
End Sub
End Class
Public Interface IRepository(Of T)
Function GetByID(ByVal ID As Integer) As T
Sub Add(ByVal Entity As T)
Sub Delete(ByVal Entity As T)
End Interface
Public Class CustomerRepository
Implements IRepository(Of Customer)
Public Sub Add(ByVal Entity As Customer) Implements IRepository(Of Customer).Add
End Sub
Public Sub Delete(ByVal Entity As Customer) Implements IRepository(Of Customer).Delete
End Sub
Public Function GetByID(ByVal ID As Integer) As Customer Implements IRepository(Of Customer).GetByID
Return New Customer With {.ID = ID, .FirstName = "Sam", .LastName = "Striano"}
End Function
End Class
Public Class Customer
Public Property ID As Integer
Public Property FirstName As String
Public Property LastName As String
End Class
I use generic repositories that get instantiated in a service class (using Dependency Injection with Ninject).
The service class essentially performs two functions:
It provides all the methods that the controller will consume.
It has a property called ViewModel, that essentially maps the data that the views need into a MyViewModel class.
The Controller consumes the service class. With this "pattern", your controllers look like:
namespace ES.eLearningFE.Areas.Courses.Controllers
{
public partial class CourseController : Controller
{
ICourseDisplayService service;
public CourseController(ICourseDisplayService service)
{
this.service = service;
}
public virtual ActionResult Display(int CourseId, int StepOrder, string PupilName, string TutorName)
{
service.CourseId = CourseId;
service.StepOrder = StepOrder;
service.PupilName = PupilName;
service.TutorName = TutorName;
if (Request.IsAjaxRequest())
{
return PartialView(service.ViewModel);
}
else
{
return View(service.ViewModel);
}
}
}
}
The ViewModel class only hold display data and no methods (except the odd really simple method to retrieve data from another property that is, for example a List<> object).
Works really well. An example of a service class:
namespace ES.eLearning.Domain.Services.Courses
{
public class SqlCourseDisplayService : ICourseDisplayService
{
DataContext db;
public SqlCourseDisplayService(DbDataContextFactory contextFactory)
{
db = contextFactory.Make();
CoursesRepository = new SqlRepository<Course>(db);
StepsRepository = new SqlRepository<CourseStep>(db);
StepLinksRepository = new SqlRepository<StepLink>(db);
UserCoursesRepository = new SqlRepository<UserCourse>(db);
CourseTutorsRepository = new SqlRepository<CourseTutor>(db);
UsersRepository = new SqlRepository<User>(db);
}
#region ICourseDisplayService Members
public ViewModels.CourseDisplayVM ViewModel
{
get
{
return new ViewModels.CourseDisplayVM
{
CourseId = this.CourseId,
CourseName = this.Course.Name,
Steps = this.Steps,
ActiveStepIndex = this.ActiveStepIndex,
CurrentStepIndex = this.CurrentStepIndex,
Pupil = new UserDto { UserId = this.PupilId, UserName = this.PupilName },
Tutors = this.GetTutors(this.CourseId),
Tutor = tutorName == null ? null : new UserDto { UserName = this.TutorName, UserId = this.TutorId}
};
}
}
#region Entities
int courseId;
public int CourseId
{
get
{
if (courseId == 0) throw new ApplicationException("Invalid Course Id!");
return courseId;
}
set
{
if (value == 0) throw new ApplicationException("Invalid Course Id!");
try
{
Course = (from c in CoursesRepository.Query where c.CourseId == value select c).First();
Steps = Course.CourseSteps.ToList();
courseId = value;
}
catch {throw new ApplicationException("No Course found for Course Id: " + value);}
}
}
public Data.Course Course { get; private set; }
public int StepOrder { get; set; }
public List<Data.CourseStep> Steps { get; private set; }
public int ActiveStepIndex
{
get
{
if (PupilName == null)
{
throw new ApplicationException("Pupil not set!");
}
if (CourseId == 0)
{
throw new ApplicationException("Course not set!");
}
try
{
var x = (from uc in UserCoursesRepository.Query where (uc.IdCourse == CourseId) && (uc.UserName == PupilName) select uc).First();
return x.ActiveStepIndex;
}
catch { throw new ApplicationException("Could not get Active Step!"); }
}
}
#endregion
#region Users
string tutorName;
public string TutorName
{
get
{
if (tutorName == null) throw new ApplicationException("Invalid call to get Tutor Name [Null Tutor Name]!");
return tutorName;
}
set
{
tutorName = value;
TutorId = (Guid)Membership.GetUser(tutorName).ProviderUserKey;
}
}
public Guid TutorId { get; set; }
string pupilName;
public string PupilName
{
get { return pupilName; }
set
{
pupilName = value;
PupilId = (Guid)Membership.GetUser(pupilName).ProviderUserKey;
}
}
public Guid PupilId { get; set; }
#endregion
#region Utility Properties
public int CurrentStepIndex { get; set; }
public int StepCount
{
get
{
return Steps == null ? 0 : Steps.Count();
}
}
#endregion
#region Private Utilities
private List<UserDto> GetTutors(int CourseId)
{
return (from ct in CourseTutorsRepository.Query join u in UsersRepository.Query
on ct.TutorName equals u.UserName
where (ct.CourseId == courseId)
select new UserDto { UserName = ct.TutorName, UserId = u.UserId }).ToList();
}
#endregion
#region Repositories
private IRepository<Course> CoursesRepository
{
get;
set;
}
private IRepository<CourseStep> StepsRepository
{
get;
set;
}
private IRepository<StepLink> StepLinksRepository
{
get;
set;
}
private IRepository<UserCourse> UserCoursesRepository
{
get;
set;
}
private IRepository<CourseTutor> CourseTutorsRepository
{
get;
set;
}
private IRepository<User> UsersRepository
{
get;
set;
}
#endregion
#endregion
}
}
May not be everyone's choice, but hey, it works for me... AND (more importantly) my clients and their users.
Edit
As requested in the comment below, the Repository that I use:
namespace ES.eLearning.Domain
{
public class SqlRepository<T> : IRepository<T> where T : class
{
DataContext db;
public SqlRepository(DataContext db)
{
this.db = db;
}
#region IRepository<T> Members
public IQueryable<T> Query
{
get { return db.GetTable<T>(); }
}
public List<T> FetchAll()
{
return Query.ToList();
}
public void Add(T entity)
{
db.GetTable<T>().InsertOnSubmit(entity);
}
public void Delete(T entity)
{
db.GetTable<T>().DeleteOnSubmit(entity);
}
public void Attach(T entity)
{
db.GetTable<T>().Attach(entity);
}
public void Save()
{
db.SubmitChanges();
}
#endregion
}
}
And the IRepository Interface:
namespace Wingspan.Web.Mvc
{
public interface IRepository<TEntity> where TEntity : class
{
List<TEntity> FetchAll();
IQueryable<TEntity> Query {get;}
void Add(TEntity entity);
void Delete(TEntity entity);
void Attach(TEntity entity);
void Save();
}
}
This should help you getting started. There are a lot of tutorials and videos available; for example:
Understanding Models, Views and Controllers
The ASP.NET MVC 2.0 basics and excellent introduction by Scott Hanselman. Personally one of my favorite speakers.
And also at www.asp.net; there are a few tutorials/examples to help you getting started. For example the Music Store sample
Unfortunately, I'm not so familiar with EF4/Repository pattern. But here's a blogpost about this pattern.
1) I would say that the repository pattern is the most widely used, then there is inversion of controll too.
2) I can't really point out the benefits with using a repository for entity framework other than that the controller should not know about how to acces data other then asking a repository. This makes it easy to switch it out sometime.
You can also eager load the data to make sure that the view don't call the database in every iteration of a foreach, for example a collection of users to display data from a child entity. You can probly do this anyway, but I feel that the repository is the right place to do it.
3) I can't tell you about the concept in a more in depth way, but I can tell some about viewmodels. In my opinion you should only use viewmodels if there is anything more then one entity you want to send to the view, for example a list of countries. You can alo use a viewmodel to "flatten" out very complex objects.
I would defiantly say the repository pattern is used a lot. This pattern can be used with Dependency Injection. Using Dependency Injection makes Unit Testing a breeze because you can snap different repositories to an abstract repoistory. Check out http://ninject.org/ for a simple to use Dependecy injector for .NET.
View Models should hold display data and transfer that data from the controller to the view. If you want to edit and display customer info, take a look at this

Resources