How to use a converter on AutoQuery - servicestack

I have an autoquery implementation like so:
public QueryResponse<BlogDto> Get(BlogsLookUpRequest request)
{
AutoMapping.RegisterConverter((Blog from) => {
var to = from.ConvertTo<BlogDto>(skipConverters: true); // avoid infinite recursion
to.Category = string.Join(",", from.BlogToBlogCategories.Select(x => x.BlogCategoryId.ToString()).Distinct());
return to;
});
var q = _AutoQuery.CreateQuery(request, base.Request);
var results = _AutoQuery.Execute(request, q);
return results;
}
My autoquery type is QueryDb<Blog, BlogDto>.
The converter doesn't get called. I want to flatten my junction table object into a csv. Is it possible to define some custom mapping behavior for this in autoquery mapping?
If not, what is the best way to alter the result?
Edit:
I got it working by declaring another type public class BlogEntityQuery : QueryDb<Blog> that doesn't have a route. Then did this:
public QueryResponse<BlogDto> Get(BlogsLookUpRequest request)
{
AutoMapping.RegisterConverter((Blog from) => {
var to = from.ConvertTo<BlogDto>(skipConverters: true); // avoid infinite recursion
to.Category = string.Join(",", from.BlogToBlogCategories.Select(x => x.BlogCategoryId.ToString()).Distinct());
return to;
});
var q = _AutoQuery.CreateQuery(request, base.Request);
var rawResults = _AutoQuery.Execute(new BlogEntityQuery().PopulateWith(request), q);
var results = new QueryResponse<BlogDto>().PopulateWith(rawResults);
return results;
}
It works but feels a bit hacky. As AutoQuery is performing automatic mapping I feel like there should be an option to register a converter for it to use.

AutoMapping Registrations like all configuration should only be configured once on Startup, e.g. in AppHost.Configure().
AutoQuery itself doesn't do any mapping, the AutoQuery Response result set is populated directly from the DB results which is why no converters are used.

Related

I need generic way to filter IQueryable data and filters are populated as dictionary

I need generic way to filter IQueryable data and filters are populated as dictionary. I have already created method like this.
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
source.Where(m => m.GetType().GetProperty(key).GetValue(m, null).Equals(filterBy[key]));
}
return source.ToList();
}
But its always returning same result.
please find the caller
Dictionary<string, string> dtFilter = new Dictionary<string, string>();
dtFilter.Add("Id", "2");
var res = context.Set<MyEntity>().CustomApplyFilter<MyEntity>(dtFilter);
The Where extension method does not change the content of the IQueryable it is applied to. The return value of the method should be used:
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
source = source.Where(m => m.GetType().GetProperty(key).GetValue(m, null).Equals(filterBy[key]));
}
return source.ToList();
}
UPDATE:
I should have noticed it, my answer so far was applicable to LINQ to Objects only. When using LINQ to Entities, however, there are certain restrictions; only expression that can be converted to an SQL query can be used. Getting properties through reflection is not such an expression obviously.
When this is the case, one possible solution would be to build the ExpressionTree manually.
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
var paramExpr = Expression.Parameter(typeof(T), key);
var keyPropExpr = Expression.Property(paramExpr, key);
var eqExpr = Expression.Equal(keyPropExpr, Expression.Constant(filterBy[key]));
var condExpr = Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
source = source.Where(condExpr);
}
return source.ToList();
}
UPDATE2:
With the comment #Venkatesh Kumar given below, it is apparent that when the underlying type of the field provided is not of type string, this solution fails (with the error message : The binary operator Equal is not defined for the types 'System.Int64' and 'System.String').
One possible way to tackle this problem would be to have a dictionary of types and delegates to use for each such property.
Since this is a static method (an extension method which has to be static), declaring a static Dictionary in class scope would be reasonable:
Let's assume the name of the class in which CustomApplyFilter is declared is SOFExtensions:
internal static class SOFExtensions
{
private static Dictionary<Type, Func<string, object>> lookup = new Dictionary<Type, Func<string, object>>();
static SOFExtensions()
{
lookup.Add(typeof(string), x => { return x; });
lookup.Add(typeof(long), x => { return long.Parse(x); });
lookup.Add(typeof(int), x => { return int.Parse(x); });
lookup.Add(typeof(double), x => { return double.Parse(x); });
}
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
var paramExpr = Expression.Parameter(typeof(T), key);
var keyPropExpr = Expression.Property(paramExpr, key);
if (!lookup.ContainsKey(keyPropExpr.Type))
throw new Exception("Unknown type : " + keyPropExpr.Type.ToString());
var typeDelegate = lookup[keyPropExpr.Type];
var constantExp = typeDelegate(filterBy[key]);
var eqExpr = Expression.Equal(keyPropExpr, Expression.Constant(constantExp));
var condExpr = Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
source = source.Where(condExpr);
}
return source.ToList();
}
}
Other types and proper delegates for them should be added to the lookup Dictionary as required.

