November Happy Hour will be moved to Thursday December 5th.
November Happy Hour will be moved to Thursday December 5th.
Hi
The replacement for CatalogContext is ICatalogSystem. Just get one of those from the service locator (either from the ServiceLocator or using constructor injection) and you can directly replace CatalogContext.Current.
If you want, you can also look into the IContentRepository, to get a more uniform way of working with Commerce and Content.
Regards
Per Gunsarfs
CatalogContext - or actually ICatalogSystem - is still working fine, but we recommend to have a transition to the content APIs as soon as possible, for various reasons, which were mentioned here http://world.episerver.com/blogs/Quan-Mai/Dates/2014/10/Moving-away-from-ICatalogSystem-part-0/
There is no simple Find-and-replace approach to change from ICatalogSystem to content APIs, but at the 10000 feet view, you'll need to use IContentRepository and the content types (ProductContent, VariationContent, etc. and their inherited classes) to work with catalog contents.
Regards,
/Q
If you are just doing loading you can start with IContentLoader and wait with IContentRepository for when you actually need to save your changes.
The Content APIs no longer gives you the untyped entries and doesn't use CatalogEntryResponseGroup.
Instead it works with typed classes and a specific language.
The corresponding content code for your calls would be:
var contentLink = _referenceConverter.GetContentLink(code, CatalogContentType.CatalogEntry);
var yourEntry = ContentReference.IsNullOrEmpty(contentLink) ? null : _contentLoader.Get<YourEntryClass>(contentLink, new CultureInfo("en-GB"));
If you want to use any related entities those have to be loaded seperatly, you no longer work with a single object that contains loads of tables.
Ah..
We used to use "CatalogEntryResponseGroup.ResponseGroup.CatalogEntryInfo" because we believed it was more performant (returning less data).
Now, is that similar to simply using _contentLoader.Get<Entry> because it doesn't automatically load tons of related data?
(or, is there a different class type that I should load?)
You should not use Entry, it's an old data class thing. You should be using ProductContent or VariationContent, or even better, their derived classes which represent your model type.
_contentLoader.Get<ProductContent> will load all the data attached to a product (except, well, prices and inventories). If you care about performance, then the general advice is to not load anything if you don't need it. You can find more detail here: http://vimvq1987.com/2017/02/episerver-commerce-performance-optimization-part-1/
It was very much more performant with CatalogEntryInfo.
Yes it is very similar to use _contentLoader.Get<T> for that exakt reason.
Recommended way is to define your own class type to load, it should include all your extended meta fields:
[CatalogContentType(GUID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx", MetaClassName = "ArticleMetaClass", DisplayName = "Article", Description = "")] [GenerateTypeScriptDefinition] public class ArticleDataModel : VariationContent, { public virtual string SupplierArticleNumber { get; set; } [Searchable, IncludeInDefaultSearch] public virtual string EAN { get; set; } [CultureSpecific] [Display(Name = "Cost price", Order = 170)] public virtual decimal CostPrice { get; set; } //..... }
Re: performance, the content allows much better cache-hit than the DTO approach, so in short term it can be slower, but in long term, and especially in concurrent scenarios, then content way fares better performance overall.
Thank you all,
But now I wonder - when I render an item list page: for each product I display, I retrieve the Product + ALL it's Variants.
There is certain data that I calculate from the Variants (such as "the price of the least expensive Variant", or "is any variant on sale").
I see that it could be expensive to retrieve all those variants. Also, ideally, I do not want to cache Variant price, sale status, etc. (or maybe only cache them for a few minutes. But not longer than that).
Is there a simple way I can use IContentLoader to retrieve only "the price of the least expensive Variant", or "is any variant on sale"? (currently I am retrieving ALL product Variants, then iterating through them locally to calculate these things).
There is an interface (IRelationRepository) and its extensions, which you can use to get the variants of a product. From that you can use some service (IPriceService) to load prices, without having to load the content themselves. Also IPromotionEngine extensions allow you you get promotions applied to them.
I would suggest you to explore the APIs, including interfaces and their extension methods. Episerver might already provide something more lightweight and effective to do what you want to, so you won't end up going the long way.
"Also, ideally, I do not want to cache Variant price, sale status, etc. (or maybe only cache them for a few minutes. But not longer than that)"
I understand the sentiment but you can never build a high-performance site on that premise. To be able to deliver good performance on any site everything needed for listings should come from a search engine and even in your case if you want to be able to filter or sort by "the price of the least expensive Variant" or "is any variant on sale" you really need to put those values in your search index.
Keeping the search index updated with current values as soon as something happens to change them is a challenge but it really is the only way i know to achieve anything above sub-par performance.
Thanks guys.. Good advice, as always. I have a lot of things to think about, and to learn about...
I have tried starting with a simple custom class, like this:
[CatalogContentType] public class MyProduct : ProductContent { }
But when I try to "get" one from IContentLoader, I get an exception:
var product = _contentLoader.Get<ProductContent>(contentLink); // <---works!
var product2 = _contentLoader.Get<MyProduct>(contentLink); // <---throws exception!
Exception: Content with id '93169__CatalogContent' is of type 'Castle.Proxies.ProductContentProxy' which does not inherit required type 'Business.Product.MyProduct'
There must be more to it then.. What am I missing?
If contentLink is a ProductContent, but not MyProduct, that's expected. You can't get an existing content and cast it to something else like that. You have to create a new MyProduct instance and save it first
What quan says or if you really intend to load an existing class you should set the existing metaclass name in your CatalogContentType.
Either way I would advice you to always explicitly set the Guid so that it will be the same in all environments instead of different random generated identifiers.
@Erik, you said -
I understand the sentiment but you can never build a high-performance site on that premise. To be able to deliver good performance on any site everything needed for listings should come from a search engine and even in your case if you want to be able to filter or sort by "the price of the least expensive Variant" or "is any variant on sale" you really need to put those values in your search index.
I am not using Epi Find (at the moment). But, in the future I will be using it. Hopefully I can do okay using only Epi repositories for now :(
Thank you all for all your advice.
Here is what my code looks like now:
// NOT cached!!! var productRef = _referenceConverter.GetContentLink(code, CatalogContentType.CatalogEntry); // cached for a long time: var productContent = localCache.GetProduct(productRef) ?? _contentLoader.Get<ProductContentWrapper>(productRef); // cached for a long time: var variationRefs = _relationRepository.GetRelationsBySource<ProductVariation>(productRef) .Select(x => x.Target) .Where(x => !ContentReference.IsNullOrEmpty(x)); // cached for 5 minutes: var variationContents = variationRefs.Select(_contentLoader.Get<VariationContent>); // cached for 5 minutes: var prices = localCache.GetPrices(productRef) ?? _priceDetailService.List(productRef);
Should I cache the call to _referenceConverter.GetContentLink? (I heard that Epi does not cache this for me).
Also I was wondering, I use IContentLoader because it is read-only. Are there read-only interfaces to replace IRelationRepository or IPriceDetailService?
Thanks!
- Ken
If you are using 9.24 or later version then ReferenceConverter.GetContentLink is cached.
And no, there is no default read-only implementation of IRelationRepository or IPriceDetailService.
The default implementation of IPriceDetailService is not cached. The other interface, IPriceService, is
If you only want to do Read-only operations for price then the other interface that Quan mentioned IPriceService is highly recommended.
IPriceDetailsService is intended to be used for changing the prices or making an admin interface where the user can change them.
For making queries to serve the site with current prices IPriceService is the one to use.
Does EPi also cache my call to:
_relationRepository.GetRelationsBySource<ProductVariation>(productRef)
(I was going to cache this locally myself)
Currently, no. That will be cached in Commerce 11, however the APIs of IRelationRepository will be changed as well (Source/Target will be obsoleted and replaced by Child/Parent)
One more thing, I noticed that ProductContent has a method: GetNodeRelations().
Can I simply use this instead of: _relationRepository.GetRelationsBySource(productReference)?
Is there any reason not to?
There are several extensions methods to help getting relations/associations easier. Note that by using that exact extension method, you will have limited mocking ability. If you want to unit test your code, use the other overload which takes IRelationRepository/ILinkRepository
I found the question a bit ambiguous since you omitted which type of relations you wanted to retrieve.
GetNodeRelations can be used instead of a call to _relationRepository.GetRelationsBySource<NodeRelation>()
but it can't be used to replace the call to
_relationRepository.GetRelationsBySource<ProductVariation>(productRef)
that you used in your code above.
Hi,
I've heard that "contexts" such as CatalogContext are now considered legacy.
So I'd like to replace all usages of CatalogContext in my code.
What is the correct way to replace the following by using repository: