Sending select parameters outside of a function? - c#

I'am trying to send two select paramaters to a function from outside. What i am trying to achieve is simplified at below
I have a model like this,
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
SampleData:
3 / Barrack / Obama
4 / Vladimir / Putin
and an another model
public class IdTextModel
{
public string Id { get; set; }
public string Text { get; set; }
}
and I'm trying to create a function like this
public List<IdNameModel> GetList(propselectorA, propselectorB){
return data.Select(x => new IdTextModel(){
Id = propselectorA,
Text = propselectorB
});
}
Usage:
var list = GetList(x => x.Id, x => x.Surname);
ExpectedResult:
3 / Obama
4 / Putin
How can i accomplish that? Thank you.

You can use Expression<Func<Entity,IdNameModel>> to abstract out the types of Id and Text:
public List<T> GetList(
Expression<Func<Entity,T>> selector
) {
return data.Select(selector).ToList();
}
and call it like this:
var list = GetList(x => IdTextModel { Id = x.Id, Name = x.Surname } );
Passing selectors for two string properties is also possible, but using Zip makes it less efficient:
public List<IdTextModel> GetList(
Expression<Func<Entity,string>> selectorId
, Expression<Func<Entity,string>> selectorName
) {
return data
.Select(selectorId)
.Zip(
data.Select(selectorName)
, (id, name) => new IdTextModel { Id = id, Name = name }
).ToList();
}

Related

Query separate collection in RavenDB Index (WHERE IN)

Using RavenDB v4.2 or higher, I want to setup an index that queries another collection. Basically, reproduce a WHERE IN clause in the mapping part of the index.
The models below represent two collections. Here each User has a collection of Device ID's:
class Device {
public string Id { get; set; }
public string Name { get; set; }
}
class User {
public string Id { get; set; }
public string BlogPostId { get; set; }
public List<string> DeviceIds { get; set; }
}
Now consider the following index as an example on what I'm trying to achieve:
public class DeviceIndex : AbstractIndexCreationTask<Device, DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
Map = devices => from d in devices
select new Result
{
Id = d.Id,
DeviceName = d.Name,
HasUser = ... ?, // How to get this from Users collection?
UserCount = ... ? // same...
};
}
How do I fill the HasUser true/false and UserCount properties in this index? E.g. how can I query the 'User' collection here?
Please note that this example is seriously simplified for brevity. I'm not so much interested in workarounds, or changing the logic behind it.
As #Danielle mentioned you need to use a mutli-map-index and reduce the result.
Here is a working example
public class DeviceIndex : AbstractMultiMapIndexCreationTask<DeviceIndex.Result>
{
public class Result
{
public string Id { get; set; }
public string DeviceName { get; set; }
public bool HasUser { get; set; }
public int UserCount { get; set; }
}
public DeviceIndex()
{
AddMap<User>(users => from u in users
from deviceId in u.DeviceIds
let d = LoadDocument<Device>(deviceId)
select new Result
{
Id = d.Id,
HasUser = true,
UserCount = 1,
DeviceName = d.Name,
});
AddMap<Device>(devices => from d in devices
select new Result
{
Id = d.Id,
HasUser = false,
UserCount = 0,
DeviceName = d.Name,
});
Reduce = results => from result in results
group result by new { result.Id } into g
select new Result
{
Id = g.First().Id,
DeviceName = g.First().DeviceName,
HasUser = g.Any(e => e.HasUser),
UserCount = g.Sum(e => e.UserCount),
};
}
}
and you can call it like this
var result = await _session.Query<DeviceIndex.Result, DeviceIndex>().ToListAsync();
If you would have a Users List in the Device class List<string> Users
a list that contains the document ids from the Users collection then you could Index these Related documents.
See:
https://demo.ravendb.net/demos/csharp/related-documents/index-related-documents
Or do the opposite,
Create an index on the Users collection, and index the related Device info
Without changing current models,
You can create a Multi-Map Index to index data from different collections.
https://ravendb.net/docs/article-page/4.2/csharp/indexes/multi-map-indexes
https://ravendb.net/docs/article-page/4.2/csharp/studio/database/indexes/create-multi-map-index
https://ravendb.net/learn/inside-ravendb-book/reader/4.0/10-static-indexes-and-other-advanced-options#querying-many-sources-at-once-with-multimap-indexes

