I'm trying to use Rx to write an asynchronous DataContext for linq-to-sql on Windows Phone 7.5. My idea is to define a method in the DataContext:
IObservable<List<Fact>> GetFacts(Func<MyDataContext, IQueryable<Fact>> selector)
{
var selectFacts = Observable.FromAsyncPattern<MyDataContext, IQueryable<Fact>>(selector.BeginInvoke, selector.EndInvoke);
return selectFacts(db).Select((query) => query.ToList());// db variable is MyDataContext instance, and is not null during the call or later
}
This method should then be called from the client code, like this:
var q = GetFacts((database) => from item in database.Facts select item)
.ObserveOnDispatcher()
.Do((facts) => MessageBox.Show(facts.Count.ToString()))
.Subscribe();
The problem I'm facing is quite strange. When the client selector (from item in database.Facts select item) is actually called, the database parameter in it's context is null! Therefore, I'm obviously getting the NullReferenceException. But when the selectFacts is called, the db value is non-null, and points to correct instance.
Are there any explanations to this fact? How to overcome it?
Thanks in advance.
I think you're going about this in a very strange way. If you want to run code in the background and have it return an IObservable<T> (like Task), you should use Observable.Start:
IObservable<List<Fact>> factsFuture = Observable.Start(
() => selectFacts(db).Select(query).ToList(),
Scheduler.ThreadPoolScheduler);
Related
i thought i got the hang of dexie, but now i'm flabbergasted:
two tables, each with a handful of records. Komps & Bretts
output all Bretts
rdb.Bretts.each(brett => {
console.log(brett);
})
output all Komps
rdb.Komps.each(komp=> {
console.log(komp);
})
BUT: this only outputs the Bretts, for some weird reason, Komps is empty
rdb.Bretts.each(brett => {
console.log(brett);
rdb.Komps.each(komp=> {
console.log(komp);
})
})
i've tried all kinds of combinations with async/await, then() etc, the inner loop cannot find any data in the inner table, whatever table i want to something with.
2nd example. This Works:
await rdb.Komps.get(163);
This produces an error ("Failed to execute 'objectStore' on 'IDBTransaction…ction': The specified object store was not found.")
rdb.Bretts.each(async brett => {
await rdb.Komps.get(163);
})
Is there some kind of locking going on? something that can be disabled?
Thank you!
Calling rdb.Bretts.each() will implicitly launch a readOnly transaction limited to 'Bretts' only. This means that within the callback you can only reach that table. And that's the reason why it doesn't find the Comps table at that point. To get access to the Comps table from within the each callback, you would need to include it in an explicit transaction block:
rdb.transaction('r', 'Komps', 'Bretts', () => {
rdb.Bretts.each(brett => {
console.log(brett);
rdb.Komps.each(komp=> {
console.log(komp);
});
});
});
However, each() does not respect promises returned by the callback, so even this fix would not be something that I would recommend either - even if it would solve your problem. You could easlily get race conditions as you loose the control of the flow when launching new each() from an each callback.
I would recommend you to using toArray(), get(), bulkGet() and other methods than each() where possible. toArray() is also faster than each() as it can utilize faster IDB Api IDBObjectStore.getAll() and IDBIndex.getAll() when possible. And you don't nescessarily need to encapsulate the code in a transaction block (unless you really need that atomicy).
const komps = await rdb.Komps.toArray();
await Promise.all(
komps.map(
async komp => {
// Do some async call per komp:
const brett = await rdb.Bretts.get(163));
console.log("brett with id 163", brett);
}
)
);
Now this example is a bit silly as it does the exact same db.Bretts.get(163) for each komp it founds, but you could replace 163 with some dynamic value there.
Conclusion: There are two issues.
The implicit transaction of Dexie's operation and the callback to each() lives within that limited transaction (tied to one single table only) unless you surround the call with a bigger explicit transaction block.
Try avoid to start new async operation within the callback of Dexie's db.Table.each() as it does not expect promises to be returned from its callback. You can do it but it is better to stick with methods where you can keep control of the async flow.
LINQ newbie here.
I have a long LINQ query, called it MYLONGQUERY, that returns a collection of certain class instances. If the list is not empty, I want to return a property (MYPROPERTY) of the first instance; otherwise it returns some default value (DEFAULTPROPERTY). So the query looks like this
(0 != MYLONGQUERY.count()) ? MYLONGQUERY.FirstOrDefault().MYPROPERTY: DEFAULTPROPERTY
This works fine. However, I don't like the fact that I have to repeat MYLONGQUERY before and after "?". I have been trying Let and Into, but have not been able to get those to work. And it has to be Method Syntax, not Query Syntax. Suggestions? Appreciate it.
You have to select the property first, then you can specify the default-value with DefaultIfEmpty:
var prop = MYLONGQUERY
.Select(x => x.MYPROPERTY)
.DefaultIfEmpty(DEFAULTPROPERTY) // new default-value
.First(); // never exception
I want to do something like
from table1
where col5="abcd"
select col1
I did like
query_ = From g In DomainService.GetGEsQuery Select New GE With {.Desc = g.codDesc}
"This cause a runtime error, i tried various combinations but failed"
please help.
I'm assuming your trying to do this on the client side. If so you could do something like this
DomainService.Load(DomainService.GetGEsQuery().Where(g => g.codDesc == "something"), lo =>
{
if (lo.HasError == false)
{
List<string> temp = lo.Entities.Select(a => a.Name).ToList();
}
}, null);
you could also do this in the server side (which i would personally prefer) like this
public IQueryable<string> GetGEStringList(string something)
{
return this.ObjectContext.GE.Where(g => g.codDesc == something).Select(a => a.Name);
}
Hope this helps
DomainService.GetGEsQuery() returns an IQueryable, that is only useful in a subsequent asynchronous load. Your are missing the () on the method call, but that is only the first problem.
You can apply filter operations to the query returned using Where etc, but it still needs to be passed to the Load method of your domain context (called DomainService in your example).
The example Jack7 has posted shows an anonymous callback from the load method which then accesses the results inside the load object lo and extracts just the required field with another query. Note that you can filter the query in RIA services, but not change the basic return type (i.e. you cannot filter out unwanted columns on the client-side).
Jack7's second suggestion to implement a specific method server-side, returning just the data you want, is your best option.
when using a MVC 3 collection that uses IEnumerable, is there a way to make small queries work to find single values? I've been experimenting with little success.
I have a collection of settings that have descriptions and settings. The problem is exposing one of them, as each is unique. I've tried something like this:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
With little success (unless I'm missing something). Is it possible to select one item from an enumerable collection and either pass it to a ViewBag or ViewData object? What I would like to do is something like the following:
var appl = _service.GetSettings(id, app); //Call to service layer & repository
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login")));
And pass this to the view, since I have a view that now combines a collection with single values.
The following seems to work within the view:
Application Name #Html.TextBox("application_name", #Model.FirstOrDefault().app_name)
But I'm not sure if this violates separation of concerns. Am I on the wrong path here? Thank you!
EDIT: Here is what I needed. The answers below got me very very close +1 +1
ViewBag.Login = appl.Where(a => a.setting_name.StartsWith("Login")).FirstOrDefault().setting_value
ViewBag.Login = appl.Select(a => a.setting_value.Where(a.setting_name.StartsWith("Login"))).FirstOrDefault();
This will select the first object that matches your criteria and return a single result or null
var appl = _service.GetSettings(id, app);
ViewBag.Login = appl
.Where(a => a.setting_name.StartsWith("Login"))
.FirstOrDefault();
I have C# code to populate a dropdown list in Silverlight which works fine except when there are duplicates. I think because IEnumerable<Insurance.Claims> is a collection, it filters out duplicates. How would I code my LINQ query to accept duplicates?
My Sample Data looks like:
Code => CodeName
FGI Field General Initiative
SRI Static Resource Initiative
JFI Joint Field Initiative - This is "overwritten" in results
JFI Joint Friend Initiative
IEnumerable<Insurance.Claims> results;
// ADO.NET Data Service
var claim = (from c in DataEntities.Claims.Expand("Claimants").Expand("Policies")
where c.Claim_Number == claimNumber
select c);
DataServiceQuery<Insurance.Claims> dataServiceQuery =
claim as DataServiceQuery<Insurance.Claims>;
dataServiceQuery.BeginExecute((asyncResult) =>
{
results = dataServiceQuery.EndExecute(asyncResult);
if (results == null)
{
// Error
}
else
{
// Code to populate Silverlight form
}
});
(Not sure if you're still struggling with this but anyway...)
I'm pretty sure it's not the IEnumerable interface but the actual drop down that is causing this behaviour. The code is being used as the key, and so obviously each time the same code is encountered, the item is being overwritten.
I don't think you can override this unless you change the code, or use another identifier as the key field in the dropdown.
You may want to add a try-catch block around dataServiceQuery.EndExecute(asyncResult) to properly handle errors.