I am updating a product within category, but my query loading time is 4.32s. But cosmos db is read pass. Is there any way to reduce time? Please suggest me any idea.
For the first load it takes around 5s, then it takes 3-4.5 seconds. Is there any way to solve this issue?
My repository class code :
public async Task<Catalog> UpdateProductInMainCategory(Guid productid, Product productData)
{
var feedOptions = new FeedOptions
{
MaxItemCount = -1,
EnableCrossPartitionQuery = true
};
var query =
$"SELECT catalog.id,catalog.VendorName,catalog.Industy,product1 FROM catalog join industry in catalog.Industy join category in industry.Category join product1 in category.Product join Subcategory in category.Subcategory join product in Subcategory.Product where product1.Id ='" + productid + "'";
var catlog = _cosmosClient.CreateDocumentQuery<Catalog>(UriFactory.CreateDocumentCollectionUri(
_azureCosmosDbOptions.Value.DatabaseId, "catalog"), query, feedOptions).AsEnumerable().FirstOrDefault();
Console.WriteLine("i am kajas");
if (catlog != null)
{
foreach (var item in catlog.Industy)
{
foreach (var cat in item.Category)
{
Product product = cat.Product.FirstOrDefault(p => p.Id == productid);
if (product != null)
{
product.Name = productData.Name == null ? product.Name : productData.Name;
product.Addons = productData.Addons == null ? product.Addons : productData.Addons;
product.CurrentQuantity =
break;
}
}
break;
}
}
var requestOptions = new RequestOptions
{
PartitionKey = new Microsoft.Azure.Documents.PartitionKey(catlog.Id.ToString())
};
var orderDocument = await _cosmosClient.ReplaceDocumentAsync(
UriFactory.CreateDocumentUri(
_azureCosmosDbOptions.Value.DatabaseId, "catalog", catlog.Id.ToString()), catlog, requestOptions);
return (Catalog)((dynamic)catlog);
}
You are also setting the MaxItemCount to -1 and you are using AsEnumerable().FirstOrDefault(). This means that you will force the system to return everything in a single page. However, this is not recommended. On top of that I can see that you aren't using the ExecuteNextAsync approach.
Rewriting your code should looks something like this:
public async Task<Catalog> UpdateProductInMainCategory(Guid productid, Product productData)
{
var feedOptions =
new FeedOptions
{
MaxItemCount = 100,
PartitionKey = new PartitionKey(productid.ToString())
};
var query =
$"SELECT catalog.id,catalog.VendorName,catalog.Industy,product1 FROM catalog join industry in catalog.Industy join category in industry.Category join product1 in category.Product join Subcategory in category.Subcategory join product in Subcategory.Product where product1.Id ='" + productid + "'";
var catlogQuery = _cosmosClient.CreateDocumentQuery<Catalog>(UriFactory.CreateDocumentCollectionUri(_azureCosmosDbOptions.Value.DatabaseId, "catalog"), query, feedOptions).AsDocumentQuery();
Catalog catalog = null;
while(catlogQuery.HasMoreResults)
{
var result = await catlogQuery.ExecuteNextAsync();
if(result.Any())
{
catalog = result;
break;
}
}
Console.WriteLine("i am kajas");
if (catlog != null)
{
foreach (var item in catlog.Industy)
{
foreach (var cat in item.Category)
{
Product product = cat.Product.FirstOrDefault(p => p.Id == productid);
if (product != null)
{
product.Name = productData.Name == null ? product.Name : productData.Name;
product.Addons = productData.Addons == null ? product.Addons : productData.Addons;
product.CurrentQuantity =
break;
}
}
break;
}
}
var requestOptions =
new RequestOptions
{
PartitionKey = new Microsoft.Azure.Documents.PartitionKey(catlog.Id.ToString())
};
var orderDocument = await _cosmosClient.ReplaceDocumentAsync(
UriFactory.CreateDocumentUri(
_azureCosmosDbOptions.Value.DatabaseId, "catalog", catlog.Id.ToString()), catlog, requestOptions);
return (Catalog)((dynamic)catlog);
}
Notice that now it will go though the proper pagination process and it will exit faster because it won't lock your thread which it's querying and also return when it has a first match in the while loop.
On a side note, it looks like your code could benefit by using Cosmonaut. It offers a lot of this functionality out of the box. This same query would look like this:
var query =
$"SELECT catalog.id,catalog.VendorName,catalog.Industy,product1 FROM catalog join industry in catalog.Industy join category in industry.Category join product1 in category.Product join Subcategory in category.Subcategory join product in Subcategory.Product where product1.Id ='" + productid + "'";
var catalog = await _catalogStore.Query(query).FirstOrDefaultAsync();
Full disclosure, I'm the creator of Cosmonaut.
Related
I'm converting from EF6 to EF Core 3.1 and this LINQ query is failing with a runtime exception stating 'The LINQ expression ... could not be translated.
The group by is what is causing the issue, but I'm not sure how to rewrite it to work with EF Core and keep the result in a nested list.
Notification notification = new Notification()
{
ProductReminders = new List<List<ProductNotification>>(),
ProductStats = new List<StatResult>()
};
var profileCode = 123;
notification.ProductReminders =
(from ng in ProductNotification
where ng.UserProfileCode == profileCode
orderby ng.EndDate ?? DateTime.MaxValue
group ng by ng.GroupGUID into groupG
select (from pn in ProductNotification
join p in Product on pn.ProductID equals p.ProductID
where pn.UserProfileCode == profileCode
&& pn.GroupGUID == groupG.Key
orderby pn.EndDate ?? DateTime.MaxValue
select new ProductNotification()
{
ProductDetail = new ProductDetail()
{
ProductId = pn.ProductID ?? 0,
Upc = p.UPC,
Brand = p.Description,
Manufacturer = p.Name,
ProfileCode = p.ProfileCode,
},
EndDate = pn.EndDate,
NotificationId = pn.NotificationID,
Status = pn.Status,
GroupGuid = pn.GroupGUID
})
.ToList())
.ToList();
Since grouping operator has limitations, I would suggest to read all needed data and provide grouping on the client side. Query in your case will be much effective:
// select only needed data from database
var minimalRequiredData =
from pn in ProductNotification
join p in Product on pn.ProductID equals p.ProductID
where pn.UserProfileCode == profileCode
select new ProductNotification
{
ProductDetail = new ProductDetail
{
ProductId = pn.ProductID ?? 0,
Upc = p.UPC,
Brand = p.Description,
Manufacturer = p.Name,
ProfileCode = p.ProfileCode,
},
EndDate = pn.EndDate,
NotificationId = pn.NotificationID,
Status = pn.Status,
GroupGuid = pn.GroupGUID
};
// materialize result
var materialized = minimalRequiredData.ToList();
// form required result shape using IEnumerable<T>
var resultQuery =
from m in materialized
orderby ng.EndDate ?? DateTime.MaxValue
group m by new m.GroupGUID into g
select g.Orderby(x => x.EndDate ?? ng.EndDate).ToList();
notification.ProductReminders = resultQuery.ToList();
How would I go about changing my if statement and foreach to something cleaner in linq using select and where.
I've tried to make the if statement into a where clause and then use the select query as a replacement for the Foreach loop but that seem to have type issues and wasn't working.
{
StripeConfiguration.ApiKey = _appSettings.StripeSecretKey;
var profile = await _userManager.FindByIdAsync(customerServiceID);
var stripeId = profile.StripeAccountId;
if (stripeId == null)
throw new ArgumentException("No associated Stripe account found.");
List<PaymentMethodDto> result = new List<PaymentMethodDto>();
var options = new PaymentMethodListOptions
{
Customer = stripeId,
Type = "card",
};
var service = new PaymentMethodService();
var payments = await service.ListAsync(options);
if (payments != null && payments.Data?.Count > 0)
{
payments.Data.ForEach((x) =>
{
result.Add(
new PaymentMethodDto
{
Brand = x.Card.Brand,
LastDigits = x.Card.Last4,
StripeToken = x.Id,
CustomerID = x.CustomerId
});
});
}
return result;
}
Just do a regular Select.
List<PaymentMethodDto> result = payments.Data.Select(x => new PaymentMethodDto
{
Brand = x.Card.Brand,
LastDigits = x.Card.Last4,
StripeToken = x.Id,
CustomerID = x.CustomerId
})
.ToList();
If payments.Data has nothing in it, this will give you an empty list, which is what you want.
If payments is null, you'll get an exception, which I think if you think about it really hard is probably what you really want in that case too. Why would .ListAsync() yield a null value?
I am trying to get a record from database using linq but it keep return no record
it is very basic sql statment
select * where productid ='12553'
however the following code does not return any result. Please advise. thx you
private static IEnumerable<ProductModel> GetAllProduct(string productId)
{
using (var dc = new TestEntities())
{
var result = (from a in dc.Products
where a.productid == productId
select new ProductModel
{
ProductId = a.productid,
Name = a.ProductName
});
return result.Distinct().ToList();
}
}
You don't need projection here:
using (var dc = new TestEntities())
{
var result = from a in dc.Products
where a.productid == productId
select a;
return result.Distinct().ToList();
}
I am having trouble to return an IEnumerable and IList, I cant do it!
I am using EF 4 with POCOs
Here's the whole method:
//public IList<Genre> GetGenresByGame(int gameId)
public IEnumerable<Genre> GetGenresByGame(int gameId)
{
using(var ctx = new XContext())
{
var results =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new Genre
{
GenreId = t0.GenreId,
GenreName = t1.GenreName
};
return results.ToList();
}
}
I have tried different ways that I have found on the net.. but can't make it work!
Question 2:
I saw a screencast with Julie something, saying that "You should always return an ICollection" when using EF4.
Any thoughts about that ?
BR
EDIT:
When I load the page in Debug-mode i get these errors: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. OR The entity or complex type 'XModel.Genre' cannot be constructed in a LINQ to Entities query.
Genre must not be a L2EF type. Try this:
public IEnumerable<Genre> GetGenresByGame(int gameId)
{
using(var ctx = new XContext())
{
var resultList =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new { t0.GenreId, t1.GenreName };
var genres = resultList.AsEnumerable().Select(o => new Genre
{
GenreId = o.GenreId,
GenreName = o.GenreName
});
return genres.ToList();
}
}
First an foremost if Genre is in the database you should select it? If you have FKs from Genre->GenreCultureDetails let me know and I can update the below, but from the looks of it you could do it like this:
using(var ctx = new XContext())
{
var results =
from g in ctx.Genre
join gcd in ctx.GenreCultureDetails on g.GenreId equals gcd.GenreId
where g.GameId == gameId && gcd.CultureId == _cultureId
select g;
return result.ToList();
}
Alternatively continue down your path select them into an annoynmous type, and then copy them. You can use select instead of convertall if you please.
IList<Genre> returnMe = Null;
using(var ctx = new XContext())
{
var results =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new
{
GenreId = t0.GenreId,
GenreName = t1.GenreName
};
returnMe = results.ToList().ConvertAll(x=>new Genre(){
GenreId = x.GenreId,
GenreName = x.GenreName
}
);
}
return returnMe;
How to dynamically generate LINQ query:
int[] IDArray = {55, 36};
public IQueryable<ContactListView> FindAllContacts(int loggedUserID, int[] IDArray)
{
var result = (
from contact in db.Contacts
//Start of dynamic part...
where
contact.UserID == loggedUserID
foreach (var id in IDArray)
{
where contact.UserID == id
}
// End of dynamic part
orderby contact.ContactID descending
select new ContactListView
{
ContactID = contact.ContactID,
FirstName = contact.FirstName,
LastName = contact.LastName
});
return result;
}
Thanks,
Ile
Can't you just use Contains? I assume you want to generate a query that has an IN clause. There's an example of that here: Creating in queries with linq to sql.
int[] IDArray = {55, 36};
public IQueryable<ContactListView> FindAllContacts(int loggedUserID, int[] IDArray)
{
var result = (
from contact in db.Contacts
where contact.UserID == loggedUserID
&& IDArray.Contains( contact.UserID )
orderby contact.ContactID descending
select new ContactListView
{
ContactID = contact.ContactID,
FirstName = contact.FirstName,
LastName = contact.LastName
});
return result;
}
You probably want where IDArray.Contains(contact.UserID)