I want to use Realm to replace SqLite in Android to store a list of classes, my code is very simple as below.
public class MyRealmObject extends RealmObject {
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
private String field;
...
}
List<MyObject> myObjects = new ArrayList();
Realm realm = Realm.getInstance(this);
for(MyRealmObject realm : realm.allobjects(MyRealmObject.class)) {
myObjects.add(new MyObject(realm));
}
realm.close();
return myObjects;
However, its performance is actually slower than a simple SqlLite table on my tested device, am I using it the wrong way? Is there any optimization tricks?
Why do you want to wrap all your RealmObjects in the MyObject class?. Especially copying the entire result set means you will loose the benefit of using Realm, namely that it doesn't copy data unless needed to.
RealmResults implements the List interface so you should be able to use the two interchangeably.
List<MyRealmObject> myObjects;
Realm realm = Realm.getInstance(this);
myObjects = realm.allObjects(MyRealmObject.class();
return myObjects;
Related
Recently I've started using shell and I noticed an improvement in page transitioning and a bug I was facing using NavigationPage was fixed just by replacing it with shell.
So I was excited to use it.
However soon after I realized I can't send objects from page to page through shell like I could using a constructer of a page. I searched a bit and now know that shell passes strings only. I turned the object into JSON but then faced an exception due to long URI length.
Honestly, I am disappointed. I thought something this important would be implemented in shell... but In any case, how do you guys work around this?
For Maui.
See (Xamarin) Process navigation data using a single method.
Also mentioned in maui issue. Adapting the Maui invocation there:
await Shell.Current.GoToAsync("//myAwesomeUri",
new Dictionary { {"data", new MyData(...)} });
This uses IQueryAttributable and ApplyQueryAttributes to pass an IDictionary<string, object> query.
(The Xamarin example shows IDictionary<string, string>, but its , object in Maui, so you can pass any object values.)
Thus the string parameters you pass can be used to look up corresponding objects.
From that (Xamarin) doc (modified to show looking up an object):
public class MonkeyDetailViewModel : IQueryAttributable, ...
{
public MyData Data { get; private set; }
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
Data = (MyData)query["data"];
}
...
}
For Xamarin Forms, the limitation to string values makes this a bit ugly. One approach is to have a static that holds possible objects, which you look up using a string. This is tolerable when the objects are all pre-defined, but is a bit clumsy if you are manually altering those objects.
public class MonkeyDetailViewModel : IQueryAttributable, ...
{
public static Dictionary<string, MyData> KeyedData;
// "static": One-time class constructor.
public static MonkeyDetailViewModel()
{
KeyedData = new Dictionary<string, MyData>();
KeyedData["data1"] = new MyData(...);
// ... other versions of the data ...
}
public MyData Data { get; private set; }
public void ApplyQueryAttributes(IDictionary<string, string> query)
{
string whichData = query["data"]; // In example, gets "data1".
Data = KeyedData[whichData];
}
...
}
Usage:
await Shell.Current.GoToAsync("//myAwesomeUri",
new Dictionary { {"data", "data1"} });
Xamarin NOTE: The static dictionary makes it possible to maintain multiple instances of MyData. The "hack" alternative is to have MyData Data be static, and explicitly set it before GoToAsync - but this is risky if you ever might have a MonkeyDetailView on nav stack, go to a second one, then go back to first one - you'll have overwritten the Data seen by the first view.
My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.
Could someone give a simple use case example why someone would use copyToRealm() instead of createObject() ?
It is not clear to me why and when would anyone use copyToRealm() if there is createObject().
In the example here they seem pretty much the same https://realm.io/docs/java/latest/ .
copyToRealm() takes an unmanaged object and connects it to a Realm, while createObject() creates an object directly in a Realm.
For example it is very useful when you copy objects generated by GSON - returned from your Rest API into Realm.
realm.createObject() also returns a RealmProxy instance and is manipulated directly and therefore creates N objects to store N objects, however you can use the following pattern to use only 1 instance of object to store N objects:
RealmUtils.executeInTransaction(realm -> {
Cat defaultCat = new Cat(); // unmanaged realm object
for(CatBO catBO : catsBO.getCats()) {
defaultCat.setId(catBO.getId());
defaultCat.setSourceUrl(catBO.getSourceUrl());
defaultCat.setUrl(catBO.getUrl());
realm.insertOrUpdate(defaultCat);
}
});
But to actually answer your question, copyToRealmOrUpdate() makes sense if you want to persist elements, put them in a RealmList<T> and set that RealmList of newly managed objects in another RealmObject. It happens mostly if your RealmObject classes and the downloaded parsed objects match.
#JsonObject
public class Cat extends RealmObject {
#PrimaryKey
#JsonField(name="id")
String id;
#JsonField(name="source_url")
String sourceUrl;
#JsonField(name="url")
String url;
// getters, setters;
}
final List<Cat> cats = //get from LoganSquare;
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Person person = realm.where(Person.class).equalTo("id", id).findFirst();
RealmList<Cat> realmCats = new RealmList<>();
for(Cat cat : realm.copyToRealmOrUpdate(cats)) {
realmCats.add(cat);
}
person.setCats(realmCats);
}
});
I am trying to use a projection to pull in data from an entity an some relations it has. However. The constructor on the projection takes three arguments; a set, integer and another integer. This all works fine if I don't have the set in there as an argument, but as soon as I add the set, I start getting SQL syntax query errors.
Here is an example of what I'm working with...
#Entity
public class Resource {
private Long id;
private String name;
private String path;
#ManyToOne
#JoinColumn(name = "FK_RENDITION_ID")
private Rendition rendition;
}
#Entity
public class Document {
private Long id;
private Integer pageCount;
private String code;
}
#Entity
public class Rendition {
Long id;
#ManyToOne
#JoinColumn(name="FK_DOCUMENT_ID")
Document doc;
#OneToMany(mappedBy="rendition")
Set<Resource> resources;
}
public class Projection {
#QueryProjection
public Projection(Set<Resource> resources, Integer pageCount, String code) {
}
}
Here is the query like what I am using (not exactly the same as this is a simplified version of what I'm dealing with)....
QRendition rendition = QRendition.rendition;
Projection projection = from(rendition)
.where(rendition.document().id.eq(documentId)
.and(rendition.resources.isNotEmpty())
.limit(1)
.singleResult(
new QProjection(rendition.resources,
rendition.document().pageCount,
rendition.document().code));
This query works fine as long as my projection class does not have the rendition.resources in it. If I try and add that in, I start getting malformed SQL errors (it changes the output sql so that it starts with this.
select . as col_0_0_
So, I guess my main question here is how do I include a Set as an object in a projection? Is it possible, or am I just doing something wrong here?
Using collections in projections is unreliable in JPA. It is safer to join the collection and aggregate the results instead.
Querydsl can also be used for result aggregation http://www.querydsl.com/static/querydsl/3.2.0/reference/html/ch03s02.html#d0e1799
In your case something like this
QRendition rendition = QRendition.rendition;
Projection projection = from(rendition)
.innerJoin(rendition.document, document)
.innerJoin(rendition.resources, resource)
.where(document.id.eq(documentId))
.limit(1)
.transform(
groupBy(document.id).as(
new QProjection(set(resources),
document.pageCount,
document.code)));
I have an ASP.NET website project that until recent had all code in App_Code folder. It uses Entity Framework 4 as ORM. Application is divided into three "sections" (let's say one for each customer). Each section has it's own database (but same schema). This is due to performance reasons, databases are over 10GB each with millions of rows.
Each time a context object is created a Session variable which holds section ID is called and proprietary connection string is chosen for this context.
It looks like this (following are members of static Connection class):
public static MyEntities GetEntityContext()
{
if (HttpContext.Current.Session["section"] == null)
{
HttpContext.Current.Response.Redirect("~/Login.aspx");
}
var context = new MyEntities(GetEntityConnectionStringForSection((int)HttpContext.Current.Session["section"]);
return context;
}
private static string GetEntityConnectionStringForSection(int section)
{
switch (section)
{
case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
}
}
It works very good and also handles situation when session timed out everytime any data access is performed.
Recently as I needed to share DB classes among two websites I moved all DB classes to separate class library and referenced System.Web library which I know is bad practice, but it's working.
Now the next step is to include unit and module tests which as I read is very difficult or impossible when using HttpContext in library, so I want to get rid of System.Web references. What is the best practice for this situation?
I think I can't just pass HttpContext to GetEntityContext() as it is also called from within my entity classes. Although this probably can be refactored. So maybe this is where I should go?
I also wondered if is it possible to somehow pass current section ID to this whole library? It cannot be just static property because as far as I understand it would be common for all users using the application. This should be user-specific.
Reassuming the objective is to make automated testing possible without loosing transparent Connection String choosing and session timeouts handling.
If I do something fundamentally wrong at this stage please also let me know. I can look again at this question tomorrow morning (8.00 am UTC) so please don't be discouraged by my silence till then.
EDIT:
Example of usage of Connection class in the library:
public partial class Store
{
public static List<Store> GetSpecialStores()
{
using (var context = Connection.GetEntityContext())
{
return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
}
}
}
You can declare interface IContextProvider inside your library ans use it to retrieve context. Something like:
public interface IContextProvider
{
MyEntities GetEntityContext();
}
This will make your library testable. In your web project you can inject IContextProvider implementation into your library.
public class WebContextProvider : IContextProvider
{
public MyEntities GetEntityContext()
{
if (HttpContext.Current.Session["section"] == null)
HttpContext.Current.Response.Redirect("~/Login.aspx");
int sectionId = (int)HttpContext.Current.Session["section"];
string connectionString = GetEntityConnectionStringForSection(sectionId);
var context = new MyEntities(connectionString);
return context;
}
private static string GetEntityConnectionStringForSection(int section)
{
switch (section)
{
case 1: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
case 2: return ConfigurationManager.ConnectionStrings["entity_2"].ConnectionString;
case 3: return ConfigurationManager.ConnectionStrings["entity_3"].ConnectionString;
default: return ConfigurationManager.ConnectionStrings["entity_1"].ConnectionString;
}
}
}
Inject this interface to repositories or other data access classes.
public partial class Store
{
private IContextProvider contextProvider;
public Store(IContextProvider contextProvider)
{
this.contextProvider = contextProvider;
}
public List<Store> GetSpecialStores()
{
using (var context = contextProvider.GetEntityContext())
{
return context.Stores.Where(qq => qq.Type > 0).OrderBy(qq => qq.Code).ToList();
}
}
}