I get an error when use Odata query with contains - asp.net-web-api

Here is controller code
[EnableQuery]
public IQueryable<Product> Get()
{
var productRepository = new ProductRepository();
return productRepository.Retrieve().AsQueryable();
}
Here is Retrieve() method
internal List<Product> Retrieve()
{
var filePath = HostingEnvironment.MapPath(#"~/App_Data/product.json");
var json = System.IO.File.ReadAllText(filePath);
var products = JsonConvert.DeserializeObject<List<Product>>(json);
return products;
}
And Product class
public class Product
{
public string Description { get; set; }
public decimal Price { get; set; }
public string ProductCode { get; set; }
public int ProductId { get; set; }
public string ProductName { get; set; }
public DateTime ReleaseDate { get; set; }
}
Other filters like $filter=Price+gt+6 or $top=4 and $skip=1 work fine. WebApi.OData package version=5.7.0
Error:
"The query specified in the URI is not valid. An unknown function with name 'contains' was found.

substringof() is a V3 function while contains() is a V4 function.
Try contains:
$filter=contains(Name,'value')

You are probably using an OData library package for OData version 3, but contains is a version 4 function. You can either query with the substringof function defined in version 3, or switch to a package that supports OData version 4.

Related

how to use projection in the include extension method in ef core?

I want to able to select certain entity properties (columns from db) in the include statement of queryable object. My query looks like below but I m getting error Lambda expression used inside Include is not valid
var samuraiWithQuotesQueryable = _context.Samurais.AsQueryable()
.Include(s => s.Quotes.Select(x => new { x.Text }));
// additional filters followed by getting the list
var samuraiList = samuraiWithQuotesQueryable.ToList();
Samurai and Quote entities look like below
public class Samurai
{
public Samurai()
{
Quotes = new List<Quote>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Quote> Quotes { get; set; }
}
public class Quote
{
public int Id { get; set; }
public string Text { get; set; }
public Samurai Samurai { get; set; }
public int SamuraiId { get; set; }
}
Wondering if this is possible with the IQueryable object?

Url syntax with List property on complex type

Model:
[DataContract]
public class Employee
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[DataMember(Name ="id")]
public int Id{ get; set; }
[DataMember(Name = "fullName")]
public string FullName { get; set; }
}
[DataContract]
public class Department
{
public Department()
{
this.Employees = new List<Employee>();
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "employees")]
public List<Employee> Employees { get; set; }
}
Controller
public HttpResponseMessage Get([FromUri]Department model)
{
if (ModelState.IsValid)
{
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
Url : "http://localhost:2070/home/get/?id=1&name=IT&Employees=1,John"
I am trying to invoke above URL and the Model does not read the Employees. Other property like int,double,string,decimal are read by the Model.
Can anyone help me on what is the correct format in passing List thru Url.
Also, I dont want to decorate each of my class with modelbinders nor the parameter in my controller.
Tech : WebApi, .Net3.5
You need to specify the index of the list and property to bind with when using FromUri and list/array
Try it this way
http://localhost:2070/home/get/?id=1&name=IT&Employees[0].Id=1&Employees[0].Name=John

Automapper unable to project one enum type to another

I'm leveraging the Project functionality in Automapper and Entity Framework, but I'm running into an issue where Automapper doesn't seem to want to project one enum type to another.
I have the following entities:
public class UserProfile
{
public Guid Id { get; set; }
public string Name { get; set; }
private HashSet<UserProfilePhone> m_Phones;
public virtual HashSet<UserProfilePhone> Phones
{
get { return m_Phones ?? (m_Phones = new HashSet<UserProfilePhone>()); }
set { this.m_Phones = value; }
}
}
public class UserProfilePhone
{
public PhoneType Type { get; set; }
public virtual string Number { get; set; }
}
public enum PhoneType
{
Home = 1,
Work = 2,
Mobile = 3,
Other = 4
}
I then am projecting these types to the following models:
public class UserProfileModel
{
public Guid Id { get; set; }
public virtual string Name { get; set; }
public IEnumerable<UserProfilePhoneModel> Phones { get; set; }
}
public class UserProfilePhoneModel
{
public UserProfilePhoneTypeModel Type { get; set; }
public string Number { get; set; }
}
public enum UserProfilePhoneTypeModel
{
Home = 1,
Work = 2,
Mobile = 3,
Other = 4
}
I then setup my mappings like so:
Mapper.CreateMap<PhoneType, UserProfilePhoneTypeModel>();
Mapper.CreateMap<UserProfilePhone, UserProfilePhoneModel>();
Mapper.CreateMap<UserProfile, UserProfileModel>();
And finally I'm executing my projection:
var result = dbContext.UserProfiles.Project().To<UserProfileModel>();
When I do this, I get the following exception:
AutoMapper.AutoMapperMappingException: Unable to create a map expression from MyNamespace.PhoneType to
MyNamespace.Models.UserProfilePhoneTypeModel
Unable to create a map expression from MyNamespace.PhoneType to MyNamespace.Models.UserProfilePhoneTypeModel
Result StackTrace:
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey
key, Func2 valueFactory)
...
I've tried creating explicit mappings, but they appear to be ignored. What am I doing wrong here?
As usual, I figured out the answer almost as soon as I posted the question.
Modifying the create map line to provide an explicit cast did the trick:
Mapper.CreateMap<UserProfilePhone, UserProfilePhoneModel>()
.ForMember(m => m.Type, opt => opt.MapFrom(t => (UserProfilePhoneTypeModel)t.Type));

How to define xml attributes using web api and model binding

I'm creating an xml feed of products which needs to match the clients scheme exactly.
I'm using web api. I would like the property extractDate to be an attribute. The following code is outputting extractDate as an element not an attribute
public Feed GetProducts()
{
var feed = new Feed()
{
extractDate = "extractDate",
incremental = true,
name = "name",
Brands = GetBrands(),
Categories = GetCategories(),
Products = GetProducts()
};
return feed;
}
Here is my model Feed. Note the following doesn't seem to turn the element into an attribute
[XmlAttribute(AttributeName = "extractDate")]
public class Feed
{
[XmlAttribute(AttributeName = "extractDate")] //attribute is ignored
public string extractDate { get; set; }
public bool incremental { get; set; }
public string name { get; set; }
public List<Brand> Brands { get; set; }
public List<Category> Categories { get; set; }
public List<Product> Products { get; set; }
}
How do i output
<feed extractDate="2012/01/01"
// other logic
/>
Web API by default uses DataContractSerializer in XmlMediaTypeFormatter and probably that's the reason you are not seeing your attribute decorations taking effect. Do you have the XmlSerializer enabled on the XmlMediaTypeFormatter to see your expected output?
config.Formatters.XmlFormatter.UseXmlSerializer = true;
Also, you could set XmlSerializer only for specific types too using the following api:
config.Formatters.XmlFormatter.SetSerializer<>
Edit
Managed to simulate your issue with a blank project and Kiran's answer seems to do the trick.Just add this line in your controller(for testing purposes, it should probably be in your global.asax)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
Do you have the [XmlRoot] on top of your class or is it missing?
Not sure the attribute will work without an xml class decorator.
A simple sanity check you could do is serialize the class without web api involved to make sure it's nothing silly but actually web api related.
How about this:
[XmlRoot("feed")]
public class Feed
{
[XmlAttribute(AttributeName = "extractDate")]
public string extractDate { get; set; }
public bool incremental { get; set; }
public string name { get; set; }
public List<Brand> Brands { get; set; }
public List<Category> Categories { get; set; }
public List<Product> Products { get; set; }
}

RavenDB SelectMany not supported

I am trying to find one or more documents in RavenDB based on the values of a child collection.
I have the following classes
public class GoldenDocument
{
public GoldenDocument()
{
LinkedDocuments = new List<LinkedDocument>();
MergeMatchFields = new List<MergeMatchField>();
}
public string Id { get; set; }
public Guid SourceRowId { get; set; }
public List<MergeMatchField> MergeMatchFields { get; set; }
public List<LinkedDocument> LinkedDocuments { get; set; }
}
And the class that is in the collection MergeMatchFields
public class MergeMatchField
{
public string Id { get; set; }
public Guid OriginId { get; set; }
public string Name { get; set; }
public MatchType MatchType { get; set; }
public double MatchPerc { get; set; }
public string Value { get; set; }
}
In a List<MergeFields> mergeFields collection I have values that is not stored in RavenDB yet. Values are compared to values in a RavenDB document for find if it is a possible match by executing the following query:
using (var session = documentStore.OpenSession())
{
var docs = from gd in session.Query<GoldenDocument>()
from mf in gd.MergeMatchFields
from tf in mergeFields
where mf.Name == tf.Name
&& JaroWinklerCalculator.jaroWinkler(mf.Value, tf.Value) > .90d
&& !string.IsNullOrEmpty(mf.Value)
select gd;
}
I understand that ravenDB does not support SelectMany() so how would I go about getting the results from the Document store?
Create an index for this that would output the values you want to query on.
Note that you can't just execute arbitrary code the way you do here: JaroWinklerCalculator.jaroWinkler(mf.Value, tf.Value) > .90d
But you can use fuzzy queries, and they will do the same.

Resources