Asp.Net Mvc templated helpers with interface types - asp.net-mvc-2

I would like to use the Asp.net MVC templated helpers functionality to generate a standard UI for my objects throughout my application, but I've run into a significant issue:
I do not directly pass class types from my controllers into their views. Instead, I pass interface types.. with the actual implementation of the Model wrapped up in a Mongo or NHibernate specific class in an indirectly referenced project.
For discussion, my objects look like:
public interface IProductRepository {
IProduct GetByName(string name);
}
public interface IProduct {
string Name { get; set; }
}
public class NHibernateProductRepository : IProductRepository {
public IProduct GetByName(string name) {
/* NHibernate Magic here */
return nhibernateFoundProduct;
}
}
public class NHibernateProduct : IProduct {
public virtual Name { get; set; }
}
public class ProductController : Controller {
public ProductController(IProductRepository productRepo) {
_ProductRepo = productRepo;
}
public ActionResult Index(string name) {
IProduct product = _ProductRepo.GetByName(name);
return View(product);
}
}
Is it possible to use interface types with the Editor.For() syntax? Are there any problems or sticking points that I need to be aware of?
I have an EditorTemplate\IProduct.ascx file available. At this time, I can't seem to get that template to be rendered without hardcoding the "IProduct" name into the Editor.For() call. I would prefer this type of 'Convention over Configuration'....

The templates helpers will use the runtime type of the object for the name. In this case you should name the file NHibernateProduct.ascx
If you don't know the name of the type at design time than you could write a helper method that would inspect the object instance and walk the list of interfaces that a particular type is implementing and return a name based on that. Then you would call the appropriate override to EditorFor that takes the string "templateName" parameter.

I have decided to use an approach with a ViewModel native to the Web project that implements the IProduct interface.

Related

Entity Framework Set with Generic Class

Ok, I might be punching above my pay grade here, but I'm trying to create a generic CRUD routine for and EF project. I've got most of it working but I'm flailing around on one point.
Normally you do something like this to add an entity through a context-
DBContext.MyClass.Add( p ); // p = instance of MyClass
That works fine, but since in a global method to handle all adds regardless of what class they are I'm passing in a Model as an object it would look more like this-
DBContext<whateverobject>.Add(whateverobject); // my objects is an object param passed into the method
I've tried doing a bunch of typeofs and there where T : class stuff but I'm having no luck. Any pointing in the right direction would help me out.
I'm using EF Core 2 so my options might also be more limited than EF 6.
Thanks.
The method you're looking for is DbContext's Set<T>()
Your generic repository for your generic CRUD would look something like this:
public class Repo<T> where T: class
{
private readonly DbSet<T> _set;
public Repo(DbContext dbContext)
{
_set = dbContext.Set<T>();
}
public void Add(T entity) => _set.Add(entity);
}
This example includes a maybe unusual thing:
where T: class: we have to specify that T has to be a reference type because DbSet<T> expects T to be a reference type
For generic querying you might want to use extension methods.
In order to implement a ById method you'd have to specify that the type T must have an Id property using an interface. That would look something like this:
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
}
public static class DbSetExtensions
{
public static T ById<T>(this DbSet<T> dbSet, int id) where T: class =>
dbSet.FirstOrDefault(entity => entity.Id == id);
}

Persist derived objects using Mongo C# driver

