breeze.js not honoring the "noTracking" option when end point returns multiple result sets - api

Consider this breze query:
return EntityQuery.from('myAPI')
.noTracking(true)
.using(manager).execute()
.then(querySucceeded)
.fail(queryFailed);
My API is defined like this:
[HttpGet]
public object myAPI()
{
// var userId = get the users id from auth ticket
var userPref = _contextProvider.Context.UserPreferences.Where(u => u.userId == userId);
var userOptions = _contextProvider.Context.UserOptions.Where(u => u.userId == userId);
return new
{
userPref,
userOptions
};
}
I know I can get access to the raw data, which is great. But in addition to this, the entities are created in the entity manager, which I would prefer they not be. This works fine for apis that return IQueryable. Is there a different syntax for noTracking for web apis that returns multiple result sets?
thanks

I can't reproduce the error you describe. I have a similar DocCode test that passes which references Breeze v1.5.3.
Here is the pertinent NorthwindController method:
[HttpGet]
public object Lookups()
{
var regions = _repository.Regions;
var territories = _repository.Territories;
var categories = _repository.Categories;
var lookups = new { regions, territories, categories };
return lookups;
}
And here's the passing QUnit test:
asyncTest('object query (e.g., lookups) w/ "no tracking" does not add to cache', function () {
expect(2);
var em = newNorthwindEm();
EntityQuery.from('Lookups')
.noTracking(true)
.using(em).execute()
.then(success).fail(handleFail).fin(start);
function success(data) {
var lookups = data.results[0];
var hasLookups = lookups &&
lookups.categories && lookups.regions && lookups.territories;
ok(hasLookups, 'Expected a lookups object w/ categories, regions and territories');
var cached = em.getEntities();
var len = cached.length;
equal(0, len, 'Expected ZERO cached entities of any kind and got ' + len);
}
});
If I comment out the noTracking(true) clause, the test fails and tells me that there are 65 entities in cache ... as predicted.
What am I missing?

Related

Return a list of elements that are NOT in two previous lists

I have 2 lists of IDs and I need to return a list with the products that aren't in any of those lists:
public IEnumerable<Produto> GetProdutosIdNotInFamily(Guid produtoId)
{
var produtosPai = GetListaPaisId(produtoId);
var produtosFilho = GetListaFilhosId(produtoId);
var prod = _dbContext.Produtos
.Where(u => !produtosPai.Any(p => p.ProdutoFilhoId == u.Id)
&& !produtosFilho.Any(p => p.ProdutoFilhoId == u.Id));
return prod;
}
You can do this in two ways -- One using Contains and other using Any like you provided in your snippet in the post.
Using Contains Method
If you want to use Contains() method, you may be pulling out all the product Ids into a collection and apply LINQ on top of it and get the list that is not part of both your reference lists. Sample code is as shown below
// This is the sample model I am dealing with
public class Dummy
{
public int Id { get; set; }
public string Name { get; set; }
}
// Assuming the below call returns list of 'Dummy' objects
var products = _dbContext.Produtos;
// list1 & list2 are populated in your case already through the method calls
var exclusionList1 = list1.Select(x => x.Id).ToList<int>();
var exclusionList2 = list2.Select(x => x.Id).ToList<int>();
var myList = products.Where(x => !exclusionList1.Contains(x.Id) && !exclusionList1.Contains(x.Id)).ToList();
Contains is an instance method and takes an object as a parameter and the time complexity depends on the collection you're using this on.
Using Any
Just like Where, Any is an extension method. It takes a delegate as a parameter which gives you greater flexibility and control with respect to what you would want to do.
Applying Any to your scenario is as shown below:
var products = _dbContext.Produtos;
var exclusionList1 = GetListaPaisId(produtoId);
var exclusionList2 = GetListaFilhosId(produtoId);
var prod = _dbContext.Produtos.Where(x => !exclusionList1.Any(z => x.Id == z.Id) &&
!exclusionList2.Any(z => x.Id == z.Id)).ToList();
You can choose your approach based on the context under which you are performing this operation.

Entity Framework Core: New transaction is not allowed because there are other threads running in the session

