Using Entity Framework Core with Lazy Loading Proxies, I have a UserAccount Model which has a property "LocalizationId" and a Lazy Load Navigation Property "Localization".
What benefit is there to using DatabaseContext.CreateProxy<UserAccount>() vs new UserAccount()?
In both cases, once the model is added to the DatabaseContext, and the Id of the Navigation property is set, the Navigation Property is Lazy Loaded and changes are tracked as expected...
var model = new Models.UserAccount()
{
LocalizationId = 1
};
DatabaseContext.Add(model);
var proxy = DatabaseContext.CreateProxy<Models.UserAccount>();
DatabaseContext.Add(proxy);
proxy.LocalizationId = 1;
var proxyLocalization = proxy.Localization.Name;
var modelLocalization = model.Localization.Name;
I feel like I have to be missing something obvious, because I don't see the benefit to using CreateProxy.
Difference that object created by CreateProxy can load related entity later instead of.
You have the same result in both objects because var proxyLocalization = proxy.Localization.Name have loaded Localization object and added to change tracker. Change tracker have found your non-proxied object and initialized automatically related property.
Related
I am using objectbox and attempting to force recomp on my lazy column with information from the viewModel.
I currently use a method that has been state hoisted to the view model but can't seem to retrieve the value in the view model and force recomp.
In my view model (DataFieldsViewModel), I've obtained all the Data Fields like this:
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox: Box<DataField> = _dataFieldsBox
and then it is passed in the composable screen using
fields = viewModel.dataFieldsBox
and the data fields object removed with
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(deletedDataField.value)
}
the reference for deletedDataField is kept so that it can be restored from a snack bar.
The delete works, the restore works, it's just that the lazy column does not update until I click another view that forces recomp. I've even mocked up a simple button with state hoisted increment and decrement methods and a value in the view model to test out recomp and got that working pretty quick, just don't know how to do it for these ObjectBox DataField objects
Edit 18/08
Tried using mutableStateOf
private var _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java)
var dataFieldsBox = mutableStateOf(_dataFieldsBox)
and then attempting to collect the value with
val fields by viewModel.dataFieldsBox
and then deleting with
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
only fix right now is the two hacky ways which i don't like. I read something about despite the values in a mutable list changing because the reference is the same the recomp doesn't happen to save on recomps.
So tried to amend the list and then duplicate with new values which also did nothing.
is DataFieldEvent.ConfirmDelete -> {
_deletedDataField.value = event.dataField
_dataFieldsBox.remove(event.dataField)
val newList = _dataFieldsBox
dataFieldsBox = mutableStateOf(newList)
}
Would be helpful if you update viewmodel code you are having.
Not worked on ObjectBox. With your current code, here is the thing.
Composable function will recompose when it is able to listen to changes. For normal variable it does not listen to changes. You can make them listen in several ways.
You can do it with mutableStateOf(). You can have mutableState variable which takes type of _dataFieldsBox . And so can access it by viewModel._dataFieldsBox.value.
private val _dataFieldsBox = ObjectBox.get().boxFor(DataField::class.java
var _dataFieldsBox = mutableStateOf(_dataFieldsBox)
And then in view model,
_dataFieldsBox.value.remove(_dataFieldsBox) // This will remove the value in the state variable and composable function will recompose.
You can also use LiveData and observe for the changes and it will recompose.
After I execute breeze query as shown below:
var breezeQuery = function(){
var query = EntityQuery.from('TableA')
.inlineCount();
function querySuceeded(data) {
//data.results[0] contains the entity
}
manager.executeQuery(query)
.then(querySuceeded)
}
I get the entity in data.results[0] which contains properties as well as other information like entityAspect etc.
How can I get the property names present in a breeze entity ?
Use the MetadataStore. Something like this:
var tableAType = manager.metadataStore.getEntityType("TableA");
var dataProperties = tableAType.dataProperties;
var navigationProperties = tableAType.navigationProperties;
or from an instance of a entity ( not a projection), since every entity will have an 'entityType' property you can also do this:
var tableAType = tableAInstance.entityType;
var dataProperties = tableAType.dataProperties;
var navigationProperties = tableAType.navigationProperties;
Also see: http://www.breezejs.com/sites/all/apidocs/classes/EntityType.html
Object.keys(data.result[0]) is the vanilla JavaScript way to get all properties of the data.result[0] object. Just saying.
Jay's way of course winnows those down to the properties monitored by Breeze, the persisted properties in particular. That's probably what you meant :-)
I saw a related question:
Sitecore Glass Mapper always null
But unfortunately it does not give a solution for my case.
Here goes a code snippet:
var db = Factory.GetDatabase("master");
var context = new SitecoreContext();
// the ID of Needed item
var g = new Guid("{F21C04FE-8826-41AB-9F3C-F7BDF5B35C76}");
// just to test if it's possible to fetch item using db.GetItem
var i = db.GetItem(new ID(g), Language.Current, Sitecore.Data.Version.Latest);
// Grab item
var t = context.GetItem<Article>(g);
In the code above:
i is not null
t is null
Article is the simple class like:
[SitecoreType(TemplateId = "{4C4EC1DA-EB77-4001-A7F9-E4C2F61A9BE9}")]
public class Article
{
[SitecoreField(FieldName = "Title")]
public string Title { get; set; }
}
There are only one language installed in Sitecore - en, it has been specified in the web.config in the items as well.
Also I have added GlassMapperSc.Start(); to Application_Start in the Global.asax.cs and added my assembly to the list of included assemblies via var attributes = new AttributeConfigurationLoader(new[] { "Assembly.Name" }); and I succeeded to find my class in the SitecoreContext mappings.
It does not looks like a language issue, as stated in the link provided in the very beginning. And I'm struggling with it already for a pretty long time, but no luck...
Thank You!
I just noticed that you are using master db for the Sitecore DB and SitecoreContext for Glass.
The SitecoreContext class will use the database that is defined by the Sitecore.Context.Database property at runtime. This probably means that it is using the web database.
Can you check that you have published the item to the web database or instead using:
var context = new SitecoreService("master");
I have an object where I maintain a relationship to another object:
public class Foo {
public Bar Bar { get; set; }
}
In the mapping I reference Bar to that I can maintain the relationship - I don't want the parent object to update properties in the child, just the existence of the relationship:
References(x => x.Bar, "BarId")
.Cascade.None();
In the UI layer I create the relationship using a property which is not the underlying primary key:
item.Bar = new Bar { Code = "123" };
In the repository layer I hydrate the object if it doesn't have the primary key populated:
if(item.Bar.Id == null)
{
item.Bar = barRepository.RetrieveByCode(item.Bar.Code);
}
When I the RetrieveByCode line runs (which is a Criteria.UniqueResult under the covers) I get a TransientObjectException telling me that "the object references an unsaved transient instance - save the transient instance before flushing" for the Bar type.
When I run the same code path without creating the temporary Bar object it works. It appears that the Bar created as a temporary oject is tracked by NHibernate, yet I want it to forget that it ever existed as it is only a placeholder.
Any thoughts on how to achieve this?
UPDATE: Doing some more testing on this it seems to be the change tracking in Foo that is causing trouble. If I call Session.Evict(item) after retrieving it, but before making any changes and then re-attach the object using Session.Update(item) after I am done it seems to work, however it updates the child objects which is not what I want - I only want to manage the relationship.
UPDATE 2: I changed the FlushMode from Auto to Commit. It seems to have disabled the queueing of any interim changes to the object. Having researched NH behavior a bit further it seems that Update works more like a "re-attach" call rather than an explicit "update now" call.
UPDATE 3: It appears changing FlushMode caused other issues with transactions that required several operational steps. I reverted back to try another approach:
if(item.Bar.Id == null)
{
var barCode = item.Bar.Code;
item.Bar = null;
item.Bar = barRepository.RetrieveByCode(barCode);
}
Why do you want it to work that way? Why not simply set item.Bar using the retrieved Bar object:
item.Bar = barRepository.RetrieveByCode("123");
You might be able to make your current pattern work using Load:
if(item.Bar.Id == null)
{
var bar = barRepository.RetrieveByCode(item.Bar.Code);
item.Bar = session.Load<Bar>(bar.Id);
}
I'm loading an instance twice from the same session, but nhibernate returns two instances which I am assuming means that the entity is not in the first level cache. What can cause this sort of behaviour?
Test:
using (new TransactionScope())
{
// arrange
NewSessionUnitOfWorkFactory factory = CreateUnitOfWorkFactory();
const int WorkItemId = 1;
const string OriginalDescription = "A";
WorkItemRepository repository = new WorkItemRepository(factory);
WorkItem workItem = WorkItem.Create(WorkItemId, OriginalDescription);
repository.Commit(workItem);
// act
using (IUnitOfWork uow = factory.Create())
{
workItem = repository.Get(WorkItemId);
WorkItem secondInstance = repository.Get(WorkItemId);
// assert
Assert.AreSame(workItem, secondInstance);
}
}
Update
The reason for this odd behaviour was this line of code:
NewSessionUnitOfWorkFactory factory = CreateUnitOfWorkFactory();
When I replaced it with this factory impl:
ExistingSessionAwareUnitOfWorkFactory factory = new ExistingSessionAwareUnitOfWorkFactory(CreateUnitOfWorkFactory(), new NonTransactionalChildUnitOfWorkFactory());
It works as expected.
I'm just guessing here, as you did not include the code for your Repository/UnitOfWork implementations. Reading this bit of code though, how does your Repository know which UnitOfWork it should be acting against?
First Level Cache is at the Session level, which I am assuming is held in your IUnitOfWork. The only setting on the Repository is the Factory, so my next assumption is that the code for repository.Get() is instantiating a new Session and loading the object through it. So the next call to Get() will instantiate another new Session and load the object. Two different level 1 caches, two different objects retrieved.
Of course, if your UnitOfWork is actually encapsulating Transaction, and the Factory is encapsulating Session, then this doesn't actually apply :)