How do I return a List to JSON along with hard coded roots

I'm not entirely sure how to define this question but basically I'm developing an ASP.Net application where I am generating a JsonResult called IndexJson.
My code is as follows:
public JsonResult IndexJson()
{
var contacts = (from x in db.ContactSet
select new
{
x.AccountId,
x.FirstName,
x.LastName,
x.FullName,
x.JobTitle,
x.ParentCustomerId,
x.EMailAddress1,
x.Telephone1,
x.MobilePhone,
x.Fax,
x.GenderCode,
x.BirthDate
}).ToList();
return Json(contacts, JsonRequestBehavior.AllowGet);
}
This works well at returning the following JSON:
[{/*contact info*/}, {/*contact info*/}, {/*contact info*/}, ...]
But now I want to return the following JSON (hard-coded for now, I will change the values later):
{
"current": 1,
"rowCount": 10,
"rows": [{/*contact info*/}, {/*contact info*/}, {/*contact info*/}, ...],
"total": 1123
}
How can I adapt my code to do that?
Simply wrap all into new anonymous object
return Json(new { current = 1, rowCount = 10, rows = contacts, total = 1123 },
JsonRequestBehavior.AllowGet
);
Looks like you're using a jQuery grid like jQuery Bootgrid at least by the look of the output you need.
If thats the case, you can do the following.
1 Create the Data Types you need
Input that comes to the controller
public class RequestData
{
public int current { get; set; }
public string rowCount { get; set; }
/*Any other fields that come from the api*/
}
The output you need
public class ResponseData<T> where T : class
{
public int current { get; set; } // current page
public int rowCount { get; set; } // rows per page
public T rows { get; set; } // items
public int total { get; set; } // total rows for whole query
}
2 Put everything together
public JsonResult IndexJson(RequestData model)
{
var contacts = (from x in db.ContactSet
select new
{
x.AccountId,
x.FirstName,
x.LastName,
x.FullName,
x.JobTitle,
x.ParentCustomerId,
x.EMailAddress1,
x.Telephone1,
x.MobilePhone,
x.Fax,
x.GenderCode,
x.BirthDate
}).ToList();
var tResult =
ResponseData<YourObjectType>()
{
current = model.current,
rowCount = model.rowCount,
rows = contacts,
total = contacts.Count
};
return Json(tResult, JsonRequestBehavior.AllowGet);
}
You might like to define a (generic) type to store your extra fields and the list of items
public class ItemsWithTotal<T> // for pete sake find a better name!
{
public int Current {get; set; }
public int RowCount {get; set; }
public int Total {get; set; }
public List<T> Rows {get; set; }
}
Usage would be
var contacts = (from x in db.ContactSet
select new
{
x.AccountId,
x.FirstName,
x.LastName,
x.FullName,
x.JobTitle,
x.ParentCustomerId,
x.EMailAddress1,
x.Telephone1,
x.MobilePhone,
x.Fax,
x.GenderCode,
x.BirthDate
}).ToList();
var result = new ItemsWithTotal<ContactSet>(){
Current = 1,
RowCount = 10,
Total = 1123,
Rows = contacts
}
return Json(result);
You could create a viewmodel that looks the same. ie
public MyReturnModel{
public int Current {get; set;}
public int rowCount {get; set;}
public List<contacts> rows {get; set;}
public int total {get; set;}
}
Then in your method, just assign each property appropriately and return the JSONify the new viewmodel and return it.

Using Lambda to insert derived attribute into IQueryable dataset