I have a database with a hierarchy of categories. Each category has a parentcategoryid. I call the following function to load the top level categories and then it recursively calls itself to load all the children.
However, I get the following error:
SqlException: New transaction is not allowed because there are other
threads running in the session.
public async Task LoadCategoriesAsync()
{
await LoadCategoriesByParentId(null);
}
private async Task LoadCategoriesByParentId(int? sourceParentId, int? parentId)
{
var sourceCategories = _dbContext.SourceCategory.Where(c => c.ParentCategoryId == sourceParentId);
foreach (var sourceCategory in sourceCategories)
{
var newCategory = new Category()
{
Name = sourceCategory.Name,
Description = sourceCategory.Description,
ParentCategoryId = parentId
};
_dbContext.Category.Add(newCategory);
await _dbContext.SaveChangesAsync();
//category.EntityId = newCategory.Id;
//_dbContext.SourceCategory.Update(category);
//await _dbContext.SaveChangesAsync();
await LoadCategoriesByParentId(sourceCategory.CategoryId, newCategory.Id);
}
}
Your Where() statement doesn't retrieve the data; just "opens the cursor" (in old-speak). So, you can't do SaveChange(). The simplest solution is to convert IEnumerable to List or Array:
var rootCategories = _dbContext.SourceCategory.Where(c => c.ParentCategoryId == parentId).ToList();
But I would strongly recommend you google the error and understand why it is happening. To do this recursively is begging for trouble

Get all sharepoint sites in a site collection to which user has access using Javascript client object model

I want to display all sites in a site collection using JSOM to which user has access to. In other words I only need to find collection of sites to which user has access in a site collection. I am able to get all webs but it doesnt work if user doesnt have permissions to some of web sites.
SP.Web.getSubwebsForCurrentUser Method returns a security trimmed (user has access) collection of sub sites (only one level beneath)
Example
var ctx = SP.ClientContext.get_current();
var webs = ctx.get_web().getSubwebsForCurrentUser(null);
ctx.load(webs);
ctx.executeQueryAsync(
function() {
for(var i=0;i< webs.get_count();i++) {
var web = webs.getItemAtIndex(i);
console.log(web.get_title());
}
},
function(sender,args){
console.log(args.get_message());
}
);
If you are interested in all sub webs within site collection, you could consider the following approach.
function getAllSubwebsForCurrentUser(success,error)
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_site().get_rootWeb();
var result = [];
var level = 0;
var getAllSubwebsForCurrentUserInner = function(web,result,success,error)
{
level++;
var ctx = web.get_context();
var webs = web.getSubwebsForCurrentUser(null);
ctx.load(webs,'Include(Title,Webs)');
ctx.executeQueryAsync(
function(){
for(var i = 0; i < webs.get_count();i++){
var web = webs.getItemAtIndex(i);
result.push(web);
if(web.get_webs().get_count() > 0) {
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
}
level--;
if (level == 0 && success)
success(result);
},
error);
};
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
Usage
getAllSubwebsForCurrentUser(
function(allwebs){
for(var i = 0; i < allwebs.length;i++){
console.log(allwebs[i].get_title());
}
},
function(sendera,args){
console.log(args.get_message());
});
Hi the following code snippet may help you.
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web);
var webCollection = web.getSubwebsForCurrentUser(null);
ctx.load(webCollection);
ctx.executeQueryAsync(
Function.createDelegate(this,this.onSuccess),
Function.createDelegate(this,this.onError)
);
getSubwebsForCurrentUser - uses a parameter of type SP.SubwebQuery which you may leave as null.
The web collection you get using this code is just of one level. You will not get the subsites of the subsites. For that you need to execute the same statements on every SP.Web object you get - recursively - starting from the root web.
If you can use the API instead then I would suggest you do the following to return all the sub webs for the current user.
using(SPSite site = new SPSite("http://example/site/"))
{
using (SPWeb web = site.OpenWeb())
{
SPWebCollection webCollection = web.GetSubwebsForCurrentUser();
}
}
Note: As pointed out by Helm Sterk in comment below, GetSubwebsForCurrentUser() would not return result which the user is seeking. So above code would not work.

MS Dynamics CRM. Get users who current record shared with

I have a entity record which is shared with or more users. I would like to unshare this record when Deactivate it. I want to do that in Plugin. But I can't understand how to get all users from sharing list who have access to this record. How to do that?
Here is my code snippet:
protected void ExecutePostPersonSetStateDynamicEntity(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
var context = localContext.PluginExecutionContext;
var targetEntity = (Entity)context.InputParameters["EntityMoniker"];
var state = (OptionSetValue)context.InputParameters["State"];
var columns = new ColumnSet(new[] { "statecode" });
var retrivedEntity = localContext.OrganizationService.Retrieve(targetEntity.LogicalName, targetEntity.Id, columns);
if (state.Value == 1)
{
RevokeAccessRequest revokeRequest = new RevokeAccessRequest()
{
Target = new EntityReference(personEntity.LogicalName, personEntity.Id),
Revokee = new EntityReference(neededEntity.LogicalName, needed.Id)
};
// Execute the request.
}
}
As you can see, I need an entity "neededEntity", I don't know how to get it from "targetEntity" or "retrievedEntity".
You need to use a RetrieveSharedPrincipalsAndAccessRequest
http://msdn.microsoft.com/en-us/library/microsoft.crm.sdk.messages.retrievesharedprincipalsandaccessrequest.aspx
You can start from the included example, basically inside the foreach you call your RevokeAcessRequest

