I'd like to reuse the method that hydrates a view model from an Entity Framework 6 IQueryable<TEntity>. Most intuitively to me, that would look something like this:
ViewModel ToViewModel(Record record) {
return new ViewModel {
Title = record.Title
}
}
// Get a single ViewModel
ViewModel GetRecord(int id) {
return ToViewModel(Context.Records.Find(id));
}
// Get multiple ViewModels
IEnumerable<ViewModel> GetRecords() {
return
from record in Context.Records
select ToViewModel(record);
}
Unfortunately EF tries to send the ToViewModel() method to the database, so enumerating the query result causes an Exception similar to "this method cannot be translated into a store expression".
Typically I'd prefer not to load the entire Entity<Record> (and all related objects referenced in the initializer) over the wire for performance reasons, otherwise I could do the following:
IEnumerable<ViewModel> GetRecords() {
return
from record in Context.Records.ToList()
select ToViewModel(record);
}
I feel like I'm overlooking something fairly simple with Expression typing. Thoughts?
Yes, you think correctly that you should use Expression. Prepare the method, and a new helper, like this:
public static Expression<Func<Record, ViewModel>> GetToViewModelExpression() {
return r => new ViewModel {
Title = r.Title
};
}
public static ViewModel ToViewModel(Record record) {
return GetToViewModelExpression().Compile()(record);
}
And use it in your dependent methods like this:
// Get a single ViewModel
ViewModel GetRecord(int id) {
return ToViewModel(Context.Records.Find(id));
}
// Get multiple ViewModels
IEnumerable<ViewModel> GetRecords() {
return Context
.Records
.Select(GetToViewModelExpression());
}
Related
I think about migrating from .net core mvc to razor pages so I am building demo application where I try the features from mvc i used and i stucked a little when I am trying to reload some part of the page based on ajax request using partial view. Sometimes partial view is very simple, like in the following example and sometimes very complex (it can contains additional nested partial views with forms etc. and suprisingly its working well).
My CustomersModel : PageModel handler looks like
it has return JsonResult because i need feedback about errors
sometimes I return more than one partial view
public JsonResult OnGetCustomerDetailPartialView(int id)
{
PopulateCustomers();
var model = new PartialViews.CustomerDetailViewModel()
{
Customer = Customers.Where(x => x.Id == id).FirstOrDefault()
};
var partialView = PartialViewHelper.PartialView("/PartialViews/CustomerDetailViewModel.cs", model, ViewData, TempData);
return new JsonResult(new { success = true, html = partialView.ToStringExtension() });
}
Partial View helper
public static class PartialViewHelper
{
public static PartialViewResult PartialView(string viewName, object model,
ViewDataDictionary viewData, ITempDataDictionary tempData)
{
viewData.Model = model; <-- this line throws error
return new PartialViewResult()
{
ViewName = viewName,
ViewData = viewData,
TempData = tempData
};
}
}
and the problem here is that i got an error
System.InvalidOperationException: 'The model item passed into the
ViewDataDictionary is of type
'RazorPages.PartialViews.CustomerDetailViewModel', but this
ViewDataDictionary instance requires a model item of type
'RazorPages.Pages.CustomersModel'.'
So the ViewData are bind to the CustomerModel, it is possible to return partial view specific ViewModel ?
The bottom line question is, should I approach Razor Pages as an replacement for the MVC or they are intended for less complicated projects than MVC ?
In response to the technical issue, try this version of your method:
public static class PartialViewHelper
{
public static PartialViewResult PartialView<T>(string viewName, object model, ViewDataDictionary viewData, ITempDataDictionary tempData)
{
return new PartialViewResult()
{
ViewName = viewName,
ViewData = new ViewDataDictionary<T>(viewData, model),
TempData = tempData
};
}
}
Then call it as follows (although the name of the partial view doesn't look right to me):
var partialView = PartialViewHelper.PartialView<PartialViews.CustomerDetailViewModel>("/PartialViews/CustomerDetailViewModel.cs", model, ViewData, TempData);
And in reply to the bottom line question, Razor Pages builds on MVC. Anything that you can do with MVC, you can also do with Razor Pages. It is intended to replace MVC for server-side generation of HTML. You can build as complicated an application as you like with it. But your code will likely be a lot simpler than an equivalent MVC application, which is a good thing, right?
i have an application that is flexible, that the user can:
filter by any field
sort by any multiple of fields.
and because it will run in ASP.Net Site + some Xamarin C# Apps, i will also have paging in it.
For network performance, it will send projection on the required fields that will be shown.
So if i include in each "Service" method, a parameter "UQueryConstraints", that can send filter expression + oderBy expression + page numbers + Projection of the fields, to be used by the Repository, which will apply it to the DBContext, is this is going to be considered a Data leak to the domain services or not?
as seen in this Pic:
http://1drv.ms/1Ngi3Kn
e.g.:
notice:
"UQueryConstraints", it will not leak any "IQueryable".
The "AmbientDbContextLocator", from:
<http://mehdi.me/ambient-dbcontext-in-ef6/>
<https://github.com/mehdime/DbContextScope>
public class UIView
{
public static void Display()
{
object constraintsB = new UQueryConstraints<Car>().Filter(x => x.carNo <= 6).SortBy(x => x.eName).Page(1, 5);
//.Projection( field1, field2, field3)
Debug.WriteLine("---------------test CarModel -------------------");
CarModel carModel1 = new CarModel();
carModel1.printCars(constraintsB);
}
}
public class CarModel
{
private CarService _carService = new CarService();
void printCars(UQueryConstraints<Car> constraints)
{
foreach ( c in _carService.getCarsList("", constraints)) {
Debug.WriteLine("Reading from converted back: aName =" + c.aName + ", eName = " + c.eName);
}
}
}
public class CarService
{
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object dbContextScopeFactory = new DbContextScopeFactory();
object ambientDbContextLocator = new AmbientDbContextLocator();
using (dbContextScope == dbContextScopeFactory.Create()) {
//after creating the Scope:
//1. create the repository
//2. call repository functions
object carRep = new CarRepository(ambientDbContextLocator);
return carRep.getCarsList("", constraints);
}
}
}
public class CarRepository : URepositoryFramwork.URepository
{
public CarRepository(IAmbientDbContextLocator contextLocator)
{
base.New(contextLocator);
}
public IList<Car> getCarsList(string Text, UQueryConstraints<Car> constraints)
{
object query = this.DataSet.Where(constraints.FilterExpression);
//.Select(constraints._projection2)
IList<Car> items;
if (constraints == null) {
items = query.ToList();
} else {
items = constraints.ApplyTo(query).ToList();
}
return items;
}
}
Regards.
Here are few points.
You don't need UQueryConstraints at all and you don't need to do any filtering in the UI at all.
I'd ague that the model is something that needs to be returned from the service so I wouldn't create CarModel in the UI layer and then pushed values to it, it doesn't make sense to me.
I'd have a method on the service that request some data and then returns it in some shape or form to the UI.
I'd inject the service to UIView.
I don't understand why there's so much noise around the context and why do you create it in getCarsList it seems like getCarList should be a class called RequestCars and both the repository and the service should be removed in favor of something like depicted in the command pattern.
I don't like the whole abstraction here at all, seems like over engineering to me and who says that IQueryable should be abstracted? it's like abstracting language/framework features whereas you should abstract domain features and only when necessary.
Abstracting 3rd-party frameworks can be fine to some extent but this isn't one of these cases.
I'm using MVVM Light Toolkit and in my View, I have a function that takes a screenshot and returns byte array of that screenshot. Since taking an screenshot (using UIElements) is related to view not ViewModel.
byte[] TakeScreenShot(Canvas sourceUiElement)
I need to get the return value of the function in my ViewModel but I can't come up with a proper way of doing it.
I other hand if I wanted to do move this function to my ViewModel, I need to have access to that element in view but without referencing the View in my ViewModel (maybe as argument or something to a Command?)
Since this question is tagged as MvvmLight, then here is an MvvmLight Toolkit answer. Use said toolkit's Messenger class. Simply define the following message classes somewhere in your application:
public class TakeScreenshotMessage : MessageBase { }
public class ScreenshotTakenMessage : GenericMessage<byte[]>
{
public ScreenshotTakenMessage (byte[]content) : base(content) { }
public ScreenshotTakenMessage (object sender, byte[]content) : base(sender, content) { }
public ScreenshotTakenMessage (object sender, object target, byte[]content) : base(sender, target, content) { }
}
In your code-behind's constructor, register for the TakeScreenshotMessage like this:
Messenger.Default.Register<TakeScreenshotMessage>(this, (msg) =>
{
byte[] bytes = this.TakeScreenShot(someCanvas);
Messenger.Default.Send(new ScreenshotTakenMessage(bytes));
});
And in your view model, register for the ScreenshotTakenMessage like this:
Messenger.Default.Register<ScreenshotTakenMessage>(this, (msg) =>
{
byte[] bytes = msg.Content.
//do something with your bytes
});
Now you can take a screen shot at any time simply by calling the following from anywhere in your application (i.e. view models, views, helper classes, etc.):
Messenger.Default.Send(new TakeScreenshotMessage());
I would bind the TakeScreenShot with the click event of the button or something in the code behind, have a property on the ViewModel called Snapshot for example, and in the click event, you get the byte[] array, assign it to the ViewModel's property Snapshot, like so in the code behind.
public void ButtonOnClick(object sender, EventArgs e)
{
var myViewModel = this.DataContext;
myViewModel.Snapshot = this.TakeScreenShot(someCanvas);
}
Depends on how strict you are with MVVM, and some might disagree, I think it is perfectly valid for your view to reference your viewmodel, aka you must know the context you are binding to anyways, but not the other way around. It is like manually binding to me.
In my API, I'd like to have routes like GET /api/v1/widgets/1,2,3 and GET /api/v1/widgets/best-widget,major-widget,bob-the-widget
public class WidgetsController : MyApiController
{
public ActionResult Show(IEnumerable<int> ids)
{
}
public ActionResult Show(IEnumerable<string> names)
{
}
}
I've got routes set up to get me to the action, but I can't figure out how to turn 1,2,3 into new List<int>(){1, 2, 3} and so on. Of course, I could just take a string and parse it in my action, but I'd like to avoid going that route.
One thing that came to mind was to put something in the OnActionExecuting method, but then I wasn't sure exactly what to put in there (I could hack something together, obviously, but I'm trying to write something reusable.)
The main questions I have are how to know whether I need to do anything at all (sometimes the ValueProviders upstream will have figured everything out), and how to handle figuring out the type to cast to (e.g., how do I know that in this case I need to go to a collection of ints, or a collection of strings, and then how do I do that?)
By the way, I had the idea of implementing a ValueProvider as well, but got lost on similar questions.
I can't figure out how to turn 1,2,3 into new List(){1, 2, 3} and so on.
To avoid polluting each controller action that needs to receive this parameter I would recommend a custom model binder:
public class IdsModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = base.BindModel(controllerContext, bindingContext);
var ids = bindingContext.ValueProvider.GetValue("ids");
if (ids != null)
{
return ids.AttemptedValue
.Split(',')
.Select(id => int.Parse(id))
.ToList();
}
return result;
}
}
and then register the model binder in Application_Start:
ModelBinders.Binders.Add(typeof(IEnumerable<int>), new IdsModelBinder());
and finally your controller action might look like this (and from what I can see in your question it already does look like this :-)):
public ActionResult Show(IEnumerable<int> ids)
{
...
}
and the custom model binder will take care for parsing the ids route token to the corresponding IEnumerable<int> value.
You could do the same with the IEnumerable<string> where you would simply remove the .Select clause in the corresponding model binder.
if your URL was
/api/v1/widgets/Show?names=best-widget&names=major-widget&names=bob-the-widget
This would bind neatly by itself :)
No need to override modelbinders in this case.
The querystring-variable names will bind to your Show-method_
public ActionResult Show(IEnumerable<string> names)
Hope this helps!
I'm relatively new to ASP.Net MVC and so I'm not sure if there is an easier way of doing this or not, however my approach would be to do something like the following:
public class WidgetsController : MyApiController
{
public ActionResult Show(string ids)
{
List<int> parsedIds = new List<int>();
foreach (var id in ids.Split(','))
{
parsedIds.Add(int.Parse(id));
}
return Show(parsedIds);
}
private ActionResult Show(List<int> ids);
}
You might also want to add some more sophisticated error handling for cases where the IDs entered can't be parsed, but thats the general approach I would use.
If I run the following code it throws the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
public void Save(Category category)
{
using(var db = new NorthwindContext())
{
if(category.CategoryID == 0)
{
db.AddToCategorySet(category);
}
else
{
//category.RemoveTracker();
db.Attach(category);
}
db.SaveChanges();
}
}
The reason is of course that the category is sent from interface which we got from GetById method which already attached the EntityChangeTracker to the category object. I also tried to set the entity tracker to null but it did not update the category object.
protected void Btn_Update_Category_Click(object sender, EventArgs e)
{
_categoryRepository = new CategoryRepository();
int categoryId = Int32.Parse(txtCategoryId.Text);
var category = _categoryRepository.GetById(categoryId);
category.CategoryName = txtUpdateCategoryName.Text;
_categoryRepository.Save(category);
}
I'm still learning Entity Framework myself, but maybe I can help a little. When working with the Entity Framework, you need to be aware of how you're handling different contexts. It looks like you're trying to localize your context as much as possible by saying:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
...
}
}
... within your data access method. Did you do the same thing in your GetById method? If so, did you remember to detach the object you got back so that it could be attached later in a different context?
public Category GetById(int categoryId)
{
using (var db = new NorthwindContext())
{
Category category = (from c in db.Category where Category.ID == categoryId select c).First();
db.Detach(category);
}
}
That way when you call Attach it isn't trying to step on an already-attached context. Does that help?
As you pointed out in your comment, this poses a problem when you're trying to modify an item and then tell your database layer to save it, because once an item is detached from its context, it no longer keeps track of the changes that were made to it. There are a few ways I can think of to get around this problem, none of them perfect.
If your architecture supports it, you could expand the scope of your context enough that your Save method could use the same context that your GetById method uses. This helps to avoid the whole attach/detach problem entirely, but it might push your data layer a little closer to your business logic than you would like.
You can load a new instance of the item out of the new context based on its ID, set all of its properties based on the category that is passed in, and then save it. This costs two database round-trips for what should really only need one, and it isn't very maintainable.
You can dig into the context itself to mark the Category's properties as changed.
For example:
public void Save(Category category)
{
using (var db = new NorthwindContext())
{
db.Attach(category);
var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
stateEntry.SetModifiedProperty(propertyName);
}
db.SaveChanges();
}
}
This looks a little uglier, but should be more performant and maintainable overall. Plus, if you want, you could make it generic enough to throw into an extension method somewhere so you don't have to see or repeat the ugly code, but you still get the functionality out of it.