RIA DomainService IQueryable - Select - c#-4.0

Following a couple different tutorials, I've been trying to build a "Silverlight Business Application" against a database I've created. I've found I have two problems. The one I'm asking about here is how to filter the query.
The query that is built in the DomainService is when using the VS2010 template is:
[EnableClientAccess]
public class ChargesService : LinqToEntitiesDomainService<ChargesEntities>
{
public IQueryable<tblChargeCode> GetCharges()
{
return ObjectContext.tblChargeCodes.OrderBy(e => e.Chrgs_Code_01).Take(10);
}
}
I'm trying to create another query against the same ObjectContext.tblChargeCodes. Pulling the entire table (30 columns by ~7k rows) creates a timeout error.
I can't figure out how to do a select. I want to select Charge_Codes_01 and Bill_Description with a "starts with" type functionality (dynamic drop down search functionality). I've tried different variations of this without success. Something just isn't clicking in my brain.
public IQueryable<tblChargeCode> SearchCharges(string num)
{
var min = System.Convert.ToInt32(num.PadRight(7, '0'));
var max = System.Convert.ToInt32(num.PadRight(7, '9'));
return ObjectContext.tblChargeCodes
.Select(e => e.Chrgs_Code_01, e.Chrgs_Code_01_Desc)
.Where(e => e.Chrgs_Code_01 >= min && e.Chrgs_Code_01 <= max)
.OrderBy(e => e.Chrgs_Code_01)
.Take(10);
}

(sorry for my bad english)
The problem with you code is the "Select" - your method signature says that it must return a IQueryable of tblChargeCode, so you cannot return a projection. Here is two ways you can write your query:
In the server:
public IQueryable<tblChargeCode> SearchCharges(int min, int max, string description)
{
return ObjectContext.tblChargeCodes
.Where(e => e.Chrgs_Code_01 >= min && e.Chrgs_Code_01 <= max)
.Where(e => e.Bill_Description.StartsWith(description))
.OrderBy(e => e.Chrgs_Code_01)
.Take(10);
}
And call it on the client:
context.Load(context.SearchChargesQuery(0, 9999999, "Bill"), (op) =>
{
//op.Entities has your entities loaded
}, null);
Or you can just leave the query on the server:
public IQueryable<tblChargeCode> GetCharges()
{
return ObjectContext.tblChargeCodes.OrderBy(e => e.Chrgs_Code_01);
}
And call it from the client (it will filter on the server)
context.Load(context.GetChargesQuery().Where(e => e.Chrgs_Code_01 >= 0 && e.Chrgs_Code_01 <= 9999999)
.Where(e => e.Bill_Description.StartsWith("Bill"))
.OrderBy(e => e.Chrgs_Code_01)
.Take(10), (op) =>
{
//op.Entities has your entities loaded
}, null);
You also can use "Contains" instead of "StartsWith" in your query.

Related

Am I paginating through a huge Firebase collection the right way?

I am trying to find a way to iterate all objects from a large collection of data in Firebase Database.
My best attempt follows but I found it odd for several reasons:
startAt() values are always inclusive. So after fetching 100 elements, I had to use my last fetched key as an argument to startAt which results in the last item being fetched again
DataSnapshot's forEach method doesn't allow a callback with an index count as you would think it would based on JS's standards so I had to create a manual index - not sure it will work in every case as i'm not sure if forEach works perfectly synchronously
Here is my code, given the assumption my collection is located at users.
const mapAllTripsPaginated = function (database, childSnapshotCallback, start = '', limit = 100, totalNb = 0) {
return database.ref('/users').orderByKey().startAt(start).limitToFirst(limit).once('value').then((snapshot) => {
let childrenPromises = []
let lastChildKey = null
let idx = 0
snapshot.forEach((childSnapshot) => {
lastChildKey = childSnapshot.key
if (start !== '' && idx === 0) {
// console.log(`Skipping ${childSnapshot.key} as 1st element of page`)
} else {
childrenPromises.push(childSnapshotCallback(childSnapshot))
}
idx = idx + 1
})
return Promise.all(childrenPromises)
.then((result) => {
let newTotal = totalNb + result.length
if (snapshot.numChildren() === limit) {
console.log(`Paginating from ${lastChildKey}`)
return mapAllTripsPaginated(database, childSnapshotCallback, start = lastChildKey, limit = limit, totalNb = newTotal)
} else {
// Done paginating
return newTotal
}
})
})
}
Any idea on how I could make this method more elegant?
Firebase queries are inclusive both for their start and end conditions. You will indeed have to deduplicate the overlapping item on the client.
Firebase's Snapshot.forEach() is a synchronous operation.
I'd normally deduplicate based on already having the key of the item. That will also remove the need for the idx counter.
snapshot.forEach((childSnapshot) => {
if (lastChildKey !== childSnapshot.key) {
childrenPromises.push(childSnapshotCallback(childSnapshot))
}
lastChildKey = childSnapshot.key
})

NHibernate queryover how to apply date isbetween without including the from and todate

