Let's assume that I have two simple classes:
public class CustomerDetails
{
[Required]
public string Address
{
get;
set;
}
}
public class Customer
{
public Customer()
{
Details = new CustomerDetails();
}
[Required]
public string Name
{
get;
set;
}
public CustomerDetails Details
{
get;
private set;
}
}
When I try to manually validate Customer class in a Console application in this way:
var customer = new Customer() { Name = "Conrad" };
var context = new ValidationContext(customer, null, null);
var results = new List<ValidationResult>();
Validator.TryValidateObject(customer, context, true);
Then -even though I chose to validate all properties of the customer instance- Validator just validates the Name property of the customer instance, but not the Address property of the Details.
Is this by design or am I missing something here? Moreover, if this is by design then is there a robust way to manually validate the full object graph decorated with validation attributes, including nested types instead of using validator for the whole object graph manually?
Please note that this is tested within a Console application and not an ASP.NET MVC application.
Kind regards.
I had almost the same problem but with the collection of nested objects. I was able to resolve it by implementing IValidatableObject on a container class. In your case it's slightly easier. Something like this:
public class Customer : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var context = new ValidationContext(this.Details, validationContext.ServiceContainer, validationContext.Items);
var results = new List<ValidationResult>();
Validator.TryValidateObject(this.Details, context, results);
return results;
}
}
Hope this helps.
Related
I created a new MVC project and I ticked the box for API.
However, I intend to create many API functions and I don't really want to fill up the main controllers folder, so, I thought this will be a good use for an area.
So, I created an area called API, but, no matter what I do, I can't seem to access any of the API pages.
Just to test, I created another controller called test on the API area, and, I can access and do everything as expected.
I came straight from MVC2/3, and then had a few years off... I'm a bit out of touch and I believe this could be related to the /App_Start/WebApiConfig.cs and /Global.asax files, but, I have tried to edit them in various ways without any luck.
Does anyone know what is happening and what I need to do to get the API features working from an area?
You don't need to create an Area for Api controllers, but your Api controllers must derive from ApiController to be picked up. Instead of creating a Area, just organize your controllers inside folders and as long as the controllers follow convention they will be picked up by the WebApi.
In your WebApiConfig.csyou'll be able to define Routes, although I recommend using Attribute Based Routing instead. Then for each "area" you could create a base api controller and stick a `[RoutePrefix("api/areaName")] attribute on the class - and then for every controller in that area, you just derive from that base controller.
Here is a quick example for a "Users" api controller. With the example below you'll be able to to use the following urls:
GET http://localhost:port/api/users
GET http://localhost:port/api/users/some-guid
POST http://localhost:port/api/users
Remember to enable attribute based routing in your WebApiConfig class.
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}
UsersApiController.cs
[RoutePrefix("api/users")]
public class UsersApiController : ApiController
{
List<User> _users = new List<User> { new User("Foo", "Bar"), new User("Bar", "Foo") };
[Route("")]
public IHttpActionResult Get()
{
var result = _users;
return Ok(result);
}
[Route("{id:guid}")]
public IHttpActionResult Get(Guid id)
{
var result = _users.FirstOrDefault(q => q.Id == id);
if (result == null)
return NotFound();
return Ok(result);
}
[Route("")]
public IHttpActionResult Post([FromBody]PostModel model)
{
// Process the input model
var user = new User(model.FirstName, model.LastName);
// Save user to database
_users.Add(user);
return Created<User>(Request.RequestUri + user.Id.ToString(), user);
}
public class PostModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class User
{
public User(string firstName, string LastName)
{
Id = Guid.NewGuid();
FirstName = firstName;
LastName = lastName;
}
public Guid Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
}
}
I'm using the new EntityFramework 4.1 with POCO objects, in conjunction with the DataAnnotation framework.
When EntityFramework needs to create a proxy class (for example, lazy loading virtual properties), all of my data annotations are lost. Is there some way that my ModelMetaData can come from the class that was proxied, instead of the proxy class?
I know that I have the choice to disable proxy creating (context.Configuration.ProxyCreationEnabled) but this seems like a poor answer. This should be something that's been solved, I would think.
Here's some example code:
public class Person
{
[Required, Display(Name = "Display That Name")]
public string DisplayName { get; set; }
}
And then in my model metadata in the view, the type is: Person_9C92B92D56F6F4F0FB1686526D585438A05323CC72E457C2152796E8866297E1 (FullName = "System.Data.Entity.DynamicProxies.Person_9C92B92D56F6F4F0FB1686526D585438A05323CC72E457C2152796E8866297E1"}), my metadata is gone, and the displayname renders out at "DisplayName" not "Display That Name".
Any suggestions?
You could make a Metadata version of your model classes.
We do that thisway... what EF generate is never touched by hand.
Let said you have a Person class:
public partial class Person
{
public int idPerson { get; set; }
public int idTenant { get; set; }
public string Name { get; set; }
}
Then you could make the Metadata class, that holds the metadata and won't be overrided:
[MetadataType(typeof(PersonMD))]
public partial class Person
{
//leave it empty
}
public partial class PersonMD
{
[Required]
public int idPerson { get; set; }
[Required]
public int idTenant { get; set; }
[Required, Display(Name = "Display That Name")]
public string Name { get; set; }
}
What we do is as follows:
Modify the T4 templates to generate partial classes for your entities.
For those entities that you wish to add annotations to, create a partial class of the same name of your entity.
Create a buddy class within this class that provides your annotation details.
Apply the attribute at the top of the partial class to specify that your buddy class is where the annotation details can be found.
See here for more details http://msdn.microsoft.com/en-us/library/ee256141.aspx
I figured out one possible solution. Not sure if there are better ones. First I wrote a new ModelMetadataProvider:
public class IgnoreProxiesDataAnnotationsModelMetadataProvider : System.Web.Mvc.DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
modelType = ObjectContext.GetObjectType(modelType);
containerType = ObjectContext.GetObjectType(containerType);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
}
And then registered it in Global.asax application start:
ModelMetadataProviders.Current = new IgnoreProxiesDataAnnotationsModelMetadataProvider();
If there's a better solution, please let me know!
While looking at MVC3 examples of models. Most people tend to use model classes to create class definitions for business objects to hold data with very little or no logic. The sole purpose of model then is to be passed around. For example:
namespace MvcMusicStore.Models
{
public class Cart
{
[Key]
public int RecordId { get; set; }
public string CartId { get; set; }
public int AlbumId { get; set; }
public int Count { get; set; }
public System.DateTime DateCreated { get; set; }
public virtual Album Album { get; set; }
}
}
Is this how models classes should typically be used in MVC? Sometimes I see logic but are very specific to manipulating data. For exmaple:
public partial class ShoppingCart
{
MusicStoreEntities storeDB = new MusicStoreEntities();
string ShoppingCartId { get; set; }
public const string CartSessionKey = "CartId";
public static ShoppingCart GetCart(HttpContextBase context)
{
var cart = new ShoppingCart();
cart.ShoppingCartId = cart.GetCartId(context);
return cart;
}
// Helper method to simplify shopping cart calls
public static ShoppingCart GetCart(Controller controller)
{
return GetCart(controller.HttpContext);
}
public void AddToCart(Album album)
{
// Get the matching cart and album instances
var cartItem = storeDB.Carts.SingleOrDefault(
c => c.CartId == ShoppingCartId
&& c.AlbumId == album.AlbumId);
if (cartItem == null)
{
// Create a new cart item if no cart item exists
cartItem = new Cart
{
AlbumId = album.AlbumId,
CartId = ShoppingCartId,
Count = 1,
DateCreated = DateTime.Now
};
storeDB.Carts.Add(cartItem);
}
else
{
// If the item does exist in the cart, then add one to the quantity
cartItem.Count++;
}
// Save changes
storeDB.SaveChanges();
}
}
What is the correct way of using the Model? In classic MVC definition, model is where the intelligence of the application should be. However looking at MVC3 samples, a lot of logic is in controller or another layer for say Data Access. What is the advantage of this?
Thanks
The short answer is it provides for separation of model definitions and data access, which are conceptually different. When you separate your Data Access to its own layer (not as part of either controllers or models) you achieve far greater De-coupling.
That said there are a lot of different ways developers are using MVC, and model as data accessors is definitely one of them - the framework even supports models based on entity framework; go straight from the database to a usable model.
There's always the "fat controller" pattern of course; that is, stick all your handling logic inside the controller. I wouldn't recommend that because it will very quickly spiral into unmaintainable code.
I consume an API that has a full object set. I don't want to have to re-create an object model in the mvc3 code just to use the DataAnnotations.
Is there a way to use this feature with out a wrapper class? The objects have their own validation rules that I can reuse, but want to use the built in mvc framework to display the messages back.
You can create a class which inherits from DataAnnotationsModelMetadataProvider, where you can consume your object set, and write out the items as DataAnnotations. A brief example:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private MyEntities _db = new MyEntities();
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
// load object
var objectSet = _db.ObjectSets.FirstOrDefault(x => x.PropertyName == propertyName);
// check attributes
if (objectSet.IsRequired)
modelMetadata.IsRequired = true;
return modelMetadata;
}
}
Then, register your provider in Global.asax.cs in the Application_Start method, as follows:
ModelMetadataProviders.Current = new MyModelMetadataProvider();
You could use a metadata class as in the following example from this link though I guess you'd have to add the MetaDataType attribute programatically...
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace MvcApplication1.Models
{
[MetadataType(typeof(MovieMetaData))]
public partial class Movie
{
}
public class MovieMetaData
{
[Required]
public object Title { get; set; }
[Required]
[StringLength(5)]
public object Director { get; set; }
[DisplayName("Date Released")]
[Required]
public object DateReleased { get; set; }
}
}
I never tried this. You can write a base class in MVC model that all API classes inherit. Implement IValidatableObject on that base class and use your existing validation rules. Using MetadataType, you will still have to rewrite the class with those fileds you need validation on.
I'm building an ASP.NET MVC3 website with an code first database and have the following question:
Is it possible to make an instance of MyDbContext class with an additional argument set which will be used for filtering the results of calls to mydbcontext.
I want to use this for restricting the resultset to the current user that is logged in on my asp.net mvc website.
Any directions would be great!
I don't see why that should be a problem. Something like this should work:
public class Northwind : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind : Northwind
{
public IQueryable<Products> GetFilteredProducts(string userRole)
{
return Products.Where(product => product.UserRole == userRole);
}
}
Update
To make it impossible for your MyDbContext to be abused, you could put all your database code and models into a separate project/assembly. Then make your DbContext an internal class (instead of public), then create a public class (FilteredDbContext) that wraps your MyDbContext and exposes methods that allow you to only grab the data your allowed to see. Then in your main assembly (your web project), you will only be able to use FilteredDbContext.
So, for example:
internal class Northwind : DbContext // note: internal class
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
public class FilteredNorthwind // note: does not inherit from `Northwind`
{
private readonly _dbContext = new Northwind();
public IQueryable<Products> GetProducts(string userRole)
{
return _dbContext.Products.Where(product => product.UserRole == userRole);
}
}
If Northwind and FilteredNorthwind are in a separate assembly from your web app, you can instantiate only FilteredNorthwind from your web app.
Update 2
If you use a ViewModel, then your web app can't get back to the list of all products for a category because you extract out only the properties you need (and only the properties the user is allowed to see).
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public IEnumerable<Products> GetProducts(string userRole)
{
return _dbContext.Products
.Where(product => product.UserRole == userRole)
.Select(product => new ProductViewModel
{
Id = product.Id,
Name = product.Name,
Price = product.Price
};
}
You could make a layer above and hide the generated one and create a your own DbContext which derives from the generated MyDbContext. Just a wild guess but it seems logical to me and so you can implement your own argument set and still use the generated one.
I would do this:
public interface IUserContext {
string User { get; set; }
}
public class Database : DbContext {
public IDbSet<Product> Products { get; set; }
}
public class AuthorizedDatabase {
private readonly Database _database;
private readonly IUserContext _userContext;
public AuthorizedDatabase(Database database, IUserContext userContext) {
_database = database;
_userContext = userContext;
}
private bool Authorize<TEntity>(TEntity entity) {
// Some code here to look at the entity and the _userContext and decide if it should be accessible.
}
public IQueryable<Product> Products {
get {
return _database.Products.Where(Authorize);
}
}
}
This would allow me to cleanly abstract the actual logic around the authorization (and your IUserContext interface can be as simple or complex as required to suite your exact needs.)
To ensure that the user is unable is circumvert this protection using a navigation property (Product.Category.Products, for example.) you might need to turn off lazy loading and explicitly load the required related entities.
Have a look at this post from ADO.NET Team Blog for ideas: loading related entities