Can a DomainService return a single custom type? - silverlight-4.0

I would like a method on my in my domain service similar to:
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
That doesn't work. Nothing is auto-generated for the Silverlight client app. Howerver if I make this an IQueryable method, then I get something generated on the client. I'll get a SystemStates property and a Query method on the context object.
Is there no way to make this a simple WCF call? I suppose I could a WCF Silverlight Enabled service to my RIA Web site, and then setting a Service Reference (that can't be right?) (and why can't I see the Services Reference in the Silverlight app?)
On first blush it seems that RIA services forces a very data-centric/easy CRUD which is great for table editors, but not so much for LOB applications that go behind drag on a datagrid and you're done.

You can return just one entity using an attribute (assuming that SystemState is your entity):
Ex:
[Query(IsComposable = false)]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
Remember that this is still a query and Ria Services will generate a method in your DomainContext like:
EntityQuery<SystemState> GetSystemStatusQuery()
Use it like a normal EntityQuery, but keep in mind that you can't perform query operations (sorting or filtering) on the returned object.
If you want to execute an operation on server, try using the [Invoke] attribute. Ex:
[Invoke]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
I don't know how complex your return type can be, but I guess if it can be serialized, it will work (not sure).

Related

ServiceStack: Can we Pass Data through a RequestFilterAttribute to the calling service

Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}

using log4net and IHttpModule with a WCF service

I have a Website that contains a number of webpages and some WCF services.
I have a logging IHttpModule which subscribes to PreRequestHandlerExecute and sets a number of log4net MDC variables such as:
MDC.Set("path", HttpContext.Current.Request.Path);
string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
MDC.Set("ip", ip);
This module works well for my aspx pages.
To enable the module to work with WCF I have set aspNetCompatibilityEnabled="true" in the web.config and RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed on the service.
But when the service method is called the MDC no longer contains any of the set values. I have confirmed they are being set by putting a logging method in the PreRequestHandlerExecute.
I think the MDC is loosing the values because in the log I can see the PreRequestHandlerExecute handler method and service method calls are on separate
threads.
The post log4net using ThreadContext.Properties in wcf PerSession service suggests using log4net.GlobalContext but I think that solution would run into issues if two users hit the application at the same time as GlobalContext is shared by all threads.
Is there a way to make this work?
Rather than taking the values from the HttpContext and storing them in one of log4net's context objects, why not log the values directly from the HttpContext? See my answer to the linked question for some techniques that might work for you.
Capture username with log4net
If you go to the bottom of my answer, you will find what might be the best solution. Write an HttpContext value provider object that you can put in log4net's GlobalDiagnosticContext.
For example, you might do something like this (untested)
public class HttpContextValueProvider
{
private string name;
public HttpContextValueProvider(string name)
{
this.name = name.ToLower();
}
public override string ToString()
{
if (HttpContext.Current == null) return "";
var context = HttpContext.Current;
switch (name)
{
case "path":
return context.Request.Path;
case "user"
if (context.User != null && context.User.Identity.IsAuthenticated)
return context.User.Identity.Name;
case "ip":
string ip = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = context.Request.ServerVariables["REMOTE_ADDR"];
return ip;
default:
return context.Items[name];
}
return "";
}
}
In the default clause I assume the name, if it is not a specifically case that we want to handle, represents a value in the HttpContext.Current.Items dictionary. You could make it more generic by also adding the ability to access Request.ServerVariables and/or other HttpContext information.
You would use this object like so:
Somewhere in your program/web site/service, add some instances of the object to log4net's global dictionary. When log4net resolves the value from the dictionary, it will call ToString before logging the value.
GDC.Set("path", new HttpContextValueProvider("path"));
GDC.Set("ip", new HttpContextValueProvider("ip"));
Note, you are using log4net's global dictionary, but the objects that you are putting in the dictionary are essentially wrappers around the HttpContext.Current object, so you will always be getting the information for the current request, even if you are handling simultaneous requests.
Good luck!

gSOAP instance of .NET service

I have a linux c++ client (via gSOAP) to WCF c# server. The WCF c# service contains list of objects, on which some action is executed. Each time i call some function on service, the new object is created, action on that object is executed and that object lands into list in service. at the end i am calling another function on service, which loops over all objects in a list and executes another call on them. this works as intended on c#, with both client and service pure WCF.
it works different via gSOAP. each time i call a first function on service via gSOAP, that action is executed and list is updated. but it is each time new service. so basically i am dealing each time with new service. i do not wont serialize/deserialize object itself, to have it on inux side.
any ideas how to solve this?
on c# side i have something like (unnecessery details skipped)
class Service : IService
{
List list = new List();
void func1(int i)
{
Class1 c = new Class1(i);
c.create();
list.Add(c);
}
void func2()
{
foreach(Class1 c in list)
{
c.close();
}
}
}
on gSOAP side i have something like
Proxy service (endpoint);
service.func1(1);
service.func1(2);
//...
service.func2();
as i said problem is: when func2() is executed it operates on empty list, meaning gSOAP object of Proxy service does not contain c# object of service.
Help, help!
ps.
the solution is found: container made "static" does the trick.

How do you set the Customvalidation in Metadata file, If the Metadata is in different Model project