I am trying to write NHibernate queryover to select all records which has been deleted between two dates. I am using IsBetween().And(). But how do i write if i dont want to include both the fromdate and todate?
Here is my query:
public IEnumerable<DeletedRecord> Search(
DateTime deletedFrom,
DateTime deletedTo
)
{
DeletedRecord delAlias = null;
var query = Session.QueryOver(() => delAlias);
query.Where(() => delAlias.DeletedDate.IsBetween(deletedFrom).And(deletedTo));
return query.Future<DeletedRecord>();
}
Can anyone help me how to achieve so that i can bring all records after the deletedFrom date and before the deletedTo date?
Thanks
Just construct your date in 2 steps:
var query = Session.QueryOver(() => delAlias);
if(youNeedFromDate) //first step
query = query.Where(() => delAlias.DeletedDate >= deletedFrom);
if(youNeedToDate) //second step
query = query.Where(() => delAlias.DeletedDate <= deletedTo);
youNeedFromDate and youNeedToDate are bool variables that you can pass to your function or it could be different condition upon your logic.
Such cases happen quite often . defining extension method helps a lot. see below
public static IQueryOver<T, T> WhereIf<T>(this IQueryOver<T, T> query,bool condition, Expression<Func<T, bool>> expression) where T : class
{
if (condition)
{
query = query.And(expression);
}
return query;
}
Using the above you can chain you conditions and only it will only include the where condition if the first arg evaluates to true.
var query= Session.QueryOver<DeletedRecord>()
.WhereIf(filterByFrom,d=>d.DeletedDate>=fromDate)
.WhereIf(filterByTo,d=>d.DeletedDate<=toDate);

How i can get latest record by using FirstOrDefault() method

Suppose i have 2 records in data base
1) 2007-12-10 10:35:31.000
2) 2008-12-10 10:35:31.000
FirstOrDefault() method will give me the first record match in sequence like 2007-12-10 10:35:31.000 but i need the latest one which is 2008-12-10 10:35:31.000
if ((from value in _names where value != null select value.ExpiryDate < now).Any())
{
return _names.FirstOrDefault();
}
You can use:
return _names.LastOrDefault();
However, your if just sends another unnecessary query (and it is a wrong query too). If you don't have any record, LastOrDefault and FirstOrDefault will return null. You can use something like this to improve the code:
var name = _names.LastOrDefault();
if(name != null)
{
return name;
}
// other code here
If you really want to use FirstOrDefault, you should order descending, like:
var name = _names.Where(n => n.ExpiryDate < now).OrderByDescending(n => n.ExpiryDate).FirstOrDefault();

ServiceStack Ormlite SqlExpressionVisitor null check in Where extension

