I have a DataService class on top of a Entity Framework 5 DbContext:
public class MyDataService : DataService<MyDbContext>, IServiceProvider
{
[WebGet]
public IQueryable<Product> GetProducts1(int category)
{
return from p in this.CurrentDataSource.Products
where p.Category == category
select p;
}
}
Now I want to expose methods from my DbContext to the DataService and used this sample code: http://efactionprovider.codeplex.com/
public class MyDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
[NonBindableAction]
public IQueryable<Product> GetProducts2(int category)
{
return from p in this.CurrentDataSource.Products
where p.Category == category
select p;
}
}
Accessing http://localhost:12345/MyDataService.svc/$metadata show that both methods are known, but the first one hat am:HttpMethod="GET"` attribut
<EntityContainer Name="MyDbContext" m:IsDefaultEntityContainer="true">
...
<FunctionImport Name="GetProducts1" ReturnType="Collection(MyNameSpace.Product)" EntitySet="Products" m:HttpMethod="GET">
<Parameter Name="category" Type="Edm.Int32" Nullable="false"/>
</FunctionImport>
<FunctionImport Name="GetProducts1" ReturnType="Collection(MyNameSpace.Product)" EntitySet="Products">
<Parameter Name="category" Type="Edm.Int32" Nullable="false"/>
</FunctionImport>
...
I can execute GetProducts1 by accessing the url
http://localhost:12345/MyDataService.svc/GetProducts1?category=1
That doesn't work for GetProducts2 (propably because of GET not allowed)
But I managed to execute GetProducts2 by using fiddler:
POST: http://localhost:12345/MyDataService.svc/GetProducts1
Request Headers:
User-Agent: Fiddler
Host: localhost:12345
Content-Length: 12
Content-Type: application/json
Request Body:
{category:1}
Ok, now here is my problem: I consume this service with a Windows application using a Service Reference. Since the code generation in the DataServiceContext derived class does not cover the actions I need to call them by myself.
For the first one (GetProducts1) I can do:
public IEnumerable<Product> GetProducts1(int category)
{
var proxy = new MyDataServiceContext(
"http://localhost:12345/MyDataService.svc");
var queryString = String.Format("{0}/GetProducts1?category={1}",
proxy.BaseUri, category);
var uri = new Uri(queryString, UriKind.RelativeOrAbsolute);
return proxy.Execute<Product>(uri);
}
But I am struggling with the second one. I tried:
public IEnumerable<Product> GetProducts2(int category)
{
var proxy = new MyDataServiceContext(
"http://localhost:12345/MyDataService.svc");
var queryString = String.Format("{0}/GetProducts2",
proxy.BaseUri);
var uri = new Uri(queryString, UriKind.RelativeOrAbsolute);
return proxy.Execute<Product>(uri, "POST", false,
new UriOperationParameter("category", category));
}
but I get a DataServiceClientException: Content-Type-Header value missing. (Status-Code 400)
Is there any way to call this method my using the Execute method? I would prefer to continue using the DataServiceContext and not make any raw Requests myself.
Thanks in advance
btw.
I am using visual studion 2010 and the Microsoft.Data.Services and Microsoft.Data.Services.Client packages from nuget rather than the default System.Data.Services dlls, because I believe I need to set config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3; for executing actions in the first place.
Try using BodyOperationParameter instead of UriOperationParameter. Action parameters are included in request body instead of Uri.
Related
Upgrading to asp.net core 2.2 in my hobby project there is a new routing system I want to migrate to. Previously I implemented a custom IRouter to be able to set the controller for the request dynamically. The incoming request path can be anything. I match the request against a database table containing slugs and it looks up the a matching data container class type for the resolved slug. After that I resolve a controller type that can handle the request and set the RouteData values to the current HttpContext and passing it along to the default implementation for IRouter and everything works ok.
Custom implementaion of IRouter:
public async Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
var page = _pIndex.GetPage(requestPath);
if (page != null)
{
var controllerType = _controllerResolver.GetController(page.PageType);
if (controllerType != null)
{
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
newRouteData.Values["pageType"] = page.PageType;
newRouteData.Values["controller"] = controllerType.Name.Replace("Controller", "");
newRouteData.Values["action"] = "Index";
context.RouteData = newRouteData;
await _defaultRouter.RouteAsync(context);
}
}
}
A controller to handle a specific page type.
public class SomePageController : PageController<PageData>
{
public ActionResult Index(PageData currentPage)
{
return View("Index", currentPage);
}
}
However I got stuck when I'm trying to figure out how I can solve it using the new system. I'm not sure where I'm suppose to extend it for this behavior. I don't want to turn off the endpoint routing feature because I see an opportunity to learn something. I would aso appreciate a code sample if possible.
In ASP.NET 3.0 there is an new dynamic controller routing system. You can implement DynamicRouteValueTransformer.
Documentation is on the way, look at the github issue
When my web request is run, some properties that are in the Properties property of HttpRequestMessage are from the System.Web namespace, although the application I am developing is .NET Core 2 Azure function.
I would like to use one object from the collection, MS_HttpContext which is of type System.Web.HttpContextWrapper. Is it possible to use this object and somehow cast to it? This object seems to be from standard .NET framework.
[FunctionName("Track")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "track")]HttpRequestMessage req, TraceWriter log, ExecutionContext context)
{
// HttpContextWrapper is from System.Web, so this does not work
var ctx = (HttpContextWrapper)req.Properties["MS_HttpContext"]
}
EDIT: my question is slightly different than my previos question (Get remote IP address in Azure function), because it asks how can classes from System.Web namespace be accessed from a .NET Core application. Although I admit the desired result is the same in both questions, to get the remote IP address.
Alright, not that I know much how this works, but reflection seems to work:
private static string GetRemoteIpAddress(HttpRequestMessage req)
{
if (req.Properties.ContainsKey("MS_HttpContext") == false)
{
return null;
}
var httpContext = req.Properties["MS_HttpContext"];
var pi = httpContext.GetType().GetProperty("Request");
var request = pi.GetValue(httpContext);
pi = request.GetType().GetProperty("UserHostAddress");
var address = pi.GetValue(request);
return address == null ? null : address.ToString();
}
Following line of code gives me an error saying "The underlying connection was closed".
return this.repository.GetQuery<Countries>().Include(g => g.Cities).AsEnumerable().ToList();
But if I remove .Include(g => g.cities) it works fine.
this code is written in one of the operation in my WCF service, and I try to test it using WCF test client. I tried by calling this operation from MVC application also, and the same issue was occurring there too.
Also, i am using generic repository with entity framework
Repository code (only few important extract)
Constructor:
public GenericRepository(DbContext objectContext)
{
if (objectContext == null)
throw new ArgumentNullException("objectContext");
this._dbContext = objectContext;
this._dbContext.Configuration.LazyLoadingEnabled = false;
this._dbContext.Configuration.ProxyCreationEnabled = false;
}
GetQuery method:
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class
{
var entityName = GetEntityName<TEntity>();
return ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
}
Attempt#1
Created following overloads in repository code:
public IQueryable<TEntity> GetQuery<TEntity>(params string[] includes) where TEntity : class
{
var entityName = GetEntityName<TEntity>();
IQueryable<TEntity> query = ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery<TEntity>(entityName);
foreach(string include in includes)
{
query = query.Include(include);
}
return query;
}
public IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate, params string[] includes) where TEntity : class
{
return GetQuery<TEntity>(includes).Where(predicate);
}
WCF is now trying to execute following line of code:
return this.repository.GetQuery<Countries>("Cities").AsEnumerable().ToList()
But it still gives the same error of "The underlying connection was closed". I tested it in WCF test client. However, when I debug the repository code it shows the navigation object getting included in result, but the issue seems occurring while trying to pass the output to client (WCF test client, or any other client)
After looking at the code you've now posted, I can conclude that, indeed, your DbContext is being closed at the end of the GetQuery method, and is thus failing when you try to use include. What you might want to do to solve it is to have an optional params variable for the GetQuery method that will take in some properties to be included, and just do the include right in the GetQuery method itself.
I'm trying to test validation that I've setup for my wcf service. What's the best way to do it?
[ServiceContract]
[ValidationBehavior]
public interface IXmlSchemaService
{
[OperationContract(Action = "SubmitSchema")]
[return: MessageParameter(Name = "SubmitSchemaReturn")]
[FaultContract(typeof(ValidationFault))]
JobData SubmitSchema([XmlStringValidator] string xmlString);
}
XmlStringValidator is a custom validator I've created. Ideally I want something like:
XmlSchemaService service = new XmlSchemaService();
service.SubmitSchema();
But in this case, validation isn't called.
By definition, this sort of test is an integration test, not a unit test. The VAB validation will only take place if the service operation is invoked via the WCF pipeline.
While you could perhaps force your calls through the WCF pipeline without creating a client proxy, wouldn't it make more sense to test this from a client proxy in order to ensure that the client is seeing exactly the fault you wish to publish from your service when the validation fails?
You can test out the validation in isolation. While it is not feasible to have validation invoked when running the service code directly, the Validation Application Block has two methods for testing your code (that I am aware of).
Using the ValidatorFactory to create a validator for your input type and Assert that the validation results contain the expected errors.
Instantiating the Validator directly and testing it with various input.
In practice I end up using a combination of the two techniques. I use method one to test for validation errors on complex input types. As an example:
[DataContract]
public class Product
{
[DataMember, NotNullValidator]
public string Name { get; set; }
[DataMember, RangeValidator(0.0, RangeBoundaryType.Exclusive,
double.MaxValue, RangeBoundaryType.Ignore,
ErrorMessage = "The value cannot be less than 0.")]
public double Price { get; set; }
}
[TestMethod]
public void InvalidProduct_ReturnsValidationErrors()
{
Product request = new Product()
{
Price = -10.0
};
var validatorFactory = EnterpriseLibraryContainer.Current
.GetInstance<ValidatorFactory>();
var validator = validatorFactory.CreateValidator<Product>();
var results = validator.Validate(request);
Assert.IsTrue(results.Any(vr => vr.Key == "Name"
&& vr.Message == "The value cannot be null."));
Assert.IsTrue(results.Any(vr => vr.Key == "Price"
&& vr.Message == "The value cannot be less than 0."));
}
For method 2 I would have tests that cover my use case scenarios for Validators I've created. As another example:
[TestMethod]
public void XmlStringValidator_ReturnsErrors_OnInvalidInput()
{
var validator = new XmlStringValidator();
var results = validator.Validate("Your input goes here");
Assert.IsTrue(results.Any(vr => vr.Key == "[KeyNameInValidator]" &&
vr.Message == "[Expected error message based on input]"));
}
Method 2 will allow you to create as many test scenarios as you would like for your XmlStringValidator.
You can find more information about these methods in this article: Chapter 6 - Banishing Validation Complication
I am having a minor problem with WCF service proxies where the message contains List<string> as a parameter.
I am using the 'Add Service reference' in Visual Studio to generate a reference to my service.
// portion of my web service message
public List<SubscribeInfo> Subscribe { get; set; }
public List<string> Unsubscribe { get; set; }
These are the generated properties on my MsgIn for one of my web methods.
You can see it used ArrayOfString when I am using List<string>, and the other takes List<SubscribeInfo> - which matches my original C# object above.
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
public System.Collections.Generic.List<DataAccess.MailingListWSReference.SubscribeInfo> Subscribe {
get {
return this.SubscribeField;
}
set {
if ((object.ReferenceEquals(this.SubscribeField, value) != true)) {
this.SubscribeField = value;
this.RaisePropertyChanged("Subscribe");
}
}
}
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false)]
publicDataAccess.MailingListWSReference.ArrayOfString Unsubscribe {
get {
return this.UnsubscribeField;
}
set {
if ((object.ReferenceEquals(this.UnsubscribeField, value) != true)) {
this.UnsubscribeField = value;
this.RaisePropertyChanged("Unsubscribe");
}
}
}
The ArrayOfString class generated looks like this. This is a class generated in my code - its not a .NET class. It actually generated me a class that inherits from List, but didn't have the 'decency' to create me any constructors.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.CollectionDataContractAttribute(Name="ArrayOfString", Namespace="http://www.example.com/", ItemName="string")]
[System.SerializableAttribute()]
public class ArrayOfString : System.Collections.Generic.List<string> {
}
The problem is that I often create my message like this :
client.UpdateMailingList(new UpdateMailingListMsgIn()
{
Email = model.Email,
Name = model.Name,
Source = Request.Url.ToString(),
Subscribe = subscribeTo.ToList(),
Unsubscribe = unsubscribeFrom.ToList()
});
I really like the clean look this gives me.
Now for the actual problem :
I cant assign a List<string> to the Unsubscribe property which is an ArrayOfString - even though it inherits from List. In fact I cant seem to find ANY way to assign it without extra statements.
I've tried the following :
new ArrayOfString(unsubscribeFrom.ToList()) - this constructor doesn't exist :-(
changing the type of the array used by the code generator - doesn't work - it always gives me ArrayOfString (!?)
try to cast List<string> to ArrayOfString - fails with 'unable to cast', even though it compiles just fine
create new ArrayOfString() and then AddRange(unsubscribeFrom.ToList()) - works, but I cant do it all in one statement
create a conversion function ToArrayOfString(List<string>), which works but isn't as clean as I want.
Its only doing this for string, which is annoying.
Am i missing something? Is there a way to tell it not to generate ArrayOfString - or some other trick to assign it ?
Any .NET object that implements a method named "Add" can be initialized just like arrays or dictionaries.
As ArrayOfString does implement an "Add" method, you can initialize it like this:
var a = new ArrayOfString { "string one", "string two" };
But, if you really want to initialize it based on another collection, you can write a extension method for that:
public static class U
{
public static T To<T>(this IEnumerable<string> strings)
where T : IList<string>, new()
{
var newList = new T();
foreach (var s in strings)
newList.Add(s);
return newList;
}
}
Usage:
client.UpdateMailingList(new UpdateMailingListMsgIn()
{
Email = model.Email,
Name = model.Name,
Source = Request.Url.ToString(),
Subscribe = subscribeTo.ToList(),
Unsubscribe = unsubscribeFrom.To<ArrayOfString>()
});
I prefer not to return generic types across a service boundary in the first place. Instead return Unsubscribe as a string[], and SubscriptionInfo as SubscriptionInfo[]. If necessary, an array can easily be converted to a generic list on the client, as follows:
Unsubscribe = new List<string>(unsubscribeFrom);
Subscribe = new List<SubscriptionInfo>(subscribeTo);
Too late but can help people in the future...
Use the svcutil and explicitly inform the command line util that you want the proxy class to be serialized by the XmlSerializer and not the DataContractSerializer (default). Here's the sample:
svcutil /out:c:\Path\Proxy.cs /config:c:\Path\Proxy.config /async /serializer:XmlSerializer /namespace:*,YourNamespace http://www.domain.com/service/serviceURL.asmx
Note that the web service is an ASP.NET web service ok?!
If you are using VS 2008 to consume service then there is an easy solution.
Click on the "Advanced..." button on the proxy dialog that is displayed when you add a Service Reference. In the Collection Type drop down you can select System.Generic.List. The methods returning List should now work properly.
(Hope this is what you were asking for, I'm a little tired and the question was a tad difficult for me to read.)