My silverlight solution has 3 project files
Silverlight part(Client)
Web part(Server)
Entity model(I maintained the edmx along with Metadata in a seperate project)
Metadata file is a partial class with relavent dataannotation validations.
[MetadataTypeAttribute(typeof(User.UserMetadata))]
public partial class User
{
[CustomValidation(typeof(UsernameValidator), "IsUsernameAvailable")]
public string UserName { get; set; }
}
Now my question is where I need to keep this class UsernameValidator
If my Metadata class and edmx are on Server side(Web) then I know I need to create a .shared.cs class in my web project, then add the proper static method.
My IsUserAvailable method intern will call a domainservice method as part of asyc validation.
[Invoke]
public bool IsUsernameAvailable(string username)
{
return !Membership.FindUsersByName(username).Cast<MembershipUser>().Any();
}
If my metadata class is in the same project as my domain service is in then I can call domain service method from my UsernameValidator.Shared.cs class.
But here my entity models and Metadata are in seperate library.
Any idea will be appreciated
Jeff wonderfully explained the asyc validation here
http://jeffhandley.com/archive/2010/05/26/asyncvalidation-again.aspx
but that will work only when your model, metadata and Shared class, all are on server side.
There is a kind of hack to do this. It is not a clean way to do it it, but this is how it would probably work.
Because the .shared takes care of the code generation it doesn't complain about certain compile errors in the #if brackets of the code. So what you can do is create a Validator.Shared.cs in any project and just make sure it generates to the silverlight side.
Add the following code. and dont forget the namespaces.
#if SILVERLIGHT
using WebProject.Web.Services;
using System.ServiceModel.DomainServices.Client;
#endif
#if SILVERLIGHT
UserContext context = new UserContext();
InvokeOperation<bool> availability = context.DoesUserExist(username);
//code ommited. use what logic you want, maybe Jeffs post.
#endif
The compiler will ignore this code part because it does not meet the condition of the if statement. Meanwhile on the silverlight client side it tries to recompile the shared validator where it DOES meet the condition of the if-statement.
Like I said. This is NOT a clean way to do this. And you might have trouble with missing namespaces. You need to resolve them in the non-generated Validator.shared.cs to finally let it work in silverlight. If you do this right you can have the validation in silverlight with invoke operations. But not in your project with models and metadata like you would have with Jeff's post.
Edit: I found a cleaner and better way
you can create a partial class on the silverlight client side and doing the following
public partial class User
{
partial void OnUserNameChanging(string value)
{
//must be new to check for this validation rule
if(EntityState == EntityState.New)
{
var ctx = new UserContext();
ctx.IsValidUserName(value).Completed += (s, args) =>
{
InvokeOperation invop = (InvokeOperation) s;
bool isValid = (bool) invop.Value;
if(!isValid)
{
ValidationResult error = new ValidationResult(
"Username already exists",
new string[] {"UserName"});
ValidationErrors.Add(error;
}
};
}
}
}
This is a method generated by WCF RIA Services and can be easily partialled and you can add out-of-band validation like this. This is a much cleaner way to do this, but still this validation now only exists in the silverlight client side.
Hope this helps

Returning wcf/json result from the wcf service

I Need by service contract to return the xml/json result depending on the request type.I also need a kind of helper function which will convert my result set (i am using linq to sql) so that i do not need to create the xml format for the result set by iterating through the table row many times.What is the suitable way to do that.
I need a kind of short cut method which will convert the table data to xml result.Had i been using asp.net mvc i would have been able to generate the xml data by overriding the the ExecuteResult method in the ActionResult and giving Conetnt-Type = "text/xml" as OP.But since i am using
Wcf i don't have the controller context(as controller context is the parameter that needs to be passed to Execute Result).
My present code for converting the table data to the xml format is below.
public XDocument UsersLists(string authToken)
{
bool IsAuthenticated = Authenticate(authToken);
XDocument xDoc = new XDocument();
XElement root = new XElement("Users");
if (IsAuthenticated)
{
List<User> lstUsers = _lService.UserRepository.GetUserCompanyFromAccountID(GetAccountId(authToken)).ToList();
if (lstUsers != null)
{
root.Add(new XElement("Message", "Success"));
foreach (var u in lstUsers)
{
XElement chid = new XElement("User");
root.Add(new XElement("UserId", u.UserId));
root.Add(new XElement("FirstName", u.FirstName));
root.Add(new XElement("LastName", u.LastName));
root.Add(new XElement("Email", u.Email));
root.Add(new XElement("CompanyName", u.Company.CompanyName));
root.Add(chid);
}
xDoc.Add(root);
return xDoc;
}
else
{
return ReturnFailure(xDoc, root);
}
}
else
{
return ReturnFailure(xDoc, root);
}
}
I need to eliminate this way of generating xml for each table records.
An early response is priceless.
Thanks
Technology : Windows Communication Foundation.
Implementation of single operation returning both XML or JSON differs between WCF 3.5 or WCF 4.0. For implementing this feature in WCF 3.5 check this thread. Generally you will have to create custom behavior and set the content type based on Accept header from the request. WCF 4.0 has native support for this feature. WebHttpBehavior has property AutomaticFormatSelectionEnabled. If you set this property to true it should just work out of the box.
To your second question. You don't need any custom formatting like in ASP.NET MVC. Formatting in WCF is handled by serialization. Just return collection from your operation and you will see what happens. Linq-To-Sql generated entities should be serializable by default. Just don't forget to execute query before returning from method. If you need special format of date which is not the same as Linq-To-Sql entities create custom data type which have public parameterless constructor and all required properties with getter and setter. If you want to make it clear makr that class with DataContract attribute and all properties with DataMember attribute. Return collection of that custom class instances from your operation.