what does string GenerateStronglyTypedClass(ExpandoObject object) look like?
Often when I'm creating MVC views, I first bind my view to an ExpandoObject
so I can quickly figure out the shape of my viewmodel, without having to spend time adding/modifying/removing real properties in a strongly-typed viewmodel.
But once I have a pretty good feel for what my viewmodel should look like, I prefer to create an actual class for it. It'd be great if I could automatically generate the viewmodel from the ExpandoObject. Is that possible?
public virtual 开发者_StackOverflow社区ActionResult DoStuff(int stuffId)
{
// eventually this stuff gets moved to a service class
dynamic vm = new ExpandoObject();
using (var ctx = new MyContext())
{
vm.Stuff = ctx.Stuff
.Where(x => x.Id == stuffId)
.Select(x => new
{
Foo = x.Foo,
Bar = x.Bar
}).SingleOrDefault().ToExpando();
}
// once I know what vm looks like, I'll want to do this to easily create a new class
// string stronglyType = GenerateStronglyTypeClass(vm);
return View(vm);
}
public string GenerateStronglyTypedClass(ExpandoObject object)
{
// what goes here??
}
I first bind my view to an ExpandoObject so I can quickly figure out the shape of my viewmodel, without having to spend time adding/modifying/removing real properties in a strongly-typed viewmodel.
Well, what you are doing is very bad. Sorry to say it but you are loosing strong typing. I can't believe people actually find it so hard to use Ctrl+A while on the Models
folder in their project, type some name ending with ViewModel
and then type a couple of prop+Tab+Tab and define a real strongly typed view model.
Instead people try to use some expandos, dynamics, ViewDatas, ViewBags, CrapBags, ...
So here's what I would suggest you: use real view models and forget about those week typing or you will continue struggling with ASP.NET MVC. Believe me. It's how it is :-)
And when you use AutoMapper to automatically bind between your domain models and view models you will have more time to drink beer, like me:
public ActionResult DoStuff(int stuffId)
{
using (var ctx = new MyContext())
{
var model = ctx.Stuff.Where(x => x.Id == stuffId);
var vm = Mapper.Map<IEnumerable<MyDomainModel>, IEnumerable<MyViewModel>>(model);
return View(vm);
}
}
See how much more fun this is?
Obviously if you ever want to have any chance to unit test your controller actions you should be using repositories instead of hardcoding dome Linq-To-I-Don't-Know-What contexts in your controllers, like this:
public ActionResult DoStuff(int stuffId)
{
var model = _stuffRepository.GetStuffs(stuffId);
var vm = Mapper.Map<IEnumerable<MyDomainModel>, IEnumerable<MyViewModel>>(model);
return View(vm);
}
Is it so hard?
You could even write a custom action automapper attribute your action might even look like this:
[AutoMap(typeof(IEnumerable<MyDomainModel>), typeof(IEnumerable<MyViewModel>))]
public ActionResult DoStuff(int stuffId)
{
var model = _stuffRepository.GetStuffs(stuffId);
return View(model);
}
OK, we are getting shorter, thinner, lighter. Exactly how controller actions should be. On a diet.
Any type of generated class at runtime (with say emit), won't give much of a benefit because if you don't know the type at compile type your are still going to be calling dynamic and expando will actually beat poco class in my tests. If your generate class is going to create a c# file that could work but you'll have to include the file and replace the code, recompile, kind of eh, but could work.
The opensource framework has ImpromptuInterface has a builder syntax that lets you assign properties or create objects graphs in the same manner no matter what the type is (expando or poco) with just a couple type changes.
dynamic New = Builder.New<ExpandoObject>();
var person = New.Person(
FirstName: "George",
LastName: "Washington"
);
and then later in your development process you could define your static type
public class Person{
public string FirstName{get;set;}
public string LastName{get;set;}
}
dynamic New = Builder.New<Person>();
Person person = New.Person( //we are static typing after this point
FirstName: "George",
LastName: "Washington"
);
It also has some other features like wrapping an static interface at runtime too that might be of use to you.
精彩评论