EntityFramework, Insert if not exist, otherwise update

I'm having a Entity-Set Countries, reflecting a database table '<'char(2),char(3),nvarchar(50> in my database.
Im having a parser that returns a Country[] array of parsed countries, and is having issues with getting it updated in the right way. What i want is: Take the array of countries, for those countries not already in the database insert them, and those existing update if any fields is different. How can this be done?
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
//Code missing
}
}
I was thinking something like
for(var c in data.Except(db.Countries)) but it wount work as it compares on wronge fields.
Hope anyone have had this issues before, and have a solution for me. If i cant use the Country object and insert/update an array of them easy, i dont see much benefict of using the framework, as from performers i think its faster to write a custom sql script that inserts them instead of ect checking if an country is already in the database before inserting?
Solution
See answer of post instead.
I added override equals to my country class:
public partial class Country
{
public override bool Equals(object obj)
{
if (obj is Country)
{
var country = obj as Country;
return this.CountryTreeLetter.Equals(country.CountryTreeLetter);
}
return false;
}
public override int GetHashCode()
{
int hash = 13;
hash = hash * 7 + (int)CountryTreeLetter[0];
hash = hash * 7 + (int)CountryTreeLetter[1];
hash = hash * 7 + (int)CountryTreeLetter[2];
return hash;
}
}
and then did:
var data = e.ParsedData as Country[];
using (var db = new entities())
{
foreach (var item in data.Except(db.Countries))
{
db.AddToCountries(item);
}
db.SaveChanges();
}
I would do it straightforward:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
foreach(var country in data)
{
var countryInDb = db.Countries
.Where(c => c.Name == country.Name) // or whatever your key is
.SingleOrDefault();
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
I don't know how often your application must run this or how many countries your world has. But I have the feeling that this is nothing where you must think about sophisticated performance optimizations.
Edit
Alternative approach which would issue only one query:
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
var names = data.Select(c => c.Name);
var countriesInDb = db.Countries
.Where(c => names.Contains(c.Name))
.ToList(); // single DB query
foreach(var country in data)
{
var countryInDb = countriesInDb
.SingleOrDefault(c => c.Name == country.Name); // runs in memory
if (countryInDb != null)
db.Countries.ApplyCurrentValues(country);
else
db.Countries.AddObject(country);
}
db.SaveChanges();
}
}
The modern form, using later EF versions would be:
context.Entry(record).State = (AlreadyExists ? EntityState.Modified : EntityState.Added);
context.SaveChanges();
AlreadyExists can come from checking the key or by querying the database to see whether the item already exists there.
You can implement your own IEqualityComparer<Country> and pass that to the Except() method. Assuming your Country object has Id and Name properties, one example of that implementation could look like this:
public class CountryComparer : IEqualityComparer<Country>
{
public bool Equals(Country x, Country y)
{
return x.Name.Equals(y.Name) && (x.Id == y.Id);
}
public int GetHashCode(Country obj)
{
return string.Format("{0}{1}", obj.Id, obj.Name).GetHashCode();
}
}
and use it as
data.Countries.Except<Country>(db, new CountryComparer());
Although, in your case it looks like you just need to extract new objects, you can use var newCountries = data.Where(c => c.Id == Guid.Empty); if your Id is Guid.
The best way is to inspect the Country.EntityState property and take actions from there regarding on value (Detached, Modified, Added, etc.)
You need to provide more information on what your data collection contains i.e. are the Country objects retrieved from a database through the entityframework, in which case their context can be tracked, or are you generating them using some other way.
I am not sure this will be the best solution but I think you have to get all countries from DB then check it with your parsed data
void Method(object sender, DocumentLoadedEvent e)
{
var data = e.ParsedData as Country[];
using(var db = new DataContractEntities)
{
List<Country> mycountries = db.Countries.ToList();
foreach(var PC in data)
{
if(mycountries.Any( C => C.Name==PC.Name ))
{
var country = mycountries.Any( C => C.Name==PC.Name );
//Update it here
}
else
{
var newcountry = Country.CreateCountry(PC.Name);//you must provide all required parameters
newcountry.Name = PC.Name;
db.AddToCountries(newcountry)
}
}
db.SaveChanges();
}
}