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.
Related
To implement a plug-in system in a AspNet Core Mvc app, I would like a non-generic method to add a data context from a list of assemblies loaded dynamically at runtime, taking a Type parameter like this:
foreach(Type tp in pluginContexts)
{
services.AddDbContext(tp, options => ...);
}
instead of the usual
services.AddDbContext<PluginDataContext>(options => ...);
That's because for dynamically loaded assemblies, I can not provide the TContext type parameter to the AddDbContextPool method, since that's statically compiled and not available at compile time.
Background
This is for a larger Asp.Net Core MVC app. The plugins must be able to both access the main database of the overall app and a separate database of their own.
Plugin assemblies, containing domain code and their private database context are to be dropped in a specified directory.
The main app loads the plugin assembly dynamically upon startup.
The way I am solving this now is to have each controller get the IConfiguration instance injected, obtain the appropriate connection string from the config, and the database context is instantiated in the controller. Not so nice but does work.
One can easily inject a general class into the Services collection with AddScoped<>, and then use it as a sort of ServiceLocator - however, that is considered an antipattern.
I looked into the source code for AddDbContext but honestly I am lost.
Is there any simple way to achieve this?
Solved it by creating an extensibility point in the plugin assembly.
Define an interface in the main app, which all plugins must implement.
public interface IPluginContextRegistration
{
void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction);
String GetDatabaseName();
}
Create a class implementing this interface (in the plugin). It has access to the type of its private database context, thus can use the generic AddDbContext method:
public class DatabaseRegistration : IPluginContextRegistration
{
public void RegisterContext(ref IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddDbContext<Test1DbContext>(optionsAction);
}
public String GetDatabaseName()
{
return "test-plugin-db";
}
}
Then in the main app ASP.Net Startup.cs file, add following code, which calls the RegisterContext() method for each plugin. For example, if you want to use Sql Server:
void RegisterPluginDbContexts(ref IServiceCollection services, List<Assembly> assemblyList)
{
IEnumerable<IPluginContextRegistration> registrars = new List<IPluginContextRegistration>();
foreach (Assembly assembly in assemblyList)
{
registrars = registrars.Concat(GetClassInstances<IPluginContextRegistration>(assembly));
}
foreach (var reg in registrars)
{
String name = reg.GetDatabaseName();
String connStr = Configuration.GetConnectionString(name);
reg.RegisterContext(ref services, options => options.UseSqlServer(connStr));
}
}
For completeness - the method "GetClassInstances" is just a helper method using Reflection to obtain an instance of classes implementing the specified interface.
So it's simple after all - no need for re-writing framework code .
What is WCF RIA Commanding? Is that even the proper name?
I have a silverlight application in which I want to use command binding to call methods on the webserver; however I'm unsure as to how to go about creating such a class and method so that it can be picked up by RIA and be used in the silverlight XAML; whiout any code behind.
a 'command' in the context of a Silverlight (or WPF) application is a class that implements the ICommand interface.
It is used to bind code in ViewModels to controls in Views.
Just about all the decent MVVM frameworks contain them (PRISM has DelegateCommand, MvvmLight has RelayCommand, etc) but it's not too hard to write your own...
Example usage:
in XAML:
<Button Command="{Binding GetCommand}" Content="Get" />
then in the ViewModel (bound to the View's DataContext)
public ICommand GetCommand
{
get
{
if (_getCommand == null) _getCommand = new RelayCommand(GetHandler, CanGetPredicate);
return _getCommand;
}
}
private void GetHandler()
{
// Do the work here - call into the server, or whatever.
}
private bool CanGetPredicate()
{
// work out if it is valid for this to be called or not
return (someRule == true); // or whatever
}
I'm working on a framework extension which handles dynamic injection using Ninject as the IoC container, but I'm having some trouble trying to work out how to achieve this.
The expectation of my framework is that you'll pass in the IModule(s) so it can easily be used in MVC, WebForms, etc. So I have the class structured like this:
public class NinjectFactory : IFactory, IDisposable {
readonly IKernel kernel;
public NinjectFactory(IModule[] modules) {
kernel = new StandardKernel(modules);
}
}
This is fine, I can create an instance in a Unit Test and pass in a basic implementation of IModule (using the build in InlineModule which seems to be recommended for testing).
The problem is that it's not until runtime that I know the type(s) I need to inject, and they are requested through the framework I'm extending, in a method like this:
public IInterface Create(Type neededType) {
}
And here's where I'm stumped, I'm not sure the best way to check->create (if required)->return, I have this so far:
public IInterface Create(Type neededType) {
if(!kernel.Components.Has(neededType)) {
kernel.Components.Connect(neededType, new StandardBindingFactory());
}
}
This adds it to the components collection, but I can't work out if it's created an instance or how I create an instance and pass in arguments for the .ctor.
Am I going about this the right way, or is Ninject not even meant to be be used that way?
Unless you want to alter or extend the internals of Ninject, you don't need to add anything to the Components collection on the kernel. To determine if a binding is available for a type, you can do something like this:
Type neededType = ...;
IKernel kernel = ...;
var registry = kernel.Components.Get<IBindingRegistry>();
if (registry.Has(neededType)) {
// Ninject can activate the type
}
Very very late answer but Microsoft.Practices.Unity allows Late Binding via App.Config
Just in case someone comes across this question
I'm using the WCF RIA Services Beta with Silverlight 3.0 and I want to be able to configure the timeout from the client. I know that the underlying technology is WCF and the default timeout seems to be 60 seconds as I would expect.
Is there an easy way to control this and other WCF settings?
My first thought is to try the DomainContext OnCreated hook point which was mentioned in the RIA Services Overview pdf file that was available prior to RIA Services going beta. The MSDN documentation for the DomainContext object no longer mentions the method although it is still there? I'm not sure if this is a case of the documentation lagging behind or an indication that I shouldn't use this extensibility point.
namespace Example.UI.Web.Services
{
public sealed partial class CustomDomainContext
{
partial void OnCreated()
{
// Try and get hold of the WCF config from here
}
}
}
http://blogs.objectsharp.com/CS/blogs/dan/archive/2010/03/22/changing-timeouts-in-wcf-ria-services-rc.aspx
Either one line after domain context creation:
((WebDomainClient<LibraryDomainContext.ILibraryDomainServiceContract>)this.DomainClient).ChannelFactory.Endpoint.Binding.SendTimeout = new TimeSpan(0, 5, 0);
or a partial class
public partial class LibraryDomainContext
{
partial void OnCreated()
{
if(DesignerProperties.GetIsInDesignMode(App.Current.RootVisual))
((WebDomainClient<LibraryDomainContext.ILibraryDomainServiceContract>)this.DomainClient).ChannelFactory.Endpoint.Binding.SendTimeout = new TimeSpan(0, 5, 0);
}
}
For reference the code below nearly works but you can't access a private member using reflection in Silverlight. Wouldn't have been happy with this hack though anyway. Interesting to note that there is a WebDomainClient contructor that takes a Binding parameter private WebDomainClient(Uri serviceUri, bool usesHttps, Binding binding) but the XML Comment for this states Private constructor. Should be made public once we have an end-to-end extensibility story on top of WCF. Looks like I'll have to wait a while before they get to exposing this kind of configuration to us.
public sealed partial class AppDomainContext
{
partial void OnCreated()
{
var webDomainClient = ((WebDomainClient<AppDomainContext.IAppDomainServiceContract>)this.DomainClient);
// Can I use reflection here to get hold of the Binding
var bindingField = webDomainClient.GetType().GetField("_binding", BindingFlags.NonPublic | BindingFlags.Instance);
// In Silverlight, the value of a private field cannot be access by using reflection so the GetValue call throws an exception
// http://msdn.microsoft.com/en-us/library/4ek9c21e%28VS.95%29.aspx
var binding = bindingField.GetValue(webDomainClient) as System.ServiceModel.Channels.Binding;
// So near yet so far!!
binding.SendTimeout = new TimeSpan(0,0,1);
}
}
I have a WCF service that generates loads Entity Framework objects (as well as some other structs and simple classes used to lighten the load) and sends them over to a client application.
I have changed 2 of the classes to implement an interface so that I can reference them in my application as a single object type. Much like this example:
Is it Possible to Force Properties Generated by Entity Framework to implement Interfaces?
However, the interface type is not added to my WCF service proxy client thingymebob as it is not directly referenced in the objects that are being sent back over the wire.
Therefore in my application that uses the service proxy classes, I can't cast or reference my interface..
Any ideas what I'm missing?
Here's some example code:
//ASSEMBLY/PROJECT 1 -- EF data model
namespace Model
{
public interface ISecurable
{
[DataMember]
long AccessMask { get; set; }
}
//partial class extending EF generated class
//there is also a class defined as "public partial class Company : ISecurable"
public partial class Chart : ISecurable
{
private long _AccessMask = 0;
public long AccessMask
{
get { return _AccessMask; }
set { _AccessMask = value; }
}
public void GetPermission(Guid userId)
{
ChartEntityModel model = new ChartEntityModel();
Task task = model.Task_GetMaskForObject(_ChartId, userId).FirstOrDefault();
_AccessMask = (task == null) ? 0 : task.AccessMask;
}
}
}
//ASSEMBLY/PROJECT 2 -- WCF web service
namespace ChartService
{
public Chart GetChart(Guid chartId, Guid userId)
{
Chart chart = LoadChartWithEF(chartId);
chart.GetPermission(userId); //load chart perms
return chart; //send it over the wire
}
}
Interfaces won't come across as separate entities in your WSDL - they will simply have their methods and properties added to the object that exposes them.
What you want to accomplish you can do using abstract classes. These will come across as distinct entities.
Good luck. Let us know how you decided to proceed.