I would like to write a method for querying table with one method by null cheking parameters using SqlExpressionVisitor of Ormlite
Here is my method :
public static List<UserChatsDTO> GetUserChats(int startRow, int rowCount, DateTime? startDate, DateTime? endDate, string operatorName, short? rating, string visitorName)
{
using (IDbConnection db = DbFactory.OpenDbConnection())
{
SqlExpressionVisitor<UserChatsDTO> ev = OrmLiteConfig.DialectProvider.ExpressionVisitor<UserChatsDTO>();
ev.Where(q =>
(startDate.HasValue && q.Dated >= startDate) &&
(endDate.HasValue && q.Dated <= endDate) &&
(!string.IsNullOrEmpty(operatorName) && q.TakenByUser.Contains(operatorName)) &&
(rating.HasValue && q.Rating == (short)rating) &&
(!string.IsNullOrEmpty(visitorName) && q.VisitorName.Contains(visitorName)));
//ev.OrderBy();
ev.Limit(startRow, rowCount);
return db.Select<UserChatsDTO>(ev);
}
}
But Object reference not set to an instance of an object. NullReferenceException is thrown when i call ev.Where part.
Is there a bug here or i am missing something ?
Thank you.
You can actually build up the ExpressionVisitor inside the Select method like so:
var chats = db.Select<UserChatsDTO>(q => q
.Where(x => startDate.HasValue && x.Date >= startDate)
.Where(x => endDate.HasValue && x.Date <= endDate)
.Where(x => string.IsNullOrEmpty(operatorName) || x.TakeByUser.Contains(operatorName))
.Where(x => rating.HasValue && x.Rating == (short)rating)
.Where(x => string.IsNullOrEmpty(visitorName) || x.VisitorName.Contains(visitorName)
.Limit(startRow, rowCount));
I know this question is 7 months old but I had a similar issue & this was the first question that came up when I searched. I wanted to add my working solution since Master Morality's didn't work for me.
Originally, I tried syntax roughly similar to mustafasturan's first attempt. I got the same NullReferenceException he did. Master Morality's answer did not help either...
I'm trying to build a search function that performs LIKE searches rather than exact match. There are multiple criteria on the request object which may or may not be null (for simplicity's sake we'll use an example with 2 criteria). Following Master Morality's suggestion, I tried this:
var searchResults = db.Select<Item>(q => q
.Where(x => string.IsNullOrWhiteSpace(request.Criteria1) || x.Criteria1.Contains(request.Criteria1))
.Where(x => string.IsNullOrWhiteSpace(request.Criteria2) || x.Criteria2.Contains(request.Criteria2))
);
I still got a NullReferenceException. Seems like the && and || operators are not using short-circuit evaluation inside the lambda expression.
What I finally had to go with is this:
SqlExpressionVisitor<Item> ev = new ServiceStack.OrmLite.MySql.MySqlExpressionVisitor<Item>();
if (!String.IsNullOrWhiteSpace(request.Criteria1)) {
ev.Where(q => q.Criteria1.Contains(request.Criteria1));
}
if (!String.IsNullOrWhiteSpace(request.Criteria2)) {
ev.Where(q => q.Criteria2.Contains(request.Criteria2));
}
searchResults = db.Select<Item>(ev);
It doesn't feel very elegant, but it's the only thing I could find that works.

Symfony2 Entity form type with a specific query_buider

Context
In my case, I've some orders with "discount vouchers" (discount). A discount can be use on under different conditions. For instance, discounts have an expired date, can be used by a limited number of customers, can be dedicated to a user, ...
Each discount can be attached to several order.
In my backoffice, I want to add to order create form a field "Discount" with a list of discount available but only right discounts.
What I made
An entity "order" with a field manyToMany
/**
* #ORM\ManyToMany(targetEntity="PATH\MyBundle\Entity\Discount", inversedBy="orders")
* #ORM\JoinTable(name="shop_discounts_orders",
* joinColumns={#ORM\JoinColumn(name="order_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="discount_id", referencedColumnName="id")}
* )
*/
private $discounts;
An entity "discounts" with a field manyToMany
/**
* #ORM\ManyToMany(targetEntity="PATH\MyBundle\Entity\Order", mappedBy="discounts")
*/
private $orders;
A form OrderType with a field discounts
$builder->add('discounts', 'entity',
array( 'label' => 'Discount vouchers',
'required' => false,
'expanded' => true,
'class' => 'PATH\MyBundle\Entity\Discount',
'property' => 'title',
'multiple' => true,
'query_builder' => function(EntityRepository $er) use ($params) {
return $er->getQuerySelectType($params);
},
));
With this solution, I can return specific discount defined by my request in my entity repository. It's good for expired date condition for instance.
What I would like
I'd like to filter results in the checkbox list. In fact, I want limit usage of the discount to a dedicated user, limit to a list of products, or limit the number of usage... And these condition cannot be done by a simple sql request.
I try to create special Type. My idea is to have an array of entities Discount and load a choice list... After that, I create a dataTransformer but It doesn't work !
Thank's for your ideas !
You could use the $options from public function buildForm(FormBuilderInterface $builder, array $options) to pass your user and product for instance. With those 2 informations you could refine your list of discount (in your query)
if you do so you need to add them in the setDefaultValue
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'user_discount' => null,
'product_discount' => null,
));
}
and in your controller:
$form = $this->formFactory->create(new YourFormType(), $entity, array(
'user_discount' => $this->getUser(),
'product_discount' => $product,
));
I found a solution and explain it if someone have the same issue as me.
Create a custom Type
My custom type is inspired by Symfony\Bridge\Doctrine\Form\Type\DoctrineType
class DiscountOrderType extends AbstractType
{
// overide choiceList callback
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$choiceListCache =& $this->choiceListCache;
$type = $this;
$choiceList = function (Options $options) use (&$choiceListCache, &$time, $container) {
[[ Copy paste same as Doctrine type ]]
// Create your own choiceList class (EntityChoiceList)
if (!isset($choiceListCache[$hash])) {
$choiceListCache[$hash] = new DiscountChoiceList(
$options['em'],
$options['class'],
$options['property'],
$options['loader'],
$options['choices'],
$options['group_by']
);
// If you want add container
$choiceListCache[$hash]->setContainer($container);
}
return $choiceListCache[$hash];
};
$resolver->setDefaults(array(
'choice_list' => $choiceList,
));
}
Create a custom EntityChoiceList
My custom type is inspired by Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList
class EntityChoiceList extends ObjectChoiceList
{
protected function load()
{
if ($this->entityLoader) {
$entities = $this->entityLoader->getEntities();
} else {
$entities = $this->em->getRepository($this->class)->findAll();
}
// You have access to the entities in the choice list
// Add your custom code here to manipulate the choice list
// you can do some check not properly possible with sql request (http requests on each result, ...) before add it in choice list
// you can add some custom cache rules, ...
// if you use gedmon and want apply a "join" with translate table, you can add $query->setHint(\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'); before playing request...
// Possibilities are infinite
// FOR INSTANCE : you already want unset first entity of the result
if (isset($entities[0])) {
unset($entities[0]);
}
// END OF CUSTOM CODE
try {
// The second parameter $labels is ignored by ObjectChoiceList
// The third parameter $preferredChoices is currently not supported
parent::initialize($entities, array(), array());
} catch (StringCastException $e) {
throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e);
}
$this->loaded = true;
}
Of course you can try to extend symfony class for beautyfull code ;).
Thank's to #maxwell2022 for your help !

Resources