I have the following class hierarchy
[BsonKnownTypes(typeof(MoveCommand))]
public abstract class Command : ICommand
{
public abstract string Name
{
get;
}
public abstract ICommandResult Execute();
}
public class MoveCommand : Command
{
public MoveCommand()
{
this.Id = ObjectId.GenerateNewId().ToString();
}
[BsonId]
public string Id { get; set; }
public override string Name
{
get { return "Move Command"; }
}
public override ICommandResult Execute()
{
return new CommandResult { Status = ExecutionStatus.InProgress };
}
}
if I save the command like so:
Command c = new MoveCommand();
MongoDataBaseInstance.GetCollection<Command>("Commands").Save(c);
and then query the DB, I don't see the derived properties persisted.
{ "_id" : "4df43312c4c2ac12a8f987e4", "_t" : "MoveCommand" }
I would expect a Name property as a key in the document.
What am I doing wrong?
Also, is there a way to avoid having a BsonKnowTypes attribute on the base class for persisting derived instances? I don't see the why a base class needs to know about derived classes. This is bad OO design and is being forced on my class hierarchy by the BSON library. Am I missing something here?
1.Name property was not saved into database because it haven't setter. Serializers not serialize properties that's haven't setters (because if serializer serialize such property it will not able deserialize it back). So if you want serialize Name property then just add fake setter(into ICommand need to add it also):
public override string Name
{
get { return "Move Command"; }
set{}
}
2.If you don't want use BsonKnownTypes attribute there is another way to notify serializer about know types it might encounter during deserialization. Just Register maps once, on app start event:
BsonClassMap.RegisterClassMap<MoveCommand>();
//all other inherited from ICommand classes need register here also
So you should use or KnownTypes attribute or register BsonClassMap for each polymorphic class, otherwise you will get 'unknown descriminator' error during deserializtion:
var commands = col.FindAllAs<ICommand>().ToList();
3 You said:
This is bad OO design and is being
forced on my class hierarchy by the
BSON library.
In any way even without KnownTypes atribute your code using Bson lib through BsonId attribute.
If you want avoid it you can:
BsonClassMap.RegisterClassMap<MoveCommand>(cm => {
cm.AutoMap();
cm.SetIdMember(cm.GetMemberMap(c => c.Id));
});
So now you can remove reference to Mongodb.Bson lib from your domain code lib.

MVC2, Entity Framework, & repository pattern

I'm trying to get a repository pattern working with MVC2 and EF.
My problem is within the concrete repository. When I attempt to cast the EF query results as an IEnumerable collection of view-model entities:
Unable to cast object of type
'System.Data.Objects.ObjectQuery`1[Data_Service.MediaReleases]'
to type
'System.Collections.Generic.IEnumerable`1[TestMVCWithFacory.Models.Entities.MediaReleaseModel]'.
I sense that's a bone-headed thing to try to do -- and it's something with Linq, and how deferred execution works, but I don't really understand the voodoo.
So what is it that I'm mis-understanding there, and how do I address it?
The view-model:
public class MediaReleaseModel
{
public string Headline { get; set; }
public string FullText { get; set; }
}
The repository interface:
public interface IMediaReleasesRepository
{
IEnumerable<MediaReleaseModel> MediaReleases { get;}
}
The concrete repository:
public class MediaReleaseRepository : IMediaReleasesRepository
{
private NewsEntities DataContext = new NewsEntities();
private IEnumerable<MediaReleases> _MRs;
public MediaReleaseRepository()
{
_MRs = from art in DataContext.MediaReleases select art;
}
public IEnumerable<MediaReleaseModel> MediaReleases
{
get { return (IEnumerable<MediaReleaseModel>)_MRs; }
}
}
Controller:
public class HomeController : Controller
{
private IMediaReleasesRepository _MRRepository;
public HomeController()
{
_MRRepository= new MediaReleaseRepository();
}
public ViewResult index()
{
return View(_MRRepository.MediaReleases.ToList());
}
}
You're trying to cast collection of MediaReleases to collection of MediaReleaseModels. If MediaReleaseModel is a separate class, this can't be done just by casting. Generally, cast will succeed only in one inheritance chain or when conversion operators are defined, which is not the case here.
What you need here is rewriting the MediaRelease fields to you model object (it can be automated using tools like AutoMapper), i.e. with help of LINQ:
public IEnumerable<MediaReleaseModel> MediaReleases
{
get
{
return _MRs.Select(x => new MediaReleaseModel()
{
Prop1 = x.Prop1
/* etc. */
});
}
}
One suggestion at the side: it's better not to have logic like that in constructor, creating objects should be cheap operation and it's a bit strange when the data are fetched before they are really needed.