ServiceStack returns partial properties?

I'm trying to return only partial properties instead of the whole. Obviously it is a GET method. The link would be like:
http://websitename/classname?parameter1=xyz&parameter2=abc
and I can get either parameter1,parameter2 as well as xyz,abc.
I am wondering how to conditional serialize the json? I am looking to use shouldSerialize method but not sure how to use it? Please enlighten me. Thanks.
public Object Get(SalesOrderDetails request)
{
SalesOrderDataProcess sODP = new SalesOrderDataProcess(Db);
SalesOrder salesOrderTemp = sODP.GetSalesOrderById(request.SalesOrderId);
if (base.Request.QueryString.ToString().IsEmpty())
return salesOrderTemp;
else
{
JsonObject obj = JsonObject.Parse(salesOrderTemp.ToJson<SalesOrder>());
JsonObject obj1 = new JsonObject();
foreach (var entry in base.Request.QueryString)
{
var temp = base.Request.QueryString[entry.ToString()].ToString();
obj1.Add(entry.ToString(), (string)obj[entry.ToString()]);
}
return JsonSerializer.DeserializeFromString<Object>(obj1.ToJson());
}
}
For initial implementation, it is working but I would like some advice to improve the code.
See this answer on Conditional Serialization in ServiceStack.Text.
Your SalesOrder could have a bool ShouldSerialize(fieldName) method to indicate which fields should be serialized, e.g:
public class SalesOrder
{
HashSet<string> IncludeFields;
public bool? ShouldSerialize(string fieldName)
{
return IncludeFields.Contains(fieldName);
}
}

Create table with custom name dynamically and insert with custom table name

I want to create the table with custom name but I cannot find the sample code. I notice the only way to create table is by generic type like db.CreateTable(). May I know if there is a way to create the table name dynamically instead of using Alias? The reason is because sometime we want to store the same object type into different tables like 2015_january_activity, 2015_february_activity.
Apart from this, the db.Insert also very limited to object type. Is there anyway to insert by passing in the table name?
I think these features are very important as it exists in NoSQL solution for long and it's very flexible. Thanks.
OrmLite is primarily a code-first ORM which uses typed POCO's to create and query the schema of matching RDMBS tables. It also supports executing Custom SQL using the Custom SQL API's.
One option to use a different table name is to change the Alias at runtime as seen in this previous answer where you can create custom extension methods to modify the name of the table, e.g:
public static class GenericTableExtensions
{
static object ExecWithAlias<T>(string table, Func<object> fn)
{
var modelDef = typeof(T).GetModelMetadata();
lock (modelDef) {
var hold = modelDef.Alias;
try {
modelDef.Alias = table;
return fn();
}
finally {
modelDef.Alias = hold;
}
}
}
public static void DropAndCreateTable<T>(this IDbConnection db, string table) {
ExecWithAlias<T>(table, () => { db.DropAndCreateTable<T>(); return null; });
}
public static long Insert<T>(this IDbConnection db, string table, T obj, bool selectIdentity = false) {
return (long)ExecWithAlias<T>(table, () => db.Insert(obj, selectIdentity));
}
public static List<T> Select<T>(this IDbConnection db, string table, Func<SqlExpression<T>, SqlExpression<T>> expression) {
return (List<T>)ExecWithAlias<T>(table, () => db.Select(expression));
}
public static int Update<T>(this IDbConnection db, string table, T item, Expression<Func<T, bool>> where) {
return (int)ExecWithAlias<T>(table, () => db.Update(item, where));
}
}
These extension methods provide additional API's that let you change the name of the table used, e.g:
var tableName = "TableA"'
db.DropAndCreateTable<GenericEntity>(tableName);
db.Insert(tableName, new GenericEntity { Id = 1, ColumnA = "A" });
var rows = db.Select<GenericEntity>(tableName, q =>
q.Where(x => x.ColumnA == "A"));
rows.PrintDump();
db.Update(tableName, new GenericEntity { ColumnA = "B" },
where: q => q.ColumnA == "A");
rows = db.Select<GenericEntity>(tableName, q =>
q.Where(x => x.ColumnA == "B"));
rows.PrintDump();
This example is also available in the GenericTableExpressions.cs integration test.

Using Dapper.Net ORM, how do I cast stored procedure output to a concrete type?

