is it possible in Db4o to load new objects into persistent IObjectContainer?
I have a desktop application which opens one connection (IObjectContainer) when started. if I query all objects with:
var objects = from DummyClass foo in session
select foo
it selects all objects perfectly. However, if another client adds new classes after this, the same query still selects the same objects, without new ones.
I also know about:
session.Ext().Refresh(obj, int.MaxValue);
but I don't have even not activated references to new objects so there. How to refresh new objects?
Just note: I don't want to open/close session every time I need some data, I want to take advantage of OODB (Transparent activation, object persistance since loaded etc.)
Thank you
UPDATE (code example for better understanding)
// store one class to fill database with some data
using (var mainSession = SessionFactory.CreateNewConnection())
{
mainSession.Store(new DummyClass());
mainSession.Commit();
}
using (var mainSession = SessionFactory.CreateNewConnection())
{
// returns one object
var objects = from DummyClass foo in session
select foo;
using (var secondSession = SessionFactory.CreateNewConnection())
{
secondSession.Store(new DummyClass());
secondSession.Commit();
}
// this loop reload objects known for mainSession (which is not new object)
foreach (var obj in objects2)
{
mainSession.Ext().Refresh(obj, int.MaxValue);
}
// new DummyClass is commited but still not visible (Read-Commited isolation)
// returns one object
var objects2 = from DummyClass foo in session
select foo;
}
using (var mainSession = SessionFactory.CreateNewConnection())
{
// returns two objects
var objects = from DummyClass foo in session
select foo;
}
I need something like:
// refresh all objects of DummyClass
session.Ext().Refresh(typeof(DummyClass), int.MaxValue);
You may use Commited events:
using Db4objects.Db4o;
using Db4objects.Db4o.Events;
using Db4objects.Db4o.IO;
using Db4objects.Db4o.Ext;
namespace PushedUpdates
{
class Program
{
static void Main()
{
var config = Db4oEmbedded.NewConfiguration();
config.File.Storage = new MemoryStorage();
var container = Db4oEmbedded.OpenFile(config, "IN-MEMORY");
var client = container.Ext().OpenSession();
var clientEvents = EventRegistryFactory.ForObjectContainer(client);
clientEvents.Committed += (s, a) =>
{
foreach(IObjectInfo added in a.Added)
{
System.Console.WriteLine(added.GetObject());
}
};
container.Store(new Item { Value = 1 } );
container.Commit();
container.Store(new Item { Value = 2 });
container.Commit();
container.Store(new Item { Value = 3 });
container.Commit();
client.Close();
container.Close();
}
}
class Item
{
public int Value { get; set; }
public override string ToString()
{
return "" + Value;
}
}
}
Did your client call the commit() method after storing the data? Otherwise the new data will not be available for other clients.
Related
I'll try to resume everything as much as possible:
I have a class that creates a dictionary (let's call it secondary dictionary) with the initial values of some of the elements of another dictionary, (for simplicity, let's call it the main dictionary and this class does this only once) and then a thread that checks every x miliseconds against this main dictionary, that's being regularly updated every y miliseconds, for any changes on the elements that where stored in the initialization phase.
My problem is, that when I want to compare the values of element1 in the main dictionary against the value of element1 in the secondary dictionary, they're always the same, (I undersatnd that, until the main dictionary is nnot updated, both values will be the same, but when the main dictionary gets updated, so does the second, immediately, without me doing anything)
I've tried ConcurrentDictionaries, using locks, a combination of boths, creating a new element in the initialize functions instead of passing it directly, but nothing seems to work
class Checker
{
private static bool secondaryDictionaryHasChanged = false;
private static ConcurrentDictionary<int, ValueDTO> secondaryDictionary = new ConcurrentDictionary<int, ValueDTO>();
public Checker()
{
initSecondaryDictionary();
Thread checkChangedValsThread = new Thread(() => checkChangedValsThreadFunction(1000));
checkChangedValsThread.Start();
}
public void initSecondaryDictionary()
{
Object MainLock = new Object();
lock (MainLock)
{
Object secondaryLock = new Object();
lock (secondaryLock)
{
foreach (var variable in Maindictionary)
{
if (variable.isElegibleValue)
{
ValueDTO ValueDTOToAdd = new ValueDTO();
ValueDTOToAdd.EligibleVar = variable;
if (variable.contextVariables.Count > 0)
{
List<Var> contextVariablesToAdd = new List<Var>();
foreach (var item in variable.contextVariables)
{
contextVariablesToAdd.Add(getVarFromMainDictionary(item));
}
ValueDTOToAdd.ContextVars = contextVariablesToAdd;
}
secondaryDictionary.TryAdd(ValueDTOToAdd.EligibleVar.varCode, ValueDTOToAdd);
}
}
}
}
secondaryDictionaryHasChanged = false;
}
public void checkChangedValsThreadFunction(int checkTime)
{
while (true)
{
try
{
if (!secondaryDictionaryHasChanged)
{
Thread.Sleep(checkTime);
Object mainLock = new Object();
lock (mainLock)
{
Object secondaryLock = new Object();
lock (secondaryLock)
{
foreach (var item in secondaryDictionary)
{
ValueDTO secondaryDictionaryDTO = item.Value;
Var variableInSecondary = secondaryDictionaryDTO.EligibleVar;
Var variableInMain = getVarFromMainDictionary(item.Value.EligibleVar.varID);
int valueInMain = variableInMain.getIntValue();
int valueInSecondary = variableInSecondary.getIntValue();
if (valueInMain != valueInSecondary)
{
//IT NEVER ENTERS THIS HERE
}
}
}
}
}
}
catch (Exception e)
{
throw new Exception("Some exception: " + e.Message);
}
}
}
}
internal class ValueDTO
{
public Var EligibleVar { get; set; }
public List<Var> ContextVars { get; set; }
}
internal class Var
{
public int varCode { get; set; }
public string varID { get; set; }
}
I add a reduced version of the code I have the thing is that it will never go inside that if(mainValue != secondaryValue)
any help or info about where I'm going wrong will be deeply appreciated
As fildor already said, you have one object and two references to it.
To simplify your problem I created a short sample:
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Example
{
class Person
{
public string Name
{
get;
set;
}
}
public static void Main()
{
var myPerson = new Person();
myPerson.Name = "User1";
var list1 = new List<Person>();
var list2 = new List<Person>();
// put a reference to myPerson into each of the lists
list1.Add(myPerson);
list2.Add(myPerson);
// get the reference to myPerson from list1
var myPersonInList1 = list1[0]
myPersonInList1.Name = "User2";
// this will print User2 because there is only one person object
Console.WriteLine(list2[0].Name);
}
}
Basically no matter where you pass the Person, it will always be a reference to the Person instance you created. All changes to it will apply to the object behind the reference. All references point to the same object.
A bit more in depth knowledge very simplified, but I hope you'll get the point:
There is a stack and a heap storage. The stack contains value types and references and the heap contains objects.
Stack:
Variable | Value
--------------------
myPerson | #abc123
list1[0] | #abc123
list2[0] | #abc123
myInt | 42
Heap:
Address | Value
-------------------
abc123 | { Name: "User2" }
Now every time you create a variable pointing to myPerson, you only create a new entry in the stack, pointing to the same object in the heap. Now, of course when you update the object behind the reference and set its name to "User1", all references will display the change.
I would like AutoMapper to map same object instances Source to the same instances of Target.
I'm sure that AutoMapper can be configured to do this, but how? I need to convert an object graph A to B, keeping the references between the mapped objects.
For example, this fails:
[Fact]
public void Same_instances_are_mapped_to_same_instances()
{
var configuration = new MapperConfiguration(config => config.CreateMap<Source, Target>());
var mapper = configuration.CreateMapper();
var source = new Source();
var list = mapper.Map<IEnumerable<Target>>(new[] { source, source, source });
list.Distinct().Should().HaveCount(1);
}
public class Source
{
}
public class Target
{
}
It would like to make this test pass.
Also, please notice that the mapper should "remember" instances mapped during the current Map call.
That's the nearest you can get:
public static class Program
{
public static void Main()
{
var config = new MapperConfiguration(conf => conf.CreateMap<Source, Target>().PreserveReferences());
var mapper = config.CreateMapper();
var source = new Source();
var list = new[] { source, source, source };
var firstRun = mapper.Map<IEnumerable<Target>>(list);
var secondRun = mapper.Map<IEnumerable<Target>>(list);
// Returns two items
var diffs = firstRun.Concat(secondRun).Distinct();
foreach (var item in diffs)
{
Console.WriteLine(item.Id);
}
}
}
public class Source
{
public Guid Id { get; } = Guid.NewGuid();
}
public class Target
{
public string Id { get; set; }
}
This means, that you only get the same item back in case of each call to mapper.Map(), but if you call the mapper multiple times, you'll get back new items for each call (which makes sense, otherwise the mapper had to hold references to all given and created instances over it's whole lifetime, which could lead to some serious memory problems).
I'm currently implementing a MongoDB database for caching.
I've made a very generic client, with the save method working like this:
public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
if (data == null || !data.Any())
return;
var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
var filter = new FilterDefinitionBuilder<T>().Empty;
var operations = new List<WriteModel<T>>
{
new DeleteManyModel<T>(filter),
};
operations.AddRange(data.Select(t => new InsertOneModel<T>(t)));
try
{
collection.BulkWrite(operations, new BulkWriteOptions { IsOrdered = true});
}
catch (MongoBulkWriteException mongoBulkWriteException)
{
throw mongoBulkWriteException;
}
}
With our other clients, calling this method looking similar to this:
public Person[] Get(bool bypassCache = false)
{
Person[] people = null;
if (!bypassCache)
people = base.Get<Person>(DefaultCollectionKeys.People.CreateCollectionKey());
if (people.SafeAny())
return people;
people = Client<IPeopleService>.Invoke(s => s.Get());
base.SaveAndOverwriteExistingCollection(DefaultCollectionKeys.People.CreateCollectionKey(), people);
return people;
}
After we've persisted data to the backend we reload the cache from MongoDB by calling our Get methods, passing the argument true. So we reload all of the data.
This works fine for most use cases. But considering how we are using a Web-garden solution (multiple processes) for the same application this leads to concurrency issues. If I save and reload the cache while another user is reloading the page, sometimes it throws a E11000 duplicate key error collection.
Command createIndexes failed: E11000 duplicate key error collection:
cache.Person index: Id_1_Name_1_Email_1 dup
key: { : 1, : "John Doe", : "foo#bar.com" }.
Considering how this is a web garden with multiple IIS processes running, locking won't do much good. Considering how bulkwrites should be threadsafe I'm a bit puzzled. I've looked into Upserting the data, but changing our clients to be type specific and updating each field will take too long and feels like unnecessary work. Therefore I'm looking for a very generic solution.
UPDATE
Removed the Insert and Delete. Changed it to a collection of ReplaceOneModel. Currently experiencing issues with only the last element in a collection being persisted.
public virtual void SaveAndOverwriteExistingCollection<T>(string collectionKey, T[] data)
{
if (data == null || !data.Any())
return;
var collection = Connector.MongoDatabase.GetCollection<T>(collectionKey.ToString());
var filter = new FilterDefinitionBuilder<T>().Empty;
var operations = new List<WriteModel<T>>();
operations.AddRange(data.Select(t => new ReplaceOneModel<T>(filter, t) { IsUpsert = true }));
try
{
collection.BulkWrite(operations, new BulkWriteOptions { IsOrdered = true });
}
catch (MongoBulkWriteException mongoBulkWriteException)
{
throw mongoBulkWriteException;
}
}
Just passed in a collection of 811 items and only the last one can be found in the collection after this method has been executed.
Example of a DTO being persisted:
public class TranslationSetting
{
[BsonId(IdGenerator = typeof(GuidGenerator))]
public object ObjectId { get; set; }
public string LanguageCode { get; set; }
public string SettingKey { get; set; }
public string Text { get; set; }
}
With this index:
string TranslationSettings()
{
var indexBuilder = new IndexKeysDefinitionBuilder<TranslationSetting>()
.Ascending(_ => _.SettingKey)
.Ascending(_ => _.LanguageCode);
return MongoDBClient.CreateIndex(DefaultCollectionKeys.TranslationSettings, indexBuilder);
}
I am trying to create a Class Method which can be called to Query the Database. The function itself works but for some reason, when the Array is returned, they're not set.
My function code is:
public Configuration[] tbl_bus(string type, string match)
{
// Create Obejct Instance
var db = new rkdb_07022016Entities2();
// Create List
List<Configuration> ConfigurationList = new List<Configuration>();
// Allow Query
if (type.ToLower() == "bustype")
{
foreach (var toCheck in db.tblbus_business.Where(b => b.BusType == match))
{
// Create Class Instance
var model = new Configuration { Name = toCheck.Name, BusinessID = toCheck.BusinessID };
// Append to the property
ConfigurationList.Add(model);
}
}
else if (type.ToLower() == "businessid")
{
foreach (var toCheck in db.tblbus_business.Where(b => b.BusinessID == match))
{
// Create Class Instance
var model = new Configuration { Name = toCheck.Name, BusinessID = toCheck.BusinessID };
// Append to the property
ConfigurationList.Add(model);
}
}
return ConfigurationList.ToArray();
}
And my Configuration code is:
public class Configuration
{
// Properties of the Database
public string Name { get; set; }
public string BusinessID { get; set; }
public string Address { get; set; }
}
public Configuration Config { get; set; }
public Controller()
{
this.Config = new Configuration();
}
On my Handler I am doing:
// Inside the NameSpace area
Controller ctrl;
// Inside the Main Void
ctrl = new Controller();
ctrl.tbl_bus("bustype", "CUS");
context.Response.Write(ctrl.Config.Name);
I tried watching the Class function and it does create the Array, only, when I watch the ctrl.Config.Name it is always set to NULL. Could anyone possibly help me in understanding why the return isn't actually setting the properties inside the Configuration class?
Edit: The function does run and it fetches 3006 rows of Data when matching the bus_type to customer. (Its a large Database) - Only, the properties are never set on return.
Edit: Is there a specific way to return an Array to a Class to set the Properties?
Thanks in advance!
Change your Configs in Controller to array
public Configuration[] Configs { get; set; }
Change your tbl_bus function to void, and set the Configs inside the function.
public void tbl_bus(string type, string match)
{
// do your code
// set the configs here
Configs = ConfigurationList.ToArray();
}
Hope it helps.
Although this is not a complete answer to your question, the problem probably lies in the fact that you're not doing anything with the array returned by the method. You're simply discarding it right away. If you change your code to
ctrl = new Controller();
Configuration[] config = ctrl.tbl_bus("bustype", "CUS");
you will be able to reference the array later on.
Console.WriteLine(config.Length);
Now you can use it to set any properties you like.
I have a session helper so that my session vars are strongly typed:
public sealed class SessionHelper
{
private static HttpSessionState Session
{
get
{
return HttpContext.Current.Session;
}
}
public static List<TestObject> Tests
{
get
{
List<TestObject> objects = new List<TestObject>();
if (Session["Tests"] != null)
{
objects = (List<TestObject>)Session["Tests"];
}
return objects;
}
set
{
Session["Tests"] = value;
}
}
}
Now I am trying to add an item to theTestObjects List so I thought I could just do:
SessionHelper.Tests.Add(new TestObject("Test name", 1));
But when I step through the code and look at the SessionHelper.Tests after the above line is run, the list count remains at 0.
If I do:
List<TestObject> tests = SessionHelper.Tests;
tests.Add(new TestObject(testName, version));
SessionHelper.Tests = tests;
Then it works properly.
Why can't I add the test object directly to the SessionHelper?
Session["Tests"] is null when you start. Therefore SessionHelper.Tests returns a new, empty list; however, this new list is not in the session object yet. Therefore SessionHelper.Tests will return a new, empty list every time. Store the new list in the session object after creating it.
public static List<TestObject> Tests
{
get
{
List<TestObject> objects = (List<TestObject>)Session["Tests"];
if (objects == null)
{
objects = new List<TestObject>();
Session["Tests"] = objects; // Store the new list in the session object!
}
return objects;
}
set // Do you still need this setter?
{
Session["Tests"] = value;
}
}