DataAnnotations MetadataType Class Ignores Base Class Properties

I've run into a bit of a wall in trying to use the .NET DataAnnotations feature to provide simple validations in a derived class. I am marking up my class with the standard annotations included in .NET 4 (from the System.ComponentModel.DataAnnotations namespace), then using the MS Enterprise Library v5 Validation Block to process the rules.
I have a number of objects derived from a common base class, which contains properties common to all of my objects. For validation purposes, I may have different rules for the various classes derived from this class.
Here's a simplified example:
public abstract class PersonBase
{
public int Id { get; set; }
public string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
public class Customer : PersonBase
{
}
[MetadataType(typeof(ManagerMD))]
public class Manager : PersonBase
{
}
public class CustomerMD
{
[Required]
[StringLength(20, ErrorMessage="Customer names may not be longer than 20 characters.")]
public object Name { get; set; }
}
public class ManagerMD
{
[Required]
[StringLength(30, ErrorMessage = "Manager names may not be longer than 30 characters.")]
public object Name { get; set; }
}
// calling code
var invalidCustomer = new Customer {Id=1, Name=string.Empty};
var valFactory = EnterpriseLibraryContainer.Current.GetInstance<ValidatorFactory>();
var customerValidator = valFactory.CreateValidator<Customer>();
var validationResults = customerValidator.Validate(invalidCustomer);
// validationResults.IsValid should equal False, but actually equals True.
I have found that I can get the expected validation results if I push the annotations down to the base class, but then I lose the ability to fulfill different requirements for different types. Also, if I put class-specific properties on a derived class and provide metadata for these properties, I get results, but only for these properties, not the properties from the base class.
I haven't yet tried using the EntLib provided validation attributes; I'd prefer to keep the library this lives in free of dependencies from outside the core framework, if at all possible.
Am I missing something, or am I just out of luck here?
I think I have a workable solution for this.
It appears that the Metadata class will not provide validation of properties belonging to the superclass of the target object. In order to get Metadata to work with this, I needed to mark the superclass properties as virtual, then provide overrides for the properties that I wanted to validate.
Example (see question above for original example):
public abstract class PersonBase
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
[MetadataType(typeof(CustomerMD))]
partial class Customer : PersonBase
{
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
}
With the override in place, the validator works as expected. It's a little more work, but it will get the job done.
I also tried adding annotations to the base class as fallback default rules; this allows me to have a base set of rules and override them as needed on a case by case basis. Looking good.
I run into the same issue and couldn't make it annotate a base class with Attributes using MethadataType. Like Scroll Lock I did the overriding part for base class virtual properties. On top of it I made "shadowing" for the none virtual properties.
public class BaseClass
{
public virtual int Id {get;set;}
public string Name {get;set;}
}
public class DerivedClass
{
[SomeAttribute]
public ovveride int Id {get{ return base.Id;} set{ base.Id = value;}}
[SomeAttribute]
public new string Name {get{ return base.Name;} set{ base.Name = value;}}
}

Does MEF Support Customized CTOR?

It looks like that MEF framework creates objects which have default CTOR. How about customized CTOR, or Constructor with parameters? For example:
[Export (typeof(IInterface1))]
public class MyClass : IInterface1
{
public MyClass(int id) {....}
....
}
If not, one way I can think is to pass object as parameters to CTOR. For example:
public Interface IParameterID {
public int Id { get; private set; }
...
}
Then the CTOR will be:
public MyClass([Import(typeof(IParameter))] IParameterID id)
{ ... }
Not sure if it is possible to add attribute to CTOR's parameters? And the next question is that if MEF will automatically create an instance of IParameter and inject it to the CTOR's parameter?
Yes, this is possible. Just put an [ImportingConstructorAttribute] on the constructor you would like to use. The parameters will automatically be treated as imports, but if you need to change the contract name on them you can also put an import attribute on them.