Using Entity Framework I can create concrete classes from most of the sprocs in the database of a project I'm working on. However, some of the sprocs use dynamic SQL and as such no metadata is returned for the sproc.
So for a that sproc, I manually created a concrete class and now want to map the sproc output to this class and return a list of this type.
Using the following method I can get a collection of objects:
var results = connection.Query<object>("get_buddies",
new { RecsPerPage = 100,
RecCount = 0,
PageNumber = 0,
OrderBy = "LastestLogin",
ProfileID = profileID,
ASC = 1},
commandType: CommandType.StoredProcedure);
My concrete class contains
[DataContractAttribute(IsReference=true)]
[Serializable()]
public partial class LoggedInMember : ComplexObject
{
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int16 RowID
{
get
{
return _RowID;
}
set
{
OnRowIDChanging(value);
ReportPropertyChanging("RowID");
_RowID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("RowID");
OnRowIDChanged();
}
}
private global::System.Int16 _RowID;
partial void OnRowIDChanging(global::System.Int16 value);
partial void OnRowIDChanged();
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String NickName
{
get
{
return _NickName;
}
set
{
OnNickNameChanging(value);
ReportPropertyChanging("NickName");
_NickName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("NickName");
OnNickNameChanged();
}
}
private global::System.String _NickName;
partial void OnNickNameChanging(global::System.String value);
partial void OnNickNameChanged();
.
.
.
Without having to iterate through the results and add the output parameters to the LoggedInMember object, how do I map these on the fly so I can return a list of them through a WCF service?
If I try var results = connection.Query<LoggedInMember>("sq_mobile_get_buddies_v35", ... I get the following error:
System.Data.DataException: Error parsing column 0 (RowID=1 - Int64)
---> System.InvalidCastException: Specified cast is not valid. at Deserialize...
At a guess your SQL column is a bigint (i.e. Int64 a.k.a. long) but your .Net type has a n Int16 property.
You could play around with the conversion and ignore the stored procedure by doing something like:
var results = connection.Query<LoggedInMember>("select cast(9 as smallint) [RowID] ...");
Where you are just selecting the properties and types you want to return your object. (smallint is the SQL equivalent of Int16)
The solution to this was to create a complex object derived from the sproc with EF:
public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)
{
using (IDbConnection connection = OpenConnection("PrimaryDBConnectionString"))
{
try
{
var profile = connection.Query<ProfileDetailsByID_Result>("sproc_profile_get_by_id",
new { profileid = profileID },
commandType: CommandType.StoredProcedure).FirstOrDefault();
return profile;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // use singleton for logging
return null;
}
}
}
In this case, ProfileDetailsByID_Result is the object that I manually created using Entity Framework through the Complex Type creation process (right-click on the model diagram, select Add/Complex Type..., or use the Complex Types tree on the RHS).
A WORD OF CAUTION
Because this object's properties are derived from the sproc, EF has no way of knowing if a property is nullable. For any nullable property types, you must manually configure these by selecting the property and setting its it's Nullable property to true.

Dynamic Func/Lambdas

I've got a method similar to this, which loops through one set of data and uses a value of the first object to find an object in a second set of data:
private void someMethod(IQueryable<RemoteUser> source, IQueryable<LocalUser> targetData) {
// Loop all records in source data
foreach(var u in source) {
// Get keyvalue from source data and use it to find the matching record in targetData
var keyValue = u.id;
var object = from data.Where(o => o.id == keyValue).FirstOrDefault();
...
}
}
I'd like to make it more re-usable by passing in Func or using some other type of lambda and then convert the method to something I can use in a generic manner, i.e.:
private void someMethod<SourceT, TargetT>(IQueryable<SourceT> source, IQueryable<TargetT> targetData) {
....
}
What I'm not exactly sure on is how I can build a Func/Predicate/etc and pass it into the method. Keeping in mind that the "id" property will not be the same across all SourceT & TargetT properties.
To further explain, I'd like something where I can do this:
someMethod(RemoteUsers, LocalUsers, something here to say 'find the user using the userId property');
someMethod(RemoteProducts, LocalProducts, something here to say 'find the user using the productId property');
Here's the most basic implementation of your someMethod routine:
private void someMethod<S, T, P>(
IQueryable<S> source,
IQueryable<T> target,
Func<S, P> sourceSelector,
Func<T, P> targetSelector)
{
foreach(var s in source)
{
var sp = sourceSelector(s);
var #object = target
.Where(t => targetSelector(t).Equals(sp)).FirstOrDefault();
//...
}
}
This implementation keeps the structure of your original code, but this comes at a cost. You are effectively doing source.Count() * target.Count() queries against your database. You need to drop the use of foreach when working with IQueryable<>.
In fact, whenever you start writing code with foreach, you need to ask yourself if you can use a LINQ query to build and filter your data and make the foreach loop only do the "simplest" tasks.
Here's how to make the method work better:
private void someMethod2<S, T, P>(
IQueryable<S> source,
IQueryable<T> target,
Expression<Func<S, P>> sourceSelector,
Expression<Func<T, P>> targetSelector)
{
var query = source
.GroupJoin(
target,
sourceSelector,
targetSelector,
(s, ts) => ts.FirstOrDefault());
foreach(var #object in query)
{
//...
}
}
Note the use of Expression<Func<,>> and not just Func<,>. Also note the GroupJoin method call.

Resources