I have a Silverlight 4 app using EF & WCF RIA Services with a SQL DB. I have a Tasks table that I want to display in a grid or listbox and I want to do a custom grouping. The custom grouping would be Overdue, today, tomrrow, next 7 days and future.
If I understand the concepts of MVVM correctly, I should be creating a custom property for my Tasks object in the TasksViewModel. But I am not sure how to do this.
I have the Tasks entity that is automatically created in the entity data model and I have a GetTasks method in the DomainService that I call in my viewmodel.
Any help will be greatly appreciated.
You should have access to those types from the client. You can create a list of the Task entity on your ViewModel that you can bind to.
private List<Task> _tasks;
public List<Task> Tasks
{
get { return _tasks; }
set {
_tasks = value;
NotifyPropertyChanged("Tasks");
}
}
Or you can create a client side poco to map to if you don't want to bind directly to entities.
The following has nothing to do with the "gouping", since it doesn't seem to be your problem.
What I would do is have a property of ObservableCollection<Task>:
public ObservableCollection<Task> Tasks {get; private set;}
which I initialize in the constructor, ask the domain context to load the tasks, and then fill the collection with the data coming in the callback like so:
private TasksDomainContext context;
public TasksViewModel()
{
Tasks=new ObservableCollection<Task>();
context= new TasksDomainContext();
LoadTasks();
}
void LoadTasks()
{
context.Load(
context.GetTasksQuery(),
callback =>
{
if(callback.HasError)
{
//handle error
}
else
{
Tasks.Clear();
foreach(var task in callback.Entities)
Tasks.Add(task);
}
},
null);
}
Hope this helps ;)
P.S.: if you're having an issue with grouping, please give more details
Related
In a Sitecore project I've integrated Simple Injector using this article
It uses sitecore pipelines and then uses a method in App_start
namespace BBC.App_Start
{
public class SimpleInjector : IPackage
{
public void RegisterServices(Container container)
{
GetContainer.RegisterServices(container);
container.Register(() => new SitecoreContext(), Lifestyle.Scoped);
container.Register(() => new Container(), Lifestyle.Singleton);
}
}
}
Simply I can inject container into controller constructor but can't have container in View files.
I tried to declare a static property in App-start and save container to it. but still I'm getting no registration type in Views
What is the best way to have container object in views?
As Stephen suggests in his comment, the literal answer to your question is "you shouldn't do that - because it's not really the way MVC and DI are supposed to work". The more detailed answer goes something like this:
The job of your view is to present data that it has been passed via the Model. Views should not really contain logic. Very simple stuff like "if flag is false, hide this block of mark-up" is ok, but the more complex code to work out what the value of the flag is shouldn't be in the view.
MVC tries to make our website code better by encouraging you to separate presentation (the View) from data (the Model) and logic (the Controller). This should make our code easier to work with - So if you have processing that needs doing, then it should really be happening when your controller method runs.
If your view requires some special data, best practice suggests it should work it out in the controller method and pass it to the view in the model. The code might look more like this:
public class MyModel
{
public string SpecialData { get; set; }
}
public class MyController : Controller
{
public ActionResult DoSomething()
{
// do whatever processing is needed
var somethingCalculate = resultFromYourOtherObject();
// do other stuff
var model = new MyModel() { SpecialData = somethingCalculated };
return View(model);
}
}
And then the View just needs to accept the MyModel class as its model, and render the SpecialData property - no logic required.
I think also it's considered a bad idea to have calls to fetch objects from your DI container spread about your codebase. For MVC apps, generally your DI container gets wired in to the process of creating a controller for a request when the app starts up. Rather than passing about a DI Container into your controllers, the DI framework extends the Controller-creation process, and the container isn't exposed outside of this. When the MVC runtime needs to create a controller, the controller-creation logic uses the DI framework to fetch objects for all the controller's dependencies.
Without more detail about what you actually want to achieve, it's difficult to say what the "right" approach to creating your object(s) here is, but the two most common patterns are probably:
1) Constructor injection: Your controller has a parameter which accepts the object required. The DI container creates this object for you at the point where it creates the controller, so your controller gets all its dependencies when it is created. Good for: scenarios where you know how to create the object at the beginning of the request.
public interface IMySpecialObject
{
string DoSomething();
}
public class MyController : Controller
{
private IMySpecialObject _specialObject;
public MyController(IMySpecialObject specialObject)
{
_specialObject = specialObject;
}
public ActionResult RenderAView()
{
// do some stuff
var data = _specialObject.DoSomething();
return View(data);
}
}
As long as IMySpecialObject and a concrete implementation for it are registered with your DI container when your app starts up, all is well.
2) Factory classes: Sometimes, however, the object in question might be optional, or it might require data that's not available at controller-creation time to create it. In that case, your DI framework could pass in a Factory object to your controller, and this is used to do the construction of the special object later.
public interface ISpecialFactory
{
ISpecialObject CreateSpecialObject(object data);
}
public class MyController : Controller
{
private IMySpecialFactory _specialFactory;
public MyController(IMySpecialFactory specialFactory)
{
_specialFactory = specialFactory;
}
public ActionResult RenderAView()
{
// do some stuff
if( requireSpecialObject )
{
var data = getSomeData();
var specialObject = _specialFactory.CreateSpecialObject(data);
var data = _specialObject.DoSomething();
return View(data);
}
return View("someOtherView");
}
}
But a good book on using DI may suggest other approaches that fit your specific problem better.
I want to extend Linq's DataContext class to implement the ORM. Currently my model looks like this:
public class Trial : DataContext
{
public Trial(string connectionString) : base(connectionString) { }
[Column(DbType = "System.Guid", IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
public Guid TrialID { get; set; }
//...
}
However when I try to instantiate a new Trial object to insert it into the database I get an error complaining that Trial does not have a constructor that takes 0 arguments. When I try to create such a constructor, VS complains that DataContext does not have a constructor that takes 0 arguments.
Am I missing something here? How do I seperate the data context from the model definition?
(First time using Linq!)
Thanks in advance,
Max.
Your data context that represents the database view should inherit from DataContext. It should expose Tables where T is the entities (rows) that you want to add. Try generating a model from the database using the designer or SQLMetal and take a closer look at the generated code to see what's going on.
Looking for some guidance in designing my new MVC 4 app.
I would like to have a url parameter s=2011 on every page of the app to let me know what year of data I'm working with. Obviously, the user will have a way to change that parameter as needed.
I will need that parameter in every controller and wondering the best way to do this. I was thinking of creating a base controller that reads Request.QueryString and puts the year into a public property. However, considering all the extensability points in MVC, I'm wondering if there's a better way to do this?
This very much depends on the design of your app, but just to give you two alternatives
IActionFilter
If you are doing data context per request you can use a global IActionFilter to hook pre-action execution globally and apply a query filter to your data context behind the scenes.
Major down-side of this is that to test the controller you will need to have the full MVC pipeline setup so that the actionfilter gets applied properly.
Dependency Injection
Instead of using sub-classing (base controller as you say) you can use dependency injection . Keeping things more loose will allow you to pull the filter from query string, cookie, user setting in the database or whatever else - without your controller knowing where it comes from.
Here is some pseudo code how I would do it if I was using something like Entity Framework or Nhibernate (also I am sure applicable with other technologies as well)
public Car
{
public string Year { get; set; }
}
public class CarsDataContext : DbContext
{
private IQuerable<Cars> _cars = null;
private Func<Car, bool> _carsFilter = null;
public IQuerable<Car> Cars {
get {
if (_carsFitler != null)
return _cars.Where(_carsFitler);
return _cars;
}
set { _cars = value; }
}
public void ApplyCarsFilter(Func<Car, bool> predicate)
{
_carsFilter = predicate;
}
}
Assuming you have dependency injection setup already (NInject or whichever other framework) in you can configure how the context to be intialized
Bind<CarsDataContext>().ToMethod(() => {
string yearFilter = GetYearFilter(); // can be coming from anywhere
CarsDataContext dataContext = new CarsDataContext();
dataContext.Applyfilter(car => car.Year == yearFilter);
return dataContext;
}).InRequestScope();
Then my controller knows nothing about the data filtering and I can easily test it:
class MyController : Controller
{
public MyController(CarsDataContext dataContext)
{
}
...
}
However I would only do this is filtering the dataset was across many controllers and important part of my software. Otherwise it's pure over-engineering.
I'm new to WCF. My web project has an ADO.NET Entity Data Model (aka EF edmx), which has the Entity Container Name JobSystemEntities.
I've created a simple oData WCF data service which uses JobSystemEntities, and it works great:
public class JobService : DataService<JobSystemEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Jobs", EntitySetRights.ReadSingle);
}
However, this exposes all of the properties on the Job. I would like to hide sensitive data, i.e. the Cost field/property/column of the Job table.
I am posting this a but late, but it might help others.
You can use the IgnoreProperties attribute http://msdn.microsoft.com/en-us/library/system.data.services.ignorepropertiesattribute.aspx on your class.
You will have to define a partial Job class in order to do this. Something in the lines of:
namespace DAL.Entities
{
[IgnoreProperties("Cost")]
public partial class Job
{
}
}
I've done something similar to this. A good starting point is found here:
http://weblogs.asp.net/rajbk/archive/2010/05/15/pre-filtering-and-shaping-odata-feeds-using-wcf-data-services-and-the-entity-framework-part-1.aspx
Basically you will need to separate the protected properties of an entity into a separate entity that is linked as a property of the other. Once that is done user a Query Interceptor to restrict when that protected entity can be viewed.
[QueryInterceptor("YourObjectsProtectedProperties")]
public Expression<Func<YourObjectsProtectedProperties, bool>> OnReadYourObjectsProtectedProperties()
{
if (ShowEntityToUser())
return o => true == true;
return o => true == false;
}
I've just been toying around with the new WCF RIA Services Beta for Silverlight this evening. So far it looks nice, but I've come across a few barriers when trying to retrieve data and exposing it to the UI via binding.
First of all, how am I able to get a single integer or string value from my service? Say if I have this method on my domainservice:
public int CountEmployees()
{
return this.ObjectContext.Employees.Count();
}
How am I able to make a call to this and bind the result to, say, a TextBlock?
Also, is there any way to make a custom layout for binding data? I feel a little "limited" to ListBox, DataGrid and such. How is it possible to, i.e., make a Grid with a stackpanel inside and have some TextBlocks showing the bound data? If it's possible at all with WCF RIA Services :)
Thanks a lot in advance.
To do custom methods you can use the Invoke attribute.
In the server side you declare in a domain service like this
[EnableClientAccess]
public class EmployeesService : DomainService
{
[Invoke]
public int CountEmployees()
{
return this.ObjectContext.Employees.Count();
}
}
And in your Client-side you can use it like this
EmployeesContext context = new EmployeesContext();
InvokeOperation<int> invokeOp = context.CountEmployees(OnInvokeCompleted, null);
private void OnInvokeCompleted(InvokeOperation<int> invOp)
{
if (invOp.HasError)
{
MessageBox.Show(string.Format("Method Failed: {0}", invOp.Error.Message));
invOp.MarkErrorAsHandled();
}
else
{
result = invokeOp.Value;
}
}
For the second question, you are not limited with binding. The object you get from your context can be binded with any elements you want.
You can name your class with schema classname.shared.cs and this code will also available in silverlight application.
Using Silverlight/WPF databinding engine you can build any fancy layout using datagrid / listbox containers and regular controls like textbox/label and apply your own style/skin - Example.
EDIT
Shared code cannot contain any database-related functions, only some plain calculations. If you want to retrieve this value from server then you need to make WCF method call.
At serverside you create DomainService implementation:
[EnableClientAccess()]
public class HelloWorld : DomainService
{
public string SayHello()
{
return "Test";
}
}
Then you can use this at client:
HelloWorld context = new HelloWorld();
context.SayHello(x => context_SayHelloCompleted(x), null);
void context_SayHelloCompleted(System.Windows.Ria.InvokeOperation<string> op)
{
HelloTextBlock.Text = op.Value;
}
All dirty work with making HelloWorld class available at Silverlight client is done by Visual Studio. Check hidden generated code folder.
[Invoke] attribute is obsolete in newest release of RIA services.