I have the following query:
IQueryable<BarcodeQuery> barcodes = db.Barcodes.Select(b => new BarcodeQuery
{
id = b.id,
category_id = b.category_id,
...
checkout = b.Checkouts.Select(c => new CheckoutChild
{
id = c.id,
loanee_id = c.loanee_id,
...
})
.Where(c => c.datein == null)
.FirstOrDefault()
});
And so on. It's based on this model:
public class BarcodeQuery
{
public int id { get; set; }
public int category_id { get; set; }
...
public CheckoutChild checkout { get; set; }
public CheckoutStatus checkoutStatus { get; set; }
}
My question is about CheckoutStatus down there at the bottom. It looks like this:
public class CheckoutStatus
{
public string status { get; set; }
public int daysUntilDue { get; set; }
public int daysOverdue { get; set; }
}
All of those values are derived from information I get from the query--none of them are in the database itself. What is the best way of inserting the CheckoutStatus values into each barcode record?
I have a function that creates the CheckoutStatus values themselves, I just don't know how to get them into the barcode records.
Thanks!
If b has just be created with new, how can b.Checkouts contain something? I do not really understadn what you are trying to do.
EF is converting the lambda expression into a SQL statement. Therefore you can only use expressions that can actually be translated to SQL. Just query the barcodes from the DB and then add the missing information to the barcodes returned in a loop.
var barcodes = db.Barcodes.Select(...).ToList();
foreach (Barcode b in barcodes) {
b.Checkouts = ...
}

How Can I use ExpressionVisitor to Change expression, adding more properties?

I have a expression with two properties and, I need changing this expression at runtime to adding more items.
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
}
public class Program
{
public void Main()
{
var query1 = db.product.Select(s => new ProductViewModel
{
Id = s.Id,
Name = s.Name
});
// Here I need pass this query to a ExpressionVisitor.
}
}
After to pass the query1 to visitor, I hope that return to me a expression:
s => new ProductViewModel { Id = s.Id, Name = s.Name, BarCode = s.BarCode }
When the BarCode result, need return a default value, for exemple: string.Empty
How I can do it? I searched any examples on the internet, but I found nothing.

LINQ - append MemberBinding expression into exist MemberInit expression

Basic idea is similar to Merging Expression Trees to Reuse in Linq Queries.
In my situation, I have two models and DTOs:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public Extra Extra { get; set; }
}
public class Extra
{
public int Id { get; set; }
public string Text { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string Name { get; set; }
public ExtraDto Extra { get; set; }
}
public class ExtraDto
{
public int Id { get; set; }
public string Text { get; set; }
}
and expressions:
Expression<Func<Extra, ExtraDto>> extraSelector = o => new ExtraDto
{
Id = o.Id,
Text = o.Text
};
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name
};
Now, I'd like to 'append' extraSelector into userSelector. The pseudo code is like:
var selectorExpression = userSelector.Append(user => user.Extra, extraSelector);
Context.Users.Select(selectorExpression).ToList();
The final expression would be like this:
Expression<Func<User, UserDto>> userSelector = o => new UserDto
{
Id = o.Id,
Name = o.Name,
Extra = new ExtraDto
{
Id = o.Extra.Id,
Text = o.Extra.Text
}
};
I've tried using ExpressionVisitor, but no luck.
Apart from the "merge" of the two selectors, you have to insert the "path" o => o.Extra into the extraSelector and create a new "bind expression" for the property Extra of UserDto.
In fact, i'm playing around with such scenarios within this project, where i've tried to abstract this kind of expression plumbing. Your "merge" would then look like that:
userSelector = extraSelector.Translate()
.Cross<User>(o => o.Extra)
.Apply(o => o.Extra, userSelector);
The Translate extension method is just a little helper to make use of type inference, Cross inserts o => o.Extra into the extraSelector, Apply creates the "bind expression" for the property Extra of UserDto and finally merges the result with userSelector.

Categories