<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="http://ondemanddevelopers.co.uk/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>The Site Doctor Blog</title>
    <link>http://ondemanddevelopers.co.uk/</link>
    <description />
    <generator>Articulate, blogging built on Umbraco</generator>
    <item>
      <guid isPermaLink="false">3022</guid>
      <link>http://ondemanddevelopers.co.uk/blog/beyond-titles-reimagining-introductions-in-professional-meetings/</link>
      <category>Business</category>
      <title>Beyond Titles: Reimagining Introductions in Professional Meetings</title>
      <description>&lt;h1&gt;Beyond Titles: Reimagining Introductions in Professional Meetings&lt;/h1&gt;
&lt;p&gt;In a recent workshop, I found myself in a room with the full C-suite of executives and their teams. As we went around the table for introductions, I noticed something that felt oddly outdated: "I'm John, the CFO." "I'm Sarah, the CMO." "I'm Mike, Head of Digital."&lt;/p&gt;
&lt;p&gt;While impressive, these role-based introductions left me wondering: &lt;em&gt;what does any of this tell us about why we're actually here?&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;The Title Trap&lt;/h2&gt;
&lt;p&gt;Job titles serve a purpose in organizational structures. They help define reporting lines, compensation bands, and broad areas of responsibility. But in the context of a specific meeting—particularly collaborative workshops or project discussions—titles can create unnecessary hierarchies and fail to communicate what truly matters: why each person is in the room.&lt;/p&gt;
&lt;p&gt;Having founded and grown TSD from a one-person operation to what it is today, I've worn nearly every hat in the business. Now that my business card reads "CEO," I've noticed that this title often communicates very little about my actual purpose in many meetings I attend.&lt;/p&gt;
&lt;h2&gt;Purpose Over Position&lt;/h2&gt;
&lt;p&gt;What if we introduced ourselves differently? Imagine this alternative:&lt;/p&gt;
&lt;p&gt;"Hi, I'm Tim. I'm here to oversee the general discussion, facilitate the direction of the conversation, and ensure the right team members are brought into the project moving forward."&lt;/p&gt;
&lt;p&gt;"Hello, I'm Bill. I head up the finance team, and I'm here because you'll need to know who to direct invoices to and discuss budget considerations."&lt;/p&gt;
&lt;p&gt;"I'm Jack from marketing. I'm here to understand the requirements from a marketing perspective and ensure our messaging aligns with what's being developed."&lt;/p&gt;
&lt;p&gt;This purpose-focused approach accomplishes several things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Clarifies contribution&lt;/strong&gt; - Everyone immediately understands each person's role in the specific discussion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduces power dynamics&lt;/strong&gt; - The focus shifts from hierarchy to contribution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identifies unnecessary participants&lt;/strong&gt; - It quickly becomes apparent if someone doesn't have a clear purpose for being there&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Directs questions appropriately&lt;/strong&gt; - Others know exactly who to address for specific concerns&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The Meeting ROI Question&lt;/h2&gt;
&lt;p&gt;Purpose-based introductions also naturally lead to the question every busy professional should ask: "Why am I in this meeting?"&lt;/p&gt;
&lt;p&gt;When forced to articulate your purpose, you might realize you don't have one. And that's perfectly fine—it gives you permission to excuse yourself and reclaim that hour for more productive work.&lt;/p&gt;
&lt;p&gt;A McKinsey study found that executives spend an average of 23 hours per week in meetings, with 15% of an organization's collective time spent in meetings—a percentage that has increased every year since 2008. Imagine the organizational impact if even a small percentage of those meetings were attended only by those with a clear purpose.&lt;/p&gt;
&lt;h2&gt;Context-Specific Roles&lt;/h2&gt;
&lt;p&gt;Another benefit of purpose-based introductions is recognizing that roles shift throughout a project lifecycle. The CEO might need to be in the kickoff meeting to set strategic direction but can skip the technical implementation discussions. The Head of Marketing might need to join requirements gathering but not testing sessions.&lt;/p&gt;
&lt;p&gt;When we define ourselves by our purpose in each specific context, we create space for more fluid, efficient participation patterns.&lt;/p&gt;
&lt;h2&gt;Implementation Tips&lt;/h2&gt;
&lt;p&gt;Ready to try purpose-based introductions in your next meeting? Here are some practical tips:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start with yourself&lt;/strong&gt; - Model the behavior by introducing yourself by purpose first&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set the expectation&lt;/strong&gt; - When facilitating, explicitly ask attendees to share why they're there rather than their title&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep it concise&lt;/strong&gt; - Aim for one or two sentences that clearly articulate your contribution&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Normalize absence&lt;/strong&gt; - Create a culture where it's acceptable (even encouraged) for people to excuse themselves from meetings where they lack a clear purpose&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document the purposes&lt;/strong&gt; - For recurring project meetings, consider documenting each attendee's purpose to reinforce their specific contribution&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Beyond Efficiency: Building Psychological Safety&lt;/h2&gt;
&lt;p&gt;There's a deeper benefit to purpose-based introductions beyond efficiency. By focusing on contribution rather than position, we create more psychologically safe environments.&lt;/p&gt;
&lt;p&gt;Research by Google's Project Aristotle identified psychological safety—the belief that one won't be punished or humiliated for speaking up with ideas, questions, or concerns—as the single most important factor in high-performing teams. When C-suite executives introduce themselves by their purpose rather than their title, they signal that input is welcome based on relevance, not rank.&lt;/p&gt;
&lt;h2&gt;The Resistance You'll Face&lt;/h2&gt;
&lt;p&gt;If you attempt to shift your organization toward purpose-based introductions, expect resistance. Titles serve as status markers, and some may feel uncomfortable setting them aside, even temporarily. This is particularly true in hierarchical organizations or industries where title progression is a primary measure of career advancement.&lt;/p&gt;
&lt;p&gt;The key is to frame this approach not as eliminating titles (which still serve important functions in the broader organizational context), but as supplementing them with purpose in specific collaborative settings.&lt;/p&gt;
&lt;h2&gt;A Small Change with Big Impact&lt;/h2&gt;
&lt;p&gt;As with many organizational changes, this one begins with personal practice. In your next meeting, try introducing yourself differently:&lt;/p&gt;
&lt;p&gt;"I'm [Name] and I'm here today to [specific purpose for this specific meeting]."&lt;/p&gt;
&lt;p&gt;You might be surprised how this small shift changes the dynamic of the discussion that follows. At a minimum, it clarifies your own thinking about why you're spending your valuable time in that room.&lt;/p&gt;
&lt;p&gt;And if you can't articulate a clear purpose? Perhaps that's the most valuable insight of all.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;p&gt;Harvard Business Review. (2017). "Stop the Meeting Madness." &lt;a href="https://hbr.org/2017/07/stop-the-meeting-madness"&gt;https://hbr.org/2017/07/stop-the-meeting-madness&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Duhigg, C. (2016). "What Google Learned From Its Quest to Build the Perfect Team." The New York Times Magazine. &lt;a href="https://www.nytimes.com/2016/02/28/magazine/what-google-learned-from-its-quest-to-build-the-perfect-team.html"&gt;&lt;span&gt;https://www.nytimes.com/2016/02/28/magazine/what-google-learned-from-its-quest-to-build-the-perfect-team.html&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;McKinsey &amp;amp; Company. (2019). "The Online Meeting Survival Guide." McKinsey Quarterly.&lt;/p&gt;</description>
      <pubDate>Fri, 28 Mar 2025 08:44:16 Z</pubDate>
      <a10:updated>2025-03-28T08:44:16Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3020</guid>
      <link>http://ondemanddevelopers.co.uk/blog/ucommerce-not-always-updating-parent-skus-on-variants/</link>
      <category>uCommerce</category>
      <title>Ucommerce not always updating Parent SKUs on variants</title>
      <description>&lt;p&gt;&lt;span&gt;The team have just been caught out with this sneaky bug in the new Ucommerce Product GUI in version 9.4.2.21145 that we first spotted in 2021.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;When editing a SKU on a parent product in the new GUI, it doesn’t update on the product's SKU on the variants. In the example below "&lt;a href="https://www.westons-cider.co.uk/shop/merchandise-and-glassware/old-rosie-t-shirt.html" target="_blank"&gt;Old Rosie T-shirt&lt;/a&gt;" was&lt;/span&gt;&lt;span&gt; changed from WES9999290 to WES9999275 however if you look at the screenshot from the SQL data when called directly, the parent product is fine but the variants are still WES9999&lt;span&gt;290.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;img style="width: 500px; height:186.83651804670913px;" src="http://ondemanddevelopers.co.uk/media/38822/image.png?width=500&amp;amp;height=186.83651804670913" alt="" data-id="3015"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p style="font-weight: 400;"&gt;You can validate this by using the old version of the editor which you can find in the top right:&lt;/p&gt;
&lt;p style="font-weight: 400;"&gt;&lt;img style="width: 500px; height:82.77777777777779px;" src="http://ondemanddevelopers.co.uk/media/38826/image.png?width=500&amp;amp;height=82.77777777777779" alt="" data-id="3019"&gt;&lt;/p&gt;
&lt;p style="font-weight: 400;"&gt;All variants are updated and now the Variants have the correct parent SKU &lt;span&gt;WES9999275&lt;/span&gt;:&lt;/p&gt;
&lt;p style="font-weight: 400;"&gt;&lt;img style="width: 500px; height:191.89765458422175px;" src="http://ondemanddevelopers.co.uk/media/38824/image.png?width=500&amp;amp;height=191.89765458422175" alt="" data-id="3017"&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 11 Mar 2025 11:25:16 Z</pubDate>
      <a10:updated>2025-03-11T11:25:16Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3013</guid>
      <link>http://ondemanddevelopers.co.uk/blog/an-invalid-parameter-or-option-was-specified-for-procedure-syssp_change_users_login/</link>
      <category>Script</category>
      <title>An invalid parameter or option was specified for procedure 'sys.sp_change_users_login'.</title>
      <description>&lt;p&gt;Today, we encountered a new issue that we had not seen before. When trying to update a database's logins after restoring it from prod, the usual stored procedure we run (sys.sp_change_users_login) didn't seem to function as we would expect.&lt;/p&gt;
&lt;pre class="brush:sql"&gt;EXEC sp_change_users_login 'Auto_Fix', 'YOURUSERNAMEHERE'&lt;/pre&gt;
&lt;p&gt;No matter what we did, that always resulted in the error message:&lt;/p&gt;
&lt;pre&gt;An invalid parameter or option was specified for procedure 'sys.sp_change_users_login'.
&lt;/pre&gt;
&lt;p&gt;After a little head-scratching, we realized the issue was because the login didn't exist so it could be linked to the database user so although the user was in the database, the login was missing on the server.&lt;/p&gt;
&lt;p&gt;It's not the greatest error message. It would be better if it said that the login was missing. Hopefully, this saves someone some time.&lt;/p&gt;</description>
      <pubDate>Thu, 06 Mar 2025 18:21:18 Z</pubDate>
      <a10:updated>2025-03-06T18:21:18Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3012</guid>
      <link>http://ondemanddevelopers.co.uk/blog/the-ultimate-ai-powered-video-generation-guide-from-text-to-talking-avatars-in-minutes/</link>
      <category>AI</category>
      <category>Expanding Your Business</category>
      <title>The Ultimate AI-Powered Video Generation Guide: From Text to Talking Avatars in Minutes</title>
      <description>&lt;p&gt;Embarking on the journey of AI-driven video generation can feel like stepping into a sci-fi novel. But with the right tools and a sprinkle of guidance, you’ll be crafting compelling content faster than you can say “artificial intelligence.”&lt;/p&gt;
&lt;p&gt;Let’s face it—creating high-quality video content is a headache. You need scripts, voiceovers, decent production values, and a budget that doesn’t make your CFO cry. But what if you could automate the whole thing and produce professional-looking AI-driven videos in minutes?&lt;/p&gt;
&lt;p&gt;Good news: you can.&lt;/p&gt;
&lt;p&gt;In this guide, we’ll break down exactly how to turn articles and insights into polished AI-generated videos with realistic avatars, compelling narration, and seamless automation. Whether you’re dipping your toes into AI or want to scale your video production without scaling your stress levels, this one’s for you.&lt;/p&gt;
&lt;h2&gt;Why AI-Generated Video?&lt;/h2&gt;
&lt;p&gt;Before we get into the nuts and bolts, let’s address the obvious question: why bother with AI video?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Speed &amp;amp; Efficiency - Traditional video production can take hours or days. AI video tools reduce that to minutes.&lt;/li&gt;
&lt;li&gt;Cost Reduction - Hiring voice actors, editors, and production teams add up. AI tools handle everything at a fraction of the price.&lt;/li&gt;
&lt;li&gt;Scalability - Need 10 videos a week? 100? AI doesn’t get tired.&lt;/li&gt;
&lt;li&gt;Experimentation Without Risk - Testing different tones, messaging, or formats is easy when AI does the heavy lifting.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, how do you actually do it? Let’s dive in.&lt;/p&gt;
&lt;h2&gt;The 10-Step AI Video Workflow&lt;/h2&gt;
&lt;p&gt;We have a refined automated process built in Python that takes raw articles and turns them into AI-generated videos in about &lt;span class="s1"&gt;a minute end-to-end&lt;/span&gt;. However, here is a similar process using readily available tools if you want to DIY it.&lt;/p&gt;
&lt;p&gt;Here’s how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Gather Your Content&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Start with source material—this could be blog posts, reports, news articles, or even a collection of insights from multiple sources.&lt;/p&gt;
&lt;p&gt;✅ Google’s NotebookLM (&lt;a href="https://notebooklm.google.com" target="_blank"&gt;Try it here&lt;/a&gt;) is great for pulling together long-form content into structured notes or STORM from Standford (&lt;a href="https://storm.genie.stanford.edu/" target="_blank"&gt;Try it here&lt;/a&gt;) is great at writing long form content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Summarise Each Article&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Rather than dumping raw text into a script, summarise the key points.&lt;/p&gt;
&lt;p&gt;✅ ChatGPT (&lt;a href="https://chatgpt.com" target="_blank"&gt;Try it here&lt;/a&gt;) and Claude (&lt;a href="https://claude.ai" target="_blank"&gt;Try it here&lt;/a&gt;) both excel at summarisation. Try giving clear, structured prompts like:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Summarise this article in 3 key bullet points suitable for a video script.”&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Summarise the Summaries (MapReduce Method)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we layer in efficiency. Instead of handling each article individually, distil all the summaries into a single, concise meta-summary.&lt;/p&gt;
&lt;p&gt;🔥 Bonus Pub Fact: This technique is called &lt;span class="s1"&gt;MapReduce&lt;/span&gt;, and now you sound like an AI pro.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Convert the Summary into a Video Script&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we shape our final script. Keep it concise, conversational, and engaging.&lt;/p&gt;
&lt;p&gt;✅ Use ChatGPT or Claude for scriptwriting, with prompts like:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Turn this summary into an engaging, human-like video script. Keep sentences short, clear, and punchy.”&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 5: Write an Attention-Grabbing Intro&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Your video needs a &lt;span class="s1"&gt;hook&lt;/span&gt; to stop people from scrolling.&lt;/p&gt;
&lt;p&gt;✅ Use ChatGPT with prompts like:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“Generate a witty, attention-grabbing introduction for a {INSERT YOUR INDUSTRY HERE} professional. Start with a compelling 4-5 second hook based on the most impactful story from the summaries provided. The intro should be engaging and include a satirical observation.”&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 6: Write an Outro That Encourages Engagement&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whether you want people to comment, share, or visit your website, your outro should subtly guide them there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 7: Refine the Script&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is an important new step in our workflow. AI-generated text is good but benefits from minor tweaks—adjusting phrasing, adding humour, or ensuring clarity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 8: Generate Hashtags &amp;amp; Video Descriptions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hashtags help your video reach the right audience. AI can generate the best ones for your content.&lt;/p&gt;
&lt;p&gt;✅ Use ChatGPT with:&lt;/p&gt;
&lt;p&gt;“Generate 5-7 hashtags for this script that maximise engagement on LinkedIn and TikTok.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 9: Generate the Voiceover&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A robotic voice will kill your video before it even starts. Luckily, AI-powered voice synthesis has come a long way.&lt;/p&gt;
&lt;p&gt;✅ ElevenLabs (&lt;a href="https://try.elevenlabs.io/dhx3igatxqqk" target="_blank"&gt;Try it here&lt;/a&gt;) creates natural, expressive speech. But punctuation matters! Experiment to get the right rhythm and pauses.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step 10: Create the AI Video Avatar&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we bring it all to life.&lt;/p&gt;
&lt;p&gt;✅ Jogg (&lt;a href="https://www.jogg.ai/?fpr=tim11" target="_blank"&gt;Try it here&lt;/a&gt;) has the best balance of realism and affordability.&lt;/p&gt;
&lt;p&gt;✅ HeyGen (&lt;a href="https://heygen.com" target="_blank"&gt;Try it here&lt;/a&gt;) is great for high-quality avatars but can feel a bit rigid.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once the avatar is synced with the voiceover, render your video, and it’s ready to go!&lt;/p&gt;
&lt;h3&gt;Bonus: Automate the Entire Workflow&lt;/h3&gt;
&lt;p&gt;Once you’re comfortable with the process, &lt;span class="s1"&gt;Zapier&lt;/span&gt; (&lt;a href="https://zapier.com" target="_blank"&gt;Try it here&lt;/a&gt;) or Make.com (&lt;a href="https://www.make.com/" target="_blank"&gt;Try it here&lt;/a&gt;) can automate the movement of content between AI tools, making the process even faster.&lt;/p&gt;
&lt;h3&gt;Where to Host &amp;amp; Share Your AI-Generated Videos&lt;/h3&gt;
&lt;p&gt;You’ve got a shiny new AI video—where should it live?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ TikTok (&lt;a href="https://www.tiktok.com/@executiveeconomics?_t=ZG-8tnpWKrWEIz&amp;amp;_r=1" target="_blank"&gt;Follow our journey here&lt;/a&gt;) for short, engaging content.&lt;/li&gt;
&lt;li&gt;✅ Vimeo (&lt;a href="https://vimeo.com" target="_blank"&gt;Try it here&lt;/a&gt;) for professional hosting and embedding.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Final Thoughts: Should You Be Using AI for Video?&lt;/h2&gt;
&lt;p&gt;If you’re looking to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;✅ Distil large documents down into smaller, more manageable formats&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;✅ &lt;/span&gt;Create more content, faster&lt;/li&gt;
&lt;li&gt;✅ Scale without increasing costs&lt;/li&gt;
&lt;li&gt;✅ Experiment with new formats and styles&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then AI video generation is absolutely worth exploring.&lt;/p&gt;
&lt;p&gt;Want to get started but not sure where to begin? We’ve spent a lot of time refining this process, so if you’re thinking about taking the plunge, feel free to reach out for a chat. No pressure, just insights.&lt;/p&gt;
&lt;p&gt;Now, go forth and let AI do the heavy lifting!&lt;/p&gt;</description>
      <pubDate>Mon, 10 Feb 2025 04:58:24 Z</pubDate>
      <a10:updated>2025-02-10T04:58:24Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3011</guid>
      <link>http://ondemanddevelopers.co.uk/blog/the-real-cost-of-building-and-running-an-efficient-engineering-team/</link>
      <category>Business</category>
      <category>Expanding Your Business</category>
      <category>Business Start-up Advice</category>
      <title>The Real Cost of Building and Running an Efficient Engineering Team</title>
      <description>&lt;p&gt;When businesses consider internalising their development efforts—whether through a self-managed offshore partner or by creating a dedicated internal team to replace an external software partner—it’s tempting to focus on the apparent cost savings of hiring a few skilled engineers. After all, the promise of reduced expenses and direct control over the work can seem like the perfect solution.&lt;/p&gt;
&lt;p&gt;However, the reality is far more complex.&lt;/p&gt;
&lt;h2&gt;It’s Not Just About Coders&lt;/h2&gt;
&lt;p&gt;Building a successful development team requires more than just great engineers. Software development is a team sport, and successful projects demand a variety of supporting roles to deliver results effectively:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Project Managers&lt;/strong&gt; to ensure timelines, priorities, and resources are managed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business Analysts&lt;/strong&gt; to bridge the gap between business needs and technical implementation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality Assurance Testers&lt;/strong&gt; to guarantee the reliability and quality of deliverables.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tech Leads or Architects&lt;/strong&gt; to set the technical vision and make key decisions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Decision-Makers&lt;/strong&gt; who oversee the strategy, provide technical direction, and ensure alignment with broader business goals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without these complementary roles, even the most talented developers can flounder. Engineers are experts at building solutions, but they often need guidance, structure, and validation to ensure their work meets the organisation’s needs.&lt;/p&gt;
&lt;h2&gt;The Hidden Risks of Building a Team&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Oversight Challenges:&lt;/strong&gt; If you’re not highly technical, how do you ensure your engineers are delivering what they promise? Delivery is king, but without the right checks and balances, it’s all too easy for “talking the talk” to overshadow actual results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quality Variability:&lt;/strong&gt; Finding great developers isn’t easy. Even seasoned professionals must “kiss a lot of frogs” to find the right talent. Key indicators can help in identifying high performers, but every hiring decision carries an element of risk—and even the best screening processes can sometimes fall short.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Team Cohesion:&lt;/strong&gt; Hiring skilled individuals doesn’t guarantee a cohesive team. Engineers, analysts, and testers must collaborate seamlessly, which requires ongoing effort, leadership, and cultural alignment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The True Price of Success&lt;/h2&gt;
&lt;p&gt;The cost of assembling a high-functioning development team isn’t just about salaries—it’s about the broader investment in creating an environment where that team can thrive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tools and infrastructure to support efficient workflows.&lt;/li&gt;
&lt;li&gt;Leadership to guide the team and make informed technical decisions.&lt;/li&gt;
&lt;li&gt;Training and development to keep skills sharp and aligned with business needs.&lt;/li&gt;
&lt;li&gt;Time spent recruiting, onboarding, and aligning individuals into a cohesive unit.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Why TSD?&lt;/h2&gt;
&lt;p&gt;At TSD, we’ve spent years refining what it takes to build and manage high-performing development teams. We’ve seen what works—and what doesn’t—through our own successes and lessons learned. With our TSD Elevate Framework, we offer more than just advice; we provide a proven system to strengthen your engineering capabilities, mitigate risks, and ensure you don’t have to face these challenges alone.&lt;/p&gt;</description>
      <pubDate>Thu, 19 Dec 2024 08:43:32 Z</pubDate>
      <a10:updated>2024-12-19T08:43:32Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3009</guid>
      <link>http://ondemanddevelopers.co.uk/blog/mailchimpnetcoremailchimpexception-akamai_503/</link>
      <category>Development</category>
      <title>MailChimp.Net.Core.MailChimpException: akamai_503</title>
      <description>&lt;p&gt;We hit an interesting error the other day that doesn't appear to be covered much on Google, so hopefully, this helps someone else.&lt;/p&gt;
&lt;p&gt;After sustained attacks on one of our busier sites, we started getting our direct MailChimp API requests rejected with a generic error:&lt;/p&gt;
&lt;pre&gt;MailChimp.Net.Core.MailChimpException: Title: akamai_503
Type: akamai_error_message
Status: 503
Instance:
Detail:
Errors:
Request URI: https://us14.api.mailchimp.com/3.0/lists/xyz/members/xyz
&lt;/pre&gt;
&lt;p&gt;We use the &lt;a href="https://www.nuget.org/packages/MailChimp.Net.V3"&gt;MailChimp.Net.V3 nuget package&lt;/a&gt;, and it had been working fine for well over a year, so we figured it was related to the attacks, but there wasn't much to go on there or on Google.&lt;/p&gt;
&lt;p&gt;MailChimp support wasn't aware of the issue and suggested enabling debugging the API calls; we're still waiting on the outcome, but suspect that this happens sooner in the lifecycle, so it wouldn't turn up in their logs.&lt;/p&gt;
&lt;p&gt;After a while, we were able to find this IP checker that you can visit from your server, and it'll tell you what reputation you have: &lt;a href="https://www.akamai.com/us/en/clientrep-lookup/"&gt;https://www.akamai.com/us/en/clientrep-lookup/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As you can see, we had been flagged as a "Web Attacker" -which is understandable based on the number of fake signups we'd had.&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 500px; height: 211.89839572192514px;" src="http://ondemanddevelopers.co.uk/media/38821/mailchimpakamai.png?width=500&amp;amp;height=211.89839572192514" alt="" data-id="3008"&gt;&lt;/p&gt;
&lt;p&gt;Submitting the request to investigate it with Akamai was a quick and simple process and had us up and running again within a few hours looking at the logs.&lt;/p&gt;
&lt;p&gt;Surprisingly the MailChimp client doesn't validate the data being sent -so validate the email addresses before sending them over. We would also recommend debouncing/caching checks where possible to further reduce the calls to the API.&lt;/p&gt;</description>
      <pubDate>Thu, 15 Feb 2024 20:28:14 Z</pubDate>
      <a10:updated>2024-02-15T20:28:14Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">3007</guid>
      <link>http://ondemanddevelopers.co.uk/blog/export-all-products-properties-and-categories-from-ucommerce/</link>
      <category>uCommerce</category>
      <title>Export all products, properties and categories from uCommerce</title>
      <description>&lt;p&gt;A while ago I &lt;a data-id="2689" href="/blog/export-all-products-and-properties-from-ucommerce/" title="Export all products and properties from uCommerce"&gt;posted about how to Export products from Ucommerce into a CSV type format&lt;/a&gt;. The database schema has changed slightly since so today I'm posting an updated version of the code for v9. I've also added a second script that outputs the categories and list of products that are within the category which may be of use to some.&lt;/p&gt;
&lt;h2&gt;Export Ucommerce Products&lt;/h2&gt;
&lt;pre class="brush: sql;"&gt;DECLARE @cols AS NVARCHAR(MAX), @query  AS NVARCHAR(MAX);

SET @cols = STUFF((
            SELECT ',' + QUOTENAME(pdf.Name)
            FROM uCommerce_ProductDefinitionField AS pdf LEFT JOIN uCommerce_ProductDefinition pd ON pdf.ProductDefinitionId = pd.ProductDefinitionId
            WHERE pdf.Deleted='0' AND pd.Deleted='0'
            GROUP BY pdf.Name
            ORDER BY MIN(pdf.SortOrder) ASC
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)'),1,1,'')

SET @query = 'SELECT 
    pvt.ProductId
    ,    p.Sku
    ,    p.VariantSku
    ,    p.Name
    ,   pgp.Amount AS [Price]
    ,    CASE p.DisplayOnSite WHEN ''1'' THEN ''Y'' ELSE ''N'' END AS [Show On Site]
    ,    CASE p.AllowOrdering WHEN ''1'' THEN ''Y'' ELSE ''N'' END AS [Allow Ordering]
    ,   def.Name AS [Product Type]
    ,    p.CreatedOn
    ,    p.ModifiedOn
    ,    pd.DisplayName
    ,    pd.ShortDescription
    ,    pd.LongDescription
    ,    p.Rating
    , ' + @cols + '
    FROM
    (
        SELECT
                pp.ProductId
            ,    ppdf.Name AS [PropertyName]
            ,    pp.Value AS [PropertyValue]
        FROM
            uCommerce_ProductProperty pp
                LEFT JOIN uCommerce_ProductDefinitionField ppdf ON pp.ProductDefinitionFieldId = ppdf.ProductDefinitionFieldId
        WHERE
            ppdf.Deleted = ''0''

    UNION ALL

        SELECT
                pd.ProductId
            ,    pdpdf.Name AS [PropertyName]
            ,    pdp.Value AS [PropertyValue]
        FROM
            uCommerce_ProductDescription pd
                LEFT JOIN uCommerce_ProductDescriptionProperty pdp ON pd.ProductDescriptionId = pdp.ProductDescriptionId
                LEFT JOIN uCommerce_ProductDefinitionField pdpdf ON pdp.ProductDefinitionFieldId = pdpdf.ProductDefinitionFieldId
        WHERE
            pdpdf.Deleted = ''0''
    ) AS x
    PIVOT 
    (
        MAX([PropertyValue])
        FOR [PropertyName] IN (' + @cols + ')
    ) AS pvt
        LEFT JOIN uCommerce_Product p ON pvt.ProductId = p.ProductId
        LEFT JOIN uCommerce_ProductDefinition def ON p.ProductDefinitionId = def.ProductDefinitionId
        LEFT JOIN uCommerce_ProductDescription pd ON p.ProductId = pd.ProductId
        LEFT JOIN uCommerce_ProductPrice pp ON p.ProductId = pp.ProductId
        LEFT JOIN uCommerce_Price pgp ON pp.PriceId=pgp.PriceId
        INNER JOIN uCommerce_PriceGroup pg ON pgp.PriceGroupId = pg.PriceGroupId
    WHERE
        def.Deleted = ''0''
        AND pg.Deleted = ''0''
    ORDER BY
        p.Sku
        , p.VariantSku
        , p.Name
'

PRINT(@query)
EXECUTE(@query);
&lt;/pre&gt;
&lt;h2&gt;Export Ucommerce Category Hierarchy&lt;/h2&gt;
&lt;pre class="brush: sql;"&gt;WITH Cats (CategoryId, ParentCategoryId, CategoryName, CategoryPath, SortOrder)
AS
(
    SELECT
        c.CategoryId
        , c.ParentCategoryId
        , LTRIM(RTRIM(c.Name))
        , CAST(LTRIM(RTRIM(c.Name)) AS NVARCHAR(MAX))
        , CAST(REPLACE(STR(c.SortOrder, 4), SPACE(1), '0') AS VARCHAR(255))
    FROM uCommerce_Category c
    WHERE
        c.Deleted = '0'
        AND c.ParentCategoryId IS NULL
    
    UNION ALL

    SELECT
        ic.CategoryId
        , ic.ParentCategoryId
        , LTRIM(RTRIM(ic.Name))
        , CONCAT(c.[CategoryPath], ' &amp;gt; ', LTRIM(RTRIM(ic.Name)))
        , CAST(CONCAT(c.[SortOrder], '&amp;gt;', REPLACE(STR(ic.SortOrder, 4), SPACE(1), '0')) AS VARCHAR(255))
    FROM 
        uCommerce_Category ic INNER JOIN Cats c ON ic.ParentCategoryId = c.CategoryId
    WHERE
        ic.Deleted = '0'
)
SELECT
    c.CategoryId
    , c.ParentCategoryId
    , c.CategoryName
    , c.CategoryPath
    , STRING_AGG (CAST(cpr.ProductId AS NVARCHAR(MAX)), ', ') AS ProductIds
FROM 
    Cats c
        LEFT JOIN uCommerce_CategoryProductRelation cpr ON c.CategoryId = cpr.CategoryId
GROUP BY
    c.CategoryId
    , c.ParentCategoryId
    , c.CategoryName
    , c.CategoryPath
    , c.SortOrder
ORDER BY
    c.SortOrder
&lt;/pre&gt;</description>
      <pubDate>Thu, 01 Feb 2024 10:00:17 Z</pubDate>
      <a10:updated>2024-02-01T10:00:17Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2989</guid>
      <link>http://ondemanddevelopers.co.uk/blog/how-to-store-a-basket-against-a-user-in-ucommerce-efficiently/</link>
      <title>How to store a basket against a user in Ucommerce efficiently</title>
      <description>&lt;p&gt;TLDR; If you want to tie a basket to the logged in user, don't follow the Ucommerce documentation if you're using a platform with a Guid as the member ID as it's a performance nightmare, instead &lt;a href="#the-solution"&gt;use our alternative approach&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The Issue&lt;/h2&gt;
&lt;p&gt;We recently had an issue where a client informed us that their E-commerce website was timing out during the checkout flow, causing issues where payment was taken and the basket was not updated as expected. This particular website has the requirement where a UCommerce basket should only be tied to a logged in user, as you must be logged in to add a product to the cart, and thus can’t use the default cookie-based mechanism that ties a basket to a particular browser.&lt;/p&gt;
&lt;p&gt;We followed the initial instructions from UCommerce documentation (&lt;a href="https://docs.ucommerce.net/ucommerce/v9.7/extending-ucommerce/change-default-basket-behavior.html"&gt;https://docs.ucommerce.net/ucommerce/v9.7/extending-ucommerce/change-default-basket-behavior.html&lt;/a&gt; ), which is to inherit from the &lt;strong&gt;OrderContext&lt;/strong&gt; class and implement overrides to &lt;strong&gt;GetBasket()&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The main change to this code is to check if the user is logged in first and if they are, instead of just creating a basket and storing it in a cookie, will instead create an order property on the basket PurchaseOrder with the members id and store that, then when retrieving the basket, will search for all PurchaseOrders that have the order property of ‘MemberId’ and check the value is the same as the current logged in user and then check they are in Basket state, rather than just checking the cookie in the browser.&lt;/p&gt;
&lt;p&gt;If the user is not logged in, a default basket will still be created that is lost when they do log in as per the original implementation in &lt;strong&gt;OrderContext&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The issue that we have faced, which is not highlighted is that this approach was most likely created under the impression that a memberId is an integer value, which for the CMS system this website uses is not the case as it is a guid. It also checks for the OrderStatus of Basket last, which means it does more work up front, before limiting down the result to just Basket Orders.&lt;/p&gt;
&lt;p&gt;That means that this statement is not performant as it is doing a lookup on a string Guid in a table that is joined to the Purchase Order table for all orders, then filtering by basket. This was the root cause of the issue we were seeing. It could also just do &lt;strong&gt;.Any()&lt;/strong&gt; instead of&lt;strong&gt; .Count() &amp;gt; 0&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;PurchaseOrder order = PurchaseOrder.SingleOrDefault(x =&amp;gt; x.OrderProperties.Where(y =&amp;gt; y.Order.OrderId == x.OrderId
&amp;amp;&amp;amp; y.Key == "MemberId"
&amp;amp;&amp;amp; y.Value == MemberService.GetCurrentMember().MemberId.ToString()
).Count() &amp;gt; 0
&amp;amp;&amp;amp; x.OrderStatus.OrderStatusId == 1); // 1 == BasketId&lt;/pre&gt;
&lt;p&gt;It is also worth mentioning that it uses SingleOrDefault, which is not performant as it can just be FirstOrDefault. SingleOrDefault will loop the entire collection to ensure there is only 1 and throw an expection, whereas FirstOrDefault will leave the iterator as soon as it finds a match.​&lt;/p&gt;
&lt;h2 id="the-solution"&gt;An Alternative Approach&lt;/h2&gt;
&lt;p&gt;To still have the functionality we need, but without this cumbersome LINQ/SQL statement, it seemed appropriate to instead take the MemberId, which as we know is a guid, and just assign that as the BasketId on the purchase order when it is created. This works as the BasketId is a transient value, which is converted to NULL on a purchase order once the checkout pipeline has run. When a user finishes their order and completes check-out and then come back to the site, they will get a new basket with their memberId and the process starts all over again.&lt;/p&gt;
&lt;p&gt;The above statement therefore becomes this when searching for a basket&lt;/p&gt;
&lt;pre&gt;var order = PurchaseOrder.FirstOrDefault(x =&amp;gt; x.OrderStatus.OrderStatusId == (int)PurchaseOrder.StatusCode.Basket &amp;amp;&amp;amp; x.BasketId == MemberService.GetCurrentMember().MemberId);&lt;/pre&gt;
&lt;p&gt;and then when setting a basket for the user the code has been changed from:&lt;/p&gt;
&lt;pre&gt;PurchaseOrder order = new PurchaseOrder();
order.OrderStatus = OrderStatus.Get((int)PurchaseOrder.StatusCode.Basket);
order.ProductCatalogGroup = store;
order.BillingCurrency = currency;
order.BasketId = Guid.NewGuid();
order.CreatedDate = DateTime.Now;
order.Save();
  
// Set the member id on the order so we can retrieve it later on
order["MemberId"] = MemberService.GetCurrentMember().MemberId.ToString();
&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre&gt;PurchaseOrder order = new PurchaseOrder
{
    OrderStatus = OrderStatus.Get((int)PurchaseOrder.StatusCode.Basket),
    ProductCatalogGroup = store,
    BillingCurrency = currency,
    BasketId = MemberService.GetCurrentMember().MemberId,
    CreatedDate = DateTime.UtcNow
};
order.Save();
&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Under the hood UCommerce translates the LINQ statement executed when searching for a user’s basket to the following SQL statements:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Original Query (Using an Order Property):&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;DECLARE @p0 NVARCHAR(4000)
DECLARE @p1 NVARCHAR(max)
DECLARE @p2 INT
DECLARE @p3 INT

SET @p0 = 'MemberId'
SET @p1 = '072a1f46-0c28-6c02-8629-ff04005ae238'
SET @p2 = 0
SET @p3 = 1
        

SELECT TOP (1) purchaseor0_.OrderId AS orderid1_83_
    ,purchaseor0_.BasketId AS basketid2_83_
    ,purchaseor0_.OrderNumber AS ordernumber3_83_
    ,purchaseor0_.CultureCode AS culturecode4_83_
    ,purchaseor0_.CreatedDate AS createddate5_83_
    ,purchaseor0_.ModifiedOn AS modifiedon6_83_
    ,purchaseor0_.CompletedDate AS completeddate7_83_
    ,purchaseor0_.Note AS note8_83_
    ,purchaseor0_.VAT AS vat9_83_
    ,purchaseor0_.OrderTotal AS ordertotal10_83_
    ,purchaseor0_.Discount AS discount11_83_
    ,purchaseor0_.DiscountTotal AS discounttotal12_83_
    ,purchaseor0_.ShippingTotal AS shippingtotal13_83_
    ,purchaseor0_.PaymentTotal AS paymenttotal14_83_
    ,purchaseor0_.TaxTotal AS taxtotal15_83_
    ,purchaseor0_.SubTotal AS subtotal16_83_
    ,purchaseor0_.OrderGuid AS orderguid17_83_
    ,purchaseor0_.Guid AS guid18_83_
    ,purchaseor0_.CustomerId AS customerid19_83_
    ,purchaseor0_.OrderStatusId AS orderstatusid20_83_
    ,purchaseor0_.CurrencyId AS currencyid21_83_
    ,purchaseor0_.ProductCatalogGroupId AS productcataloggroupid22_83_
    ,purchaseor0_.BillingAddressId AS billingaddressid23_83_
FROM Ucommerce_PurchaseOrder purchaseor0_
WHERE (
        SELECT cast(count(*) AS INT)
        FROM Ucommerce_OrderProperty orderprope1_
        WHERE purchaseor0_.OrderId = orderprope1_.OrderId
            AND (orderprope1_.OrderLineId IS NULL)
            AND (
                orderprope1_.OrderId = purchaseor0_.OrderId
                OR (orderprope1_.OrderId IS NULL)
                AND (purchaseor0_.OrderId IS NULL)
                )
            AND orderprope1_.[key] = @p0
            AND orderprope1_.Value = @p1
        ) &amp;gt; @p2
    AND purchaseor0_.OrderStatusId = @p3
&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Updated Query (Setting the MemberId as the BasketId):&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;DECLARE @p4 INT
DECLARE @p5 UNIQUEIDENTIFIER

SET @p4 = 1
SET @p5 = '072a1f46-0c28-6c02-8629-ff04005ae238'

SELECT TOP (1) purchaseor0_.OrderId AS orderid1_83_
	,purchaseor0_.BasketId AS basketid2_83_
	,purchaseor0_.OrderNumber AS ordernumber3_83_
	,purchaseor0_.CultureCode AS culturecode4_83_
	,purchaseor0_.CreatedDate AS createddate5_83_
	,purchaseor0_.ModifiedOn AS modifiedon6_83_
	,purchaseor0_.CompletedDate AS completeddate7_83_
	,purchaseor0_.Note AS note8_83_
	,purchaseor0_.VAT AS vat9_83_
	,purchaseor0_.OrderTotal AS ordertotal10_83_
	,purchaseor0_.Discount AS discount11_83_
	,purchaseor0_.DiscountTotal AS discounttotal12_83_
	,purchaseor0_.ShippingTotal AS shippingtotal13_83_
	,purchaseor0_.PaymentTotal AS paymenttotal14_83_
	,purchaseor0_.TaxTotal AS taxtotal15_83_
	,purchaseor0_.SubTotal AS subtotal16_83_
	,purchaseor0_.OrderGuid AS orderguid17_83_
	,purchaseor0_.Guid AS guid18_83_
	,purchaseor0_.CustomerId AS customerid19_83_
	,purchaseor0_.OrderStatusId AS orderstatusid20_83_
	,purchaseor0_.CurrencyId AS currencyid21_83_
	,purchaseor0_.ProductCatalogGroupId AS productcataloggroupid22_83_
	,purchaseor0_.BillingAddressId AS billingaddressid23_83_
FROM Ucommerce_PurchaseOrder purchaseor0_
WHERE purchaseor0_.OrderStatusId = @p4
	AND purchaseor0_.BasketId = @p5
&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;The Benchmark&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run to clear any cache data and level the playing field&lt;/li&gt;
&lt;li&gt;Run the original query with known values (Without Execution plan monitoring)&lt;/li&gt;
&lt;li&gt;Run the updated query with known values (Without Execution plan monitoring)&lt;/li&gt;
&lt;li&gt;Run the SQL below to see the result (remember to turn on the execution plan)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;DBCC DROPCLEANBUFFERS -- clear data cache&lt;br&gt;DBCC FREEPROCCACHE -- clear proc plan cache&lt;br&gt;SELECT TOP 1000
   creation_time
   ,last_execution_time
   ,total_physical_reads
   ,total_logical_reads
   ,total_logical_writes
   , execution_count
   , total_worker_time
   , total_elapsed_time
   , total_elapsed_time / execution_count avg_elapsed_time
   ,st.text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
ORDER BY execution_count DESC;
&lt;/pre&gt;
&lt;p&gt;&lt;img src="http://ondemanddevelopers.co.uk/media/38819/picture-0.png" alt="" data-id="3004"&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;As you can see this is a drastic reduction in the time taken for each query to run and the logical reads!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Next, repeat, but turn on the execution plan.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Original Query Execution Plan&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; &lt;img style="width: 451px; height: 156px;" src="http://ondemanddevelopers.co.uk/media/38806/picture-1.png?width=451&amp;amp;height=156" alt="" data-id="2991"&gt;&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Stats for Each Step:&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 326px; height: 179px;" src="http://ondemanddevelopers.co.uk/media/38807/picture-2.png?width=326&amp;amp;height=179" alt="" data-id="2992"&gt; &lt;/p&gt;
&lt;p&gt;&lt;img style="width: 355px; height: 311px;" src="http://ondemanddevelopers.co.uk/media/38810/picture-3.png?width=355&amp;amp;height=311" alt="" data-id="2995"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 350px; height: 299px;" src="http://ondemanddevelopers.co.uk/media/38812/picture-4.png?width=350&amp;amp;height=299" alt="" data-id="2997"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 404px; height: 296px;" src="http://ondemanddevelopers.co.uk/media/38813/picture-5.png?width=404&amp;amp;height=296" alt="" data-id="2998"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 451px; height: 304px;" src="http://ondemanddevelopers.co.uk/media/38817/picture-6.png?width=451&amp;amp;height=304" alt="" data-id="3002"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 420px; height: 271px;" src="http://ondemanddevelopers.co.uk/media/38814/picture-7.png?width=420&amp;amp;height=271" alt="" data-id="2999"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 363px; height: 297px;" src="http://ondemanddevelopers.co.uk/media/38811/picture-8.png?width=363&amp;amp;height=297" alt="" data-id="2996"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 451px; height: 308px;" src="http://ondemanddevelopers.co.uk/media/38816/picture-9.png?width=451&amp;amp;height=308" alt="" data-id="3001"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;New Solution Execution Plan&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 437px; height: 238px;" src="http://ondemanddevelopers.co.uk/media/38808/picture-10.png?width=437&amp;amp;height=238" alt="" data-id="2993"&gt; &lt;/p&gt;
&lt;p&gt;&lt;u&gt;Stats for Each Step:&lt;/u&gt;&lt;/p&gt;
&lt;p&gt; &lt;img style="width: 334px; height: 179px;" src="http://ondemanddevelopers.co.uk/media/38809/picture-11.png?width=334&amp;amp;height=179" alt="" data-id="2994"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 356px; height: 312px;" src="http://ondemanddevelopers.co.uk/media/38815/picture-12.png?width=356&amp;amp;height=312" alt="" data-id="3000"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 451px; height: 299px;" src="http://ondemanddevelopers.co.uk/media/38818/picture-13.png?width=451&amp;amp;height=299" alt="" data-id="3003"&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Hopefully you may find this useful if your ever in a similar position with either needing to tie baskets to a user, or have already done something similar, and are finding it not as performant as expected.&lt;/p&gt;</description>
      <pubDate>Fri, 08 Dec 2023 13:42:32 Z</pubDate>
      <a10:updated>2023-12-08T13:42:32Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2988</guid>
      <link>http://ondemanddevelopers.co.uk/blog/high-database-cpu-usage-while-checking-out-on-ucommerce/</link>
      <category>uCommerce</category>
      <category>Development</category>
      <title>High database CPU usage while checking out on Ucommerce</title>
      <description>&lt;p&gt;We noticed the other day that a client's site was running really slowly and when calling `ExecuteBasketPipeline` or checking out was returning a mismatch in the number of affected rows.&lt;/p&gt;
&lt;p&gt;We initially thought the issue was caused by the basket being updated twice but actually it was caused by a slow database. Digging into the query executions we found that there were missing indexes, the one causing the biggest impact was one missing from the payments:&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;That the highlighted a missing index which was:&lt;/p&gt;
&lt;pre&gt;CREATE NONCLUSTERED INDEX [IX_TSD_uCommerce_Payment] ON [dbo].[uCommerce_Payment]&lt;br /&gt;(&lt;br /&gt;    [OrderId] ASC&lt;br /&gt;)&lt;br /&gt;INCLUDE([TransactionId],[PaymentMethodName],[Created],[PaymentMethodId],[Fee],[FeePercentage],[PaymentStatusId],[Amount],[FeeTotal],[ReferenceId],[Tax],[TaxRate],[GrossAmount],[Guid]) &lt;br /&gt;WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]&lt;br /&gt;GO
&lt;/pre&gt;
&lt;p&gt;This was getting triggered by the following huuuuuge statement which is presumably generated by NHibernate. We tracked down the parameters by running this query:&lt;/p&gt;
&lt;pre&gt;SELECT TOP 10&lt;br /&gt;    * 
FROM sys.dm_exec_requests r 
    CROSS APPLY sys.dm_exec_query_plan(plan_handle) as qp
    CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) 
WHERE 
    r.database_id = DB_ID('Your-Database-Name') 
&lt;/pre&gt;
&lt;p&gt;Having performance issues with your Ucommerce instance? Give us a call on +44 121 31 45 374 or &lt;a href="mailto:clinic@tsd.digital"&gt;clinic@tsd.digital&lt;/a&gt; to see how we can help.&lt;/p&gt;
&lt;p&gt;This was the sort of error message we were seeing:&lt;/p&gt;
&lt;pre&gt; UCommerce.Pipelines.PipelineException: Exception occoured while processing pipeline 'UCommerce.Pipelines.Transactions.Baskets.Basket.BasketPipeline'. See inner exception for details. ---&amp;gt; NHibernate.StaleStateException: Batch update returned unexpected row count from update; actual row count: 16; expected: 20
   at NHibernate.AdoNet.Expectations.VerifyOutcomeBatched(Int32 expectedRowCount, Int32 rowCount)
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
   at NHibernate.AdoNet.AbstractBatcher.ExecuteBatchWithTiming(IDbCommand ps)
   at NHibernate.AdoNet.AbstractBatcher.ExecuteBatch()
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
   at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
   at NHibernate.Impl.SessionImpl.Flush()
   at UCommerce.Pipelines.Common.SavePurchaseOrderTask.Execute(PurchaseOrder purchaseOrder)
   at UCommerce.Pipelines.Pipeline`1.Execute(T subject)
   --- End of inner exception stack trace ---
   at UCommerce.Pipelines.Pipeline`1.Execute(T subject)
   at UCommerce.Transactions.TransactionLibraryInternal.ExecuteOrderPipeline(String pipelineName)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at UCommerce.Infrastructure.Interceptor.ExceptionLoggingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
   at UCommerce.Infrastructure.Interceptor.ExceptionLoggingInterceptor.Intercept(IInvocation invocation)
   at Castle.DynamicProxy.AbstractInvocation.Proceed()
&lt;/pre&gt;
&lt;p&gt;If you're interested, this was the SQL it was firing:&lt;/p&gt;
&lt;pre&gt;(@p0 uniqueidentifier,@p1 uniqueidentifier,@p2 uniqueidentifier,@p3 uniqueidentifier,@p4 uniqueidentifier,@p5 uniqueidentifier,@p6 uniqueidentifier,@p7 uniqueidentifier,@p8 uniqueidentifier,@p9 uniqueidentifier,@p10 uniqueidentifier,@p11 uniqueidentifier,@p12 uniqueidentifier)&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_, purchaseor0_.BasketId as BasketId20358_, purchaseor0_.OrderNumber as OrderNum3_20358_, purchaseor0_.CultureCode as CultureC4_20358_, purchaseor0_.CreatedDate as CreatedD5_20358_, purchaseor0_.ModifiedOn as ModifiedOn20358_, purchaseor0_.CompletedDate as Complete7_20358_, purchaseor0_.Note as Note20358_, purchaseor0_.VAT as VAT20358_, purchaseor0_.OrderTotal as OrderTotal20358_, purchaseor0_.Discount as Discount20358_, purchaseor0_.DiscountTotal as Discoun12_20358_, purchaseor0_.ShippingTotal as Shippin13_20358_, purchaseor0_.PaymentTotal as Payment14_20358_, purchaseor0_.TaxTotal as TaxTotal20358_, purchaseor0_.SubTotal as SubTotal20358_, purchaseor0_.OrderGuid as OrderGuid20358_, purchaseor0_.Guid as Guid20358_, purchaseor0_.CustomerId as CustomerId20358_, purchaseor0_.OrderStatusId as OrderSt20_20358_, purchaseor0_.CurrencyId as CurrencyId20358_, purchaseor0_.ProductCatalogGroupId as Product22_20358_, purchaseor0_.BillingAddressId as Billing23_20358_ from uCommerce_PurchaseOrder purchaseor0_ where purchaseor0_.BasketId=@p0;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderaddre1_.OrderAddressId as OrderAdd1_20322_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderaddre1_.FirstName as FirstName20322_1_, orderaddre1_.LastName as LastName20322_1_, orderaddre1_.EmailAddress as EmailAdd4_20322_1_, orderaddre1_.PhoneNumber as PhoneNum5_20322_1_, orderaddre1_.MobilePhoneNumber as MobilePh6_20322_1_, orderaddre1_.Line1 as Line7_20322_1_, orderaddre1_.Line2 as Line8_20322_1_, orderaddre1_.PostalCode as PostalCode20322_1_, orderaddre1_.City as City20322_1_, orderaddre1_.[State] as column11_20322_1_, orderaddre1_.Attention as Attention20322_1_, orderaddre1_.CompanyName as Company13_20322_1_, orderaddre1_.AddressName as Address14_20322_1_, orderaddre1_.Guid as Guid20322_1_, orderaddre1_.CountryId as CountryId20322_1_, orderaddre1_.OrderId as OrderId20322_1_ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderAddress orderaddre1_ on purchaseor0_.BillingAddressId=orderaddre1_.OrderAddressId where purchaseor0_.BasketId=@p1;&lt;br /&gt;&lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderprope1_.OrderPropertyId as OrderPro1_20325_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderprope1_.[key] as column2_20325_1_, orderprope1_.Value as Value20325_1_, orderprope1_.Guid as Guid20325_1_, orderprope1_.OrderId as OrderId20325_1_, orderprope1_.OrderLineId as OrderLin6_20325_1_, orderprope1_.OrderId as OrderId0__, orderprope1_.OrderPropertyId as OrderPro1_0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderProperty orderprope1_ on purchaseor0_.OrderId=orderprope1_.OrderId and (orderprope1_.OrderLineId is null) where purchaseor0_.BasketId=@p2;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, discounts1_.DiscountId as DiscountId20311_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, discounts1_.CampaignName as Campaign2_20311_1_, discounts1_.CampaignItemName as Campaign3_20311_1_, discounts1_.Description as Descript4_20311_1_, discounts1_.AmountOffTotal as AmountOf5_20311_1_, discounts1_.CreatedOn as CreatedOn20311_1_, discounts1_.ModifiedOn as ModifiedOn20311_1_, discounts1_.CreatedBy as CreatedBy20311_1_, discounts1_.ModifiedBy as ModifiedBy20311_1_, discounts1_.Guid as Guid20311_1_, discounts1_.OrderId as OrderId20311_1_, discounts1_.OrderId as OrderId0__, discounts1_.DiscountId as DiscountId0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_Discount discounts1_ on purchaseor0_.OrderId=discounts1_.OrderId where purchaseor0_.BasketId=@p3;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, payments1_.PaymentId as PaymentId20328_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, payments1_.TransactionId as Transact2_20328_1_, payments1_.PaymentMethodName as PaymentM3_20328_1_, payments1_.Created as Created20328_1_, payments1_.Fee as Fee20328_1_, payments1_.FeeTotal as FeeTotal20328_1_, payments1_.FeePercentage as FeePerce7_20328_1_, payments1_.Amount as Amount20328_1_, payments1_.ReferenceId as Referenc9_20328_1_, payments1_.Tax as Tax20328_1_, payments1_.TaxRate as TaxRate20328_1_, payments1_.GrossAmount as GrossAm12_20328_1_, payments1_.Guid as Guid20328_1_, payments1_.PaymentMethodId as Payment14_20328_1_, payments1_.PaymentStatusId as Payment15_20328_1_, payments1_.OrderId as OrderId20328_1_, payments1_.OrderId as OrderId0__, payments1_.PaymentId as PaymentId0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_Payment payments1_ on purchaseor0_.OrderId=payments1_.OrderId where purchaseor0_.BasketId=@p4;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderstatu1_.OrderStatusId as OrderSta1_20327_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderstatu1_.Name as Name20327_1_, orderstatu1_.Sort as Sort20327_1_, orderstatu1_.RenderChildren as RenderCh4_20327_1_, orderstatu1_.RenderInMenu as RenderIn5_20327_1_, orderstatu1_.ExternalId as ExternalId20327_1_, orderstatu1_.IncludeInAuditTrail as IncludeI7_20327_1_, orderstatu1_.AllowUpdate as AllowUpd8_20327_1_, orderstatu1_.AlwaysAvailable as AlwaysAv9_20327_1_, orderstatu1_.Pipeline as Pipeline20327_1_, orderstatu1_.AllowOrderEdit as AllowOr11_20327_1_, orderstatu1_.Guid as Guid20327_1_, orderstatu1_.NextOrderStatusId as NextOrd13_20327_1_ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderStatus orderstatu1_ on purchaseor0_.OrderStatusId=orderstatu1_.OrderStatusId where purchaseor0_.BasketId=@p5;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderstatu1_.OrderStatusAuditId as OrderSta1_20326_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderstatu1_.CreatedOn as CreatedOn20326_1_, orderstatu1_.CreatedBy as CreatedBy20326_1_, orderstatu1_.Message as Message20326_1_, orderstatu1_.Guid as Guid20326_1_, orderstatu1_.NewOrderStatusId as NewOrder6_20326_1_, orderstatu1_.OrderId as OrderId20326_1_, orderstatu1_.OrderId as OrderId0__, orderstatu1_.OrderStatusAuditId as OrderSta1_0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderStatusAudit orderstatu1_ on purchaseor0_.OrderId=orderstatu1_.OrderId where purchaseor0_.BasketId=@p6;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderlines1_.OrderLineId as OrderLin1_20323_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderlines1_.Sku as Sku20323_1_, orderlines1_.ProductName as ProductN3_20323_1_, orderlines1_.Price as Price20323_1_, orderlines1_.Quantity as Quantity20323_1_, orderlines1_.CreatedOn as CreatedOn20323_1_, orderlines1_.CreatedBy as CreatedBy20323_1_, orderlines1_.Discount as Discount20323_1_, orderlines1_.UnitDiscount as UnitDisc9_20323_1_, orderlines1_.VAT as VAT20323_1_, orderlines1_.Total as Total20323_1_, orderlines1_.VATRate as VATRate20323_1_, orderlines1_.VariantSku as VariantSku20323_1_, orderlines1_.Guid as Guid20323_1_, orderlines1_.ShipmentId as ShipmentId20323_1_, orderlines1_.OrderId as OrderId20323_1_, orderlines1_.OrderId as OrderId0__, orderlines1_.OrderLineId as OrderLin1_0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderLine orderlines1_ on purchaseor0_.OrderId=orderlines1_.OrderId where purchaseor0_.BasketId=@p7;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderlines1_.OrderLineId as OrderLin1_20323_1_, orderprope2_.OrderPropertyId as OrderPro1_20325_2_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderlines1_.Sku as Sku20323_1_, orderlines1_.ProductName as ProductN3_20323_1_, orderlines1_.Price as Price20323_1_, orderlines1_.Quantity as Quantity20323_1_, orderlines1_.CreatedOn as CreatedOn20323_1_, orderlines1_.CreatedBy as CreatedBy20323_1_, orderlines1_.Discount as Discount20323_1_, orderlines1_.UnitDiscount as UnitDisc9_20323_1_, orderlines1_.VAT as VAT20323_1_, orderlines1_.Total as Total20323_1_, orderlines1_.VATRate as VATRate20323_1_, orderlines1_.VariantSku as VariantSku20323_1_, orderlines1_.Guid as Guid20323_1_, orderlines1_.ShipmentId as ShipmentId20323_1_, orderlines1_.OrderId as OrderId20323_1_, orderlines1_.OrderId as OrderId0__, orderlines1_.OrderLineId as OrderLin1_0__, orderprope2_.[key] as column2_20325_2_, orderprope2_.Value as Value20325_2_, orderprope2_.Guid as Guid20325_2_, orderprope2_.OrderId as OrderId20325_2_, orderprope2_.OrderLineId as OrderLin6_20325_2_, orderprope2_.OrderLineId as OrderLin6_1__, orderprope2_.OrderPropertyId as OrderPro1_1__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderLine orderlines1_ on purchaseor0_.OrderId=orderlines1_.OrderId left outer join uCommerce_OrderProperty orderprope2_ on orderlines1_.OrderLineId=orderprope2_.OrderLineId where purchaseor0_.BasketId=@p8;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, orderlines1_.OrderLineId as OrderLin1_20323_1_, discount3_.DiscountId as DiscountId20311_2_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, orderlines1_.Sku as Sku20323_1_, orderlines1_.ProductName as ProductN3_20323_1_, orderlines1_.Price as Price20323_1_, orderlines1_.Quantity as Quantity20323_1_, orderlines1_.CreatedOn as CreatedOn20323_1_, orderlines1_.CreatedBy as CreatedBy20323_1_, orderlines1_.Discount as Discount20323_1_, orderlines1_.UnitDiscount as UnitDisc9_20323_1_, orderlines1_.VAT as VAT20323_1_, orderlines1_.Total as Total20323_1_, orderlines1_.VATRate as VATRate20323_1_, orderlines1_.VariantSku as VariantSku20323_1_, orderlines1_.Guid as Guid20323_1_, orderlines1_.ShipmentId as ShipmentId20323_1_, orderlines1_.OrderId as OrderId20323_1_, orderlines1_.OrderId as OrderId0__, orderlines1_.OrderLineId as OrderLin1_0__, discount3_.CampaignName as Campaign2_20311_2_, discount3_.CampaignItemName as Campaign3_20311_2_, discount3_.Description as Descript4_20311_2_, discount3_.AmountOffTotal as AmountOf5_20311_2_, discount3_.CreatedOn as CreatedOn20311_2_, discount3_.ModifiedOn as ModifiedOn20311_2_, discount3_.CreatedBy as CreatedBy20311_2_, discount3_.ModifiedBy as ModifiedBy20311_2_, discount3_.Guid as Guid20311_2_, discount3_.OrderId as OrderId20311_2_, discounts2_.OrderLineId as OrderLin2_1__, discounts2_.DiscountId as DiscountId1__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_OrderLine orderlines1_ on purchaseor0_.OrderId=orderlines1_.OrderId left outer join uCommerce_OrderLineDiscountRelation discounts2_ on orderlines1_.OrderLineId=discounts2_.OrderLineId left outer join uCommerce_Discount discount3_ on discounts2_.DiscountId=discount3_.DiscountId where purchaseor0_.BasketId=@p9;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, shipments1_.ShipmentId as ShipmentId20362_1_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, shipments1_.ShipmentName as Shipment2_20362_1_, shipments1_.CreatedOn as CreatedOn20362_1_, shipments1_.CreatedBy as CreatedBy20362_1_, shipments1_.ShipmentPrice as Shipment5_20362_1_, shipments1_.DeliveryNote as Delivery6_20362_1_, shipments1_.TrackAndTrace as TrackAnd7_20362_1_, shipments1_.Tax as Tax20362_1_, shipments1_.TaxRate as TaxRate20362_1_, shipments1_.ShipmentTotal as Shipmen10_20362_1_, shipments1_.ShipmentDiscount as Shipmen11_20362_1_, shipments1_.Guid as Guid20362_1_, shipments1_.ShipmentAddressId as Shipmen13_20362_1_, shipments1_.ShippingMethodId as Shippin14_20362_1_, shipments1_.OrderId as OrderId20362_1_, shipments1_.OrderId as OrderId0__, shipments1_.ShipmentId as ShipmentId0__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_Shipment shipments1_ on purchaseor0_.OrderId=shipments1_.OrderId where purchaseor0_.BasketId=@p10;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, shipments1_.ShipmentId as ShipmentId20362_1_, orderlines2_.OrderLineId as OrderLin1_20323_2_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, shipments1_.ShipmentName as Shipment2_20362_1_, shipments1_.CreatedOn as CreatedOn20362_1_, shipments1_.CreatedBy as CreatedBy20362_1_, shipments1_.ShipmentPrice as Shipment5_20362_1_, shipments1_.DeliveryNote as Delivery6_20362_1_, shipments1_.TrackAndTrace as TrackAnd7_20362_1_, shipments1_.Tax as Tax20362_1_, shipments1_.TaxRate as TaxRate20362_1_, shipments1_.ShipmentTotal as Shipmen10_20362_1_, shipments1_.ShipmentDiscount as Shipmen11_20362_1_, shipments1_.Guid as Guid20362_1_, shipments1_.ShipmentAddressId as Shipmen13_20362_1_, shipments1_.ShippingMethodId as Shippin14_20362_1_, shipments1_.OrderId as OrderId20362_1_, shipments1_.OrderId as OrderId0__, shipments1_.ShipmentId as ShipmentId0__, orderlines2_.Sku as Sku20323_2_, orderlines2_.ProductName as ProductN3_20323_2_, orderlines2_.Price as Price20323_2_, orderlines2_.Quantity as Quantity20323_2_, orderlines2_.CreatedOn as CreatedOn20323_2_, orderlines2_.CreatedBy as CreatedBy20323_2_, orderlines2_.Discount as Discount20323_2_, orderlines2_.UnitDiscount as UnitDisc9_20323_2_, orderlines2_.VAT as VAT20323_2_, orderlines2_.Total as Total20323_2_, orderlines2_.VATRate as VATRate20323_2_, orderlines2_.VariantSku as VariantSku20323_2_, orderlines2_.Guid as Guid20323_2_, orderlines2_.ShipmentId as ShipmentId20323_2_, orderlines2_.OrderId as OrderId20323_2_, orderlines2_.ShipmentId as ShipmentId1__, orderlines2_.OrderLineId as OrderLin1_1__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_Shipment shipments1_ on purchaseor0_.OrderId=shipments1_.OrderId left outer join uCommerce_OrderLine orderlines2_ on shipments1_.ShipmentId=orderlines2_.ShipmentId where purchaseor0_.BasketId=@p11;&lt;br /&gt; &lt;br /&gt;select purchaseor0_.OrderId as OrderId20358_0_, shipments1_.ShipmentId as ShipmentId20362_1_, discount3_.DiscountId as DiscountId20311_2_, purchaseor0_.BasketId as BasketId20358_0_, purchaseor0_.OrderNumber as OrderNum3_20358_0_, purchaseor0_.CultureCode as CultureC4_20358_0_, purchaseor0_.CreatedDate as CreatedD5_20358_0_, purchaseor0_.ModifiedOn as ModifiedOn20358_0_, purchaseor0_.CompletedDate as Complete7_20358_0_, purchaseor0_.Note as Note20358_0_, purchaseor0_.VAT as VAT20358_0_, purchaseor0_.OrderTotal as OrderTotal20358_0_, purchaseor0_.Discount as Discount20358_0_, purchaseor0_.DiscountTotal as Discoun12_20358_0_, purchaseor0_.ShippingTotal as Shippin13_20358_0_, purchaseor0_.PaymentTotal as Payment14_20358_0_, purchaseor0_.TaxTotal as TaxTotal20358_0_, purchaseor0_.SubTotal as SubTotal20358_0_, purchaseor0_.OrderGuid as OrderGuid20358_0_, purchaseor0_.Guid as Guid20358_0_, purchaseor0_.CustomerId as CustomerId20358_0_, purchaseor0_.OrderStatusId as OrderSt20_20358_0_, purchaseor0_.CurrencyId as CurrencyId20358_0_, purchaseor0_.ProductCatalogGroupId as Product22_20358_0_, purchaseor0_.BillingAddressId as Billing23_20358_0_, shipments1_.ShipmentName as Shipment2_20362_1_, shipments1_.CreatedOn as CreatedOn20362_1_, shipments1_.CreatedBy as CreatedBy20362_1_, shipments1_.ShipmentPrice as Shipment5_20362_1_, shipments1_.DeliveryNote as Delivery6_20362_1_, shipments1_.TrackAndTrace as TrackAnd7_20362_1_, shipments1_.Tax as Tax20362_1_, shipments1_.TaxRate as TaxRate20362_1_, shipments1_.ShipmentTotal as Shipmen10_20362_1_, shipments1_.ShipmentDiscount as Shipmen11_20362_1_, shipments1_.Guid as Guid20362_1_, shipments1_.ShipmentAddressId as Shipmen13_20362_1_, shipments1_.ShippingMethodId as Shippin14_20362_1_, shipments1_.OrderId as OrderId20362_1_, shipments1_.OrderId as OrderId0__, shipments1_.ShipmentId as ShipmentId0__, discount3_.CampaignName as Campaign2_20311_2_, discount3_.CampaignItemName as Campaign3_20311_2_, discount3_.Description as Descript4_20311_2_, discount3_.AmountOffTotal as AmountOf5_20311_2_, discount3_.CreatedOn as CreatedOn20311_2_, discount3_.ModifiedOn as ModifiedOn20311_2_, discount3_.CreatedBy as CreatedBy20311_2_, discount3_.ModifiedBy as ModifiedBy20311_2_, discount3_.Guid as Guid20311_2_, discount3_.OrderId as OrderId20311_2_, discounts2_.ShipmentId as ShipmentId1__, discounts2_.DiscountId as DiscountId1__ from uCommerce_PurchaseOrder purchaseor0_ left outer join uCommerce_Shipment shipments1_ on purchaseor0_.OrderId=shipments1_.OrderId left outer join uCommerce_ShipmentDiscountRelation discounts2_ on shipments1_.ShipmentId=discounts2_.ShipmentId left outer join uCommerce_Discount discount3_ on discounts2_.DiscountId=discount3_.DiscountId where purchaseor0_.BasketId=@p12;&lt;/pre&gt;</description>
      <pubDate>Tue, 28 Nov 2023 15:03:40 Z</pubDate>
      <a10:updated>2023-11-28T15:03:40Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2986</guid>
      <link>http://ondemanddevelopers.co.uk/blog/ucommerce-vague-scratch-indexer-errors-not-quite-what-it-seems/</link>
      <title>UCommerce Vague scratch indexer errors, not quite what it seems</title>
      <description>&lt;p&gt;I hit a problem recently where I was working with Ucommerce and linking it up to Sitefinity as the e-commerce solution.&lt;/p&gt;
&lt;p&gt;As part of this task due to the customer's requirements, we decided to manually set up Ucommerce and then export the &lt;span&gt;Ucommerce&lt;/span&gt;_X SQL tables in the database. This was also spurred on with a bug in &lt;span&gt;Ucommerce&lt;/span&gt; for Sitefinity, where it does not run the install scripts on installation as expected. This works well in practice as it means when this script is run against another database, the product structure, price groups, catalogs, categories, payment methods, order states, payment methods, shipping methods, email profiles/types and order series (plus more) are all set up. As SiteSync for Sitefinity does not implement uCommerce entities at this time it makes life much easier when replicating the uCommerce store structure across environments. The script will not contain things specific to an environment, such as products, orders, order lines, customers or addresses.&lt;/p&gt;
&lt;p&gt;This in theory, works perfectly; until it doesn't.&lt;/p&gt;
&lt;p&gt;When running this script onto a database for a new environment, I started getting issues with the website.&lt;/p&gt;
&lt;p&gt;Naturally, as the database structure data for uCommerce has changed, the scratch indexer needs to be run first to localise everything on the hosting server.&lt;/p&gt;
&lt;p&gt;I hit a brick wall when running the indexer and got no errors, but I found that the indexes were not getting created for 'Product'.&lt;/p&gt;
&lt;p&gt;As this is the Sitefinity CMS, this manifests as showing all website widgets as an exception message instead of rendering, which makes the website unusable.&lt;/p&gt;
&lt;p&gt;The error being thrown in the logs for these widgets is:&lt;/p&gt;
&lt;pre&gt;"Type : Ucommerce.Search.Lucene.Indexes.IndexMissingError, Ucommerce.Search.Lucene, Version=9.5.1.21265, Culture=neutral, PublicKeyToken=null
Message : Searching for Product in English, I did not find the index files at '\App_Data/Ucommerce/Indexes/B/Product/DefaultProductsIndexDefinition/24473202/en'. Please check the base path in ucommerce/Configuration/Search.config and index everything from scratch.
Source : Ucommerce.Search.Lucene
Help link :
Data : System.Collections.ListDictionaryInternal
TargetSite : Lucene.Net.Facet.Taxonomy.SearcherTaxonomyManager GetManager(Ucommerce.Search.Lucene.IndexLocation)
HResult : -2146233088
Stack Trace :    at Ucommerce.Search.Lucene.Indexes.DiskIndex`1.GetManager(IndexLocation location)
   at Ucommerce.Search.Lucene.Indexes.DiskIndex`1.Find[TProjection](CultureInfo culture)"
&lt;/pre&gt;
&lt;p&gt;However, the solution given is to run the scratch indexer again, which reports no error messages when it has finished execution.&lt;/p&gt;
&lt;p&gt;After some pondering, I realised that the message is not actually telling me that it can't find 'en' Product indexes that it is expecting to be there and that they have just not been generated. But they were not generated because no Products are defined in the database.&lt;/p&gt;
&lt;p&gt;Thinking about the default install process that uCommerce ships with, this starts to make sense when, by Default UCommerce ships with a uCommerce store, several catalogs and default products (which no one ever asked for).&lt;/p&gt;
&lt;p&gt;This then makes more sense as to why these are there when it is an unwritten requirement to have at least one product, price group and catalog.&lt;/p&gt;
&lt;p&gt;Annoyingly the error message from the logs, the uCommerce documentation or in the uCommerce UI, nowhere does not inform you that there must be at least one product defined for the indexer to work. The uCommerce UI also does not stop you from deleting all products defined and having a website that is then broken when you next index. The only fix then is to manually trawl the SQL tables and create a placeholder product with a sql script. Another side note on this is that the product must also have 'DisplayOnSite' = True for the product to be indexed.&lt;/p&gt;
&lt;p&gt;The main takeaway with this is that if you are using uCommerce and you are having head-scratching issues with uCommerce's scratch indexer missing index files that you are not sure about. Ensure you have at least one of the types of index defined before indexing.&lt;/p&gt;
&lt;p&gt;It sounds obvious now, but if you don't know, you don't know.&lt;/p&gt;
&lt;p&gt;If you're in this boat, you may find this script helpful for creating a default product to get you out of this jam:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre&gt;-- Create a default product
	DECLARE @PriceGroupId int;
	SET @PriceGroupId = 
	DECLARE @ProductDefinitionId int;
	SET @ProductDefinitionId = 
--Just creates with a price of 0
 INSERT INTO  [dbo].[uCommerce_Price] VALUES(0, @PriceGroupId, NEWID())
	DECLARE @PriceId int;
	SET @PriceId = (SELECT TOP 1 PriceId From [dbo].[uCommerce_Price])
	--NOTE UCommerce MUST have at least one product defined and it must be Display On Site = 1 for the indexer to be able to run. Go Figure....
	INSERT INTO  [dbo].[uCommerce_Product] VALUES (NULL, 'DEFAULT', null, 'DEFAULT', 1, null, null, 0, @ProductDefinitionId, 0, 'System', GETUTCDATE(), GETUTCDATE(), 'System', null, NEWID());
		DECLARE @ProductId int;
	SET @ProductId = (SELECT TOP 1 ProductId From [dbo].[uCommerce_Product])
		INSERT INTO [dbo].[uCommerce_ProductPrice] VALUES (1, NEWID(), @ProductId, @PriceId)
-- Check what localisations you have defined in your languages table or if you have a category use that and add for each as below. You may not have these 10 cultures that i have..

INSERT INTO [dbo].[uCommerce_ProductDescription] 
SELECT @ProductId, 'DEFAULT', '','', d.CultureCode, NEWID() 

FROM (SELECT DISTINCT cd.[CultureCode] FROM uCommerce_CategoryDescription cd) d
 ===
-- Finally, query what productDefinitionFieldIds you have for the @ProductDefinitionId you have used above and set a default value for each ProductDefinitionId returned:
--  = SELECT  [ProductDefinitionFieldId]  FROM [dbo].[uCommerce_ProductDefinitionField] where ProductDefinitionId = @ProductDefinitionId and Deleted = 0
INSERT INTO [dbo].[uCommerce_ProductProperty] VALUES ('  ', , @ProductId, NEWID())
&lt;/pre&gt;</description>
      <pubDate>Wed, 12 Jul 2023 16:34:52 +0100</pubDate>
      <a10:updated>2023-07-12T16:34:52+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2984</guid>
      <link>http://ondemanddevelopers.co.uk/blog/1p-rounding-issue-on-ucommerce-productsdiscounts/</link>
      <category>uCommerce</category>
      <category>Useful Script</category>
      <title>1p rounding issue on Ucommerce products/discounts</title>
      <description>&lt;p&gt;We've been hitting an issue over the past few years where the product's price is `1p` out in certain circumstances e.g. £2 off 2 £30 items -it should equal £56.00 but often results in £55.99.&lt;/p&gt;
&lt;p&gt;The fix is surprisingly (annoyingly) simple -you need to alter the precision of the amount off award columns in Ucommerce, which are set to 2dp:&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 373px; height: 412px;" src="http://ondemanddevelopers.co.uk/media/38805/2023-03-07_11-51-14.png?width=373&amp;amp;height=412" alt="" data-id="2983"&gt;&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Changing these to `decimal(18,6)` means Ucommerce calculates everything with sufficient precision to result in correct rounding.&lt;/p&gt;
&lt;p&gt;You can do that like this:&lt;/p&gt;
&lt;pre class="brush: sql;"&gt;ALTER TABLE [dbo].[uCommerce_AmountOffUnitAward] ALTER COLUMN AmountOff decimal(18,6);
ALTER TABLE [dbo].[uCommerce_AmountOffOrderLinesAward] ALTER COLUMN AmountOff decimal(18,6);
ALTER TABLE [dbo].[uCommerce_AmountOffOrderTotalAward] ALTER COLUMN AmountOff decimal(18,6);
&lt;/pre&gt;
&lt;p&gt;Sometimes you will need to update the `NumberOfDigitsPrecision` setting in  `www/umbraco/Ucommerce/Configuration/Settings/Settings.config` and change it to `5`.&lt;/p&gt;
&lt;p&gt;The Math&lt;/p&gt;
&lt;p&gt;VAT in the UK is 20% and Ucommerce requires all prices to be input excluding VAT.&lt;/p&gt;
&lt;p&gt;£30.00 exc VAT: £25.00 (this is what’s entered in Ucommrece)&lt;br&gt;£2.00 exc VAT: £1.66667 (but we can only enter £1.67 in Ucommerce due to the precision)&lt;br&gt; &lt;br&gt;The desired/expected total when ordering 2 is: £56.00.&lt;br&gt; &lt;br&gt;When calculating it out at 2dp:&lt;br&gt; &lt;br&gt;Unit Price: 25.00 – 1.67 = 23.33&lt;br&gt;Line Total: 23.33 * 2 = 46.66&lt;br&gt;Order Total: 46.66 * 1.2 = 55.992&lt;br&gt; &lt;br&gt;That then rounds down to £55.99&lt;br&gt; &lt;br&gt;If you calculate it to 5dp:&lt;br&gt;Unit Price: 25.00 – 1.66667 = 23.33333&lt;br&gt;Line Total: 23.33 * 2 = 46.666666&lt;br&gt;Order Total: 46.66 * 1.2 = 55.99992&lt;br&gt; &lt;br&gt;That then rounds to £56.00&lt;/p&gt;</description>
      <pubDate>Tue, 07 Mar 2023 12:02:15 Z</pubDate>
      <a10:updated>2023-03-07T12:02:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2982</guid>
      <link>http://ondemanddevelopers.co.uk/blog/sitefinityucommerce-admin-area-the-content-is-blocked-contact-the-site-owner-to-fix-the-issue/</link>
      <title>Sitefinity/Ucommerce admin area - The content is blocked. Contact the site owner to fix the issue.</title>
      <description>&lt;p&gt;Loading the Ucommerce admin area in Sitefinity we were hitting ​a rather unfriendly error message "The content is blocked. Contact the site owner to fix the issue.​"&lt;/p&gt;
&lt;p&gt;The fix is relatively simple -edit `App_Data/Sitefinity/Configuration/WebSecurityConfig.config​` and add "'self'" to both "frame-src" and "frame-ancestors" so it looks like this:&lt;/p&gt;
&lt;p&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;webSecurityConfig xmlns:config="urn:telerik:sitefinity:configuration" xmlns:type="urn:telerik:sitefinity:configuration:type" config:version="14.3.8000.0"&amp;gt;&lt;br /&gt; &amp;lt;httpSecurityHeaders&amp;gt;&lt;br /&gt; &amp;lt;responseHeaders&amp;gt;&lt;br /&gt; &amp;lt;add name="Content-Security-Policy" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;directives&amp;gt;&lt;br /&gt; &amp;lt;remove name="frame-src" /&amp;gt;&lt;br /&gt; &amp;lt;add name="script-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;remove value="https://www.youtube.com" /&amp;gt;&lt;br /&gt; &amp;lt;remove value="https://*.googletagmanager.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="https://www.youtube.com/iframe_api" /&amp;gt;&lt;br /&gt; &amp;lt;add value="https://dec.azureedge.net/" /&amp;gt;&lt;br /&gt; &amp;lt;add value="munchkin.marketo.net" /&amp;gt;&lt;br /&gt; &amp;lt;add value="use.fontawesome.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="stackpath.bootstrapcdn.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="style-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="img-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;remove value="i.ytimg.com" /&amp;gt;&lt;br /&gt; &amp;lt;remove value="https://*.googletagmanager.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="https://*.insight.sitefinity.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="https://*.dec.sitefinity.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.awwwards.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="font-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="frame-src"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;add value="'self'" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="connect-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;remove value="data:" /&amp;gt;&lt;br /&gt; &amp;lt;remove value="*.gstatic.com" /&amp;gt;&lt;br /&gt; &amp;lt;remove value="https://*.googletagmanager.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.mktoresp.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="media-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;add value="https://cdn.livechatinc.com/widget/static/media/new_message.a37211a6.ogg" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="child-src" config:flags="1"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;remove value="https://www.youtube-nocookie.com" /&amp;gt;&lt;br /&gt; &amp;lt;add value="*.livechatinc.com" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add name="frame-ancestors"&amp;gt;&lt;br /&gt; &amp;lt;values&amp;gt;&lt;br /&gt; &amp;lt;add value="'self'" /&amp;gt;&lt;br /&gt; &amp;lt;/values&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;/directives&amp;gt;&lt;br /&gt; &amp;lt;/add&amp;gt;&lt;br /&gt; &amp;lt;add disabled="True" name="Cross-Origin-Embedder-Policy" config:flags="1" /&amp;gt;&lt;br /&gt; &amp;lt;add disabled="True" name="Cross-Origin-Opener-Policy" config:flags="1" /&amp;gt;&lt;br /&gt; &amp;lt;add disabled="True" name="Cross-Origin-Resource-Policy" config:flags="1" /&amp;gt;&lt;br /&gt; &amp;lt;add disabled="True" name="Permissions-Policy" config:flags="1" /&amp;gt;&lt;br /&gt; &amp;lt;/responseHeaders&amp;gt;&lt;br /&gt; &amp;lt;/httpSecurityHeaders&amp;gt;&lt;br /&gt; &amp;lt;csrfProtection enable="False" /&amp;gt;&lt;br /&gt;&amp;lt;/webSecurityConfig&amp;gt;&lt;/p&gt;</description>
      <pubDate>Wed, 01 Feb 2023 16:22:17 Z</pubDate>
      <a10:updated>2023-02-01T16:22:17Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2970</guid>
      <link>http://ondemanddevelopers.co.uk/blog/delete-all-orders-from-ucommerce/</link>
      <category>Useful Script</category>
      <category>uCommerce</category>
      <title>Delete all orders from Ucommerce</title>
      <description>&lt;p&gt;A while ago I blogged about &lt;a href="https://thesitedoctor.co.uk/blog/deleting-test-orders-and-baskets-from-ucommerce/"&gt;how to delete test orders and baskets from Ucommerce&lt;/a&gt; and &lt;a href="https://thesitedoctor.co.uk/blog/delete-all-products-and-orders-from-ucommerce/"&gt;Delete all products and orders from Ucommerce&lt;/a&gt; which I frequently use.&lt;/p&gt;
&lt;p&gt;As I often want to only clear out the orders as well so I thought it worth popping together a quick script to delete only the orders. This works on Ucommerce v9 and should work for older versions too but I've not tested it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    BEGIN TRAN   
    DELETE FROM uCommerce_OrderLineDiscountRelation
    DELETE FROM uCommerce_ShipmentDiscountRelation
    DELETE FROM uCommerce_Discount
    UPDATE uCommerce_OrderLine SET ShipmentId = NULL
    UPDATE uCommerce_PurchaseOrder SET BillingAddressId = NULL
    DELETE FROM uCommerce_Shipment
    DELETE FROM uCommerce_OrderAddress
    DELETE FROM uCommerce_OrderProperty
    DELETE FROM uCommerce_OrderLine
    DELETE FROM uCommerce_PaymentProperty
    DELETE FROM uCommerce_Payment
    DELETE FROM uCommerce_OrderStatusAudit
    DELETE FROM uCommerce_PurchaseOrder
    DELETE FROM uCommerce_Address
    -- NOTE: Only needed if you use our Stripe package
    -- DELETE FROM uCommerce_StripeCustomer
    SELECT * FROM uCommerce_PurchaseOrder o
    ROLLBACK TRAN
    -- When happy it works, uncomment this line and comment out the ROLLBACK
    -- COMMIT TRAN
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Sat, 22 May 2021 10:45:00 +0100</pubDate>
      <a10:updated>2021-05-22T10:45:00+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2967</guid>
      <link>http://ondemanddevelopers.co.uk/blog/power-ucommerce-facet-display-names-from-the-cms/</link>
      <category>uCommerce</category>
      <title>Power Ucommerce facet display names from the CMS</title>
      <description>&lt;p&gt;The index changes released in v9 have been great and API is blazingly fast -however it's come with a couple of costs when it comes to configuring the Facet settings via the UI.&lt;/p&gt;
&lt;p&gt;Setting the DisplayName for the facet within code &lt;a href="https://docs.ucommerce.net/ucommerce/v9.4/migration/migrating-to-v9.4/Migrating-to-new-IndexDefinition-DSL-syntax.html"&gt;as per the documentation&lt;/a&gt; feels like a big step backwards. Don't worry though, you can work around it with this simple Extension method.&lt;/p&gt;
&lt;p&gt;Add the &lt;code&gt;FieldDefinitionExtensions&lt;/code&gt; class below to your project and then you can automatically add any descriptions like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.Field(p =&amp;gt; p[&amp;quot;YourPropertyName&amp;quot;], typeof(string)).DisplayNameFromUcommerce().Facet();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hope that helps someone else!&lt;/p&gt;
&lt;p&gt;using System.Collections.Generic;
	using System.Linq;
	using Ucommerce.EntitiesV2;
	using Ucommerce.Search;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;namespace TSD.Core.Commerce.Common.Extensions
{
    public static class FieldDefinitionExtensions
    {
        private static readonly IRepository&amp;lt;ProductDefinitionField&amp;gt; ProductDefinitionFieldRepository = Ucommerce.Infrastructure.ObjectFactory.Instance.Resolve&amp;lt;Ucommerce.EntitiesV2.IRepository&amp;lt;Ucommerce.EntitiesV2.ProductDefinitionField&amp;gt;&amp;gt;();
        private static Dictionary&amp;lt;string, ICollection&amp;lt;ProductDefinitionFieldDescription&amp;gt;&amp;gt; AllDisplayNames;

        public static FieldDefinition&amp;lt;T&amp;gt; DisplayNameFromUcommerce&amp;lt;T&amp;gt;(this FieldDefinition&amp;lt;T&amp;gt; self)
        {
            var displayName = GetProductDefinitionFieldDescriptions(self.Name);
            foreach (var description in displayName)
                self.DisplayNames[description.CultureCode] = description.DisplayName;
            return self;
        }


        private static void EnsureProductDefinitionFields()
        {
            if (AllDisplayNames != null) return;

            AllDisplayNames = new Dictionary&amp;lt;string, ICollection&amp;lt;ProductDefinitionFieldDescription&amp;gt;&amp;gt;();

            var definitionFields = ProductDefinitionFieldRepository.Select(f =&amp;gt; f.Deleted == false);

            foreach (var field in definitionFields)
            {
                if (!AllDisplayNames.ContainsKey(field.Name))
                    AllDisplayNames.Add(field.Name, field.ProductDefinitionFieldDescriptions);
            }
        }
        private static ICollection&amp;lt;ProductDefinitionFieldDescription&amp;gt; GetProductDefinitionFieldDescriptions(string key)
        {
            EnsureProductDefinitionFields();

            return (AllDisplayNames.TryGetValue(key, out var descriptions)) ? descriptions : Enumerable.Empty&amp;lt;ProductDefinitionFieldDescription&amp;gt;().ToList();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

</description>
      <pubDate>Tue, 13 Apr 2021 20:35:24 +0100</pubDate>
      <a10:updated>2021-04-13T20:35:24+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2966</guid>
      <link>http://ondemanddevelopers.co.uk/blog/error-running-scratch-indexer-in-ucommerce-v9plus/</link>
      <title>Error running scratch indexer in Ucommerce v9+</title>
      <description>&lt;p&gt;We ran into an odd issue today where we suddenly couldn't run the scratch indexer in Ucommerce after making a few changes to the product properties.&lt;/p&gt;
&lt;p&gt;We tracked the issue down to the IndexDefinition which was using non-nullable field declarations e.g.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;this.Field(p =&amp;gt; p[&amp;quot;Date&amp;quot;], typeof(DateTime));&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Resulted in &lt;em&gt;String was not recognized as a valid DateTime.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We also had an incorrect definition for a &lt;code&gt;string&lt;/code&gt; field which was getting declared as a &lt;code&gt;bool&lt;/code&gt; which resulted in &lt;em&gt;Input string was not in a correct format.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The fix was fairly simple -when using nullable properties you need to declare them as so in the IndexDefinition but it was one of those head scratchers for a while!&lt;/p&gt;
&lt;p&gt;This is how the nullable &lt;code&gt;DateTime&lt;/code&gt; declaration should look:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;this.Field(p =&amp;gt; p[&amp;quot;Date&amp;quot;], typeof(DateTime?));&lt;/code&gt;&lt;/p&gt;
</description>
      <pubDate>Mon, 05 Apr 2021 06:08:43 +0100</pubDate>
      <a10:updated>2021-04-05T06:08:43+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2964</guid>
      <link>http://ondemanddevelopers.co.uk/blog/longest-company-names-for-companies-in-the-financial-markets/</link>
      <category>Useful</category>
      <category>Design</category>
      <title>Longest Company Names for Companies in the Financial Markets</title>
      <description>&lt;p&gt;When designing websites or mobile apps it's important that you allow for the extremities in the content to avoid breaking your design -even more so if you're dealing with a data website such as an e-commerce website or financial markets.&lt;/p&gt;
&lt;p&gt;Don't be tempted to create all your designs with just &lt;strong&gt;BP&lt;/strong&gt; because it's simple, use real data, below is a list of the longest company names by major market as well as the longest part (in case you're wrapping your text). &lt;/p&gt;
&lt;table border="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Index&lt;/th&gt;
&lt;th&gt;Longest Company Name&lt;/th&gt;
&lt;th&gt;Length&lt;/th&gt;
&lt;th&gt;Longest Name Part&lt;/th&gt;
&lt;th&gt;Length&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FTSE 100&lt;/td&gt;
&lt;td&gt;International Consolidated Airlines Group SA&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;InterContinental&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FTSE 250&lt;/td&gt;
&lt;td&gt;Perpetual Income &amp;amp; Growth Investment Trust PLC&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;Moneysupermarket.com&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EURO STOXX 50&lt;/td&gt;
&lt;td&gt;Banco Bilbao Vizcaya Argentaria&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;UNIBAIL-RODAMCO&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DAX&lt;/td&gt;
&lt;td&gt;Muenchener Rueckversicherungs Gesellschaft in Muenchen AG&lt;/td&gt;
&lt;td&gt;57&lt;/td&gt;
&lt;td&gt;Rueckversicherungs&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CAC 40&lt;/td&gt;
&lt;td&gt;Essilor International Compagnie Generale d'Optique SA&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;Etablissements&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bel 20&lt;/td&gt;
&lt;td&gt;Groupe Bruxelles Lambert SA&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;Anheuser-Busch&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IBEX 35&lt;/td&gt;
&lt;td&gt;ACS Actividades de Construccion y Servicios SA&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;Infraestructuras&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NIKKEI 225&lt;/td&gt;
&lt;td&gt;Mitsui Engineering &amp;amp; Shipbuilding Co Ltd&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;Pharmaceutical&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dow 30&lt;/td&gt;
&lt;td&gt;International Business Machines Corporation&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;Communications&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NASDAQ 100&lt;/td&gt;
&lt;td&gt;Expeditors International of Washington Inc.&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;Pharmaceuticals,&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S&amp;amp;P 500&lt;/td&gt;
&lt;td&gt;Fidelity National Information Services Inc&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;AmerisourceBergen&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FTSE MIB (FTMIB) 40&lt;/td&gt;
&lt;td&gt;UnipolSai (or UnipolSai Assicurazioni) post raggruppamento&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;STMicroelectronics&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Amusingly there is a company registered in the UK called &lt;a href="https://find-and-update.company-information.service.gov.uk/company/04120480"&gt;THIS IS THE COMPANY WITH THE LONGEST NAME SO FAR INCORPORATED AT THE REGISTRY OF COMPANIES IN ENGLAND AND WALES AND ENCOMPASSING THE REGISTRIES BASED IN SCOTLAN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;</description>
      <pubDate>Mon, 08 Feb 2021 05:43:22 Z</pubDate>
      <a10:updated>2021-02-08T05:43:22Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2911</guid>
      <link>http://ondemanddevelopers.co.uk/blog/what-is-browser-testing-and-why-does-it-take-so-long/</link>
      <category>Web Development</category>
      <category>eCommerce</category>
      <title>What is browser testing and why does it take so long?</title>
      <description>&lt;p&gt;Your design is built, content loaded, media is in and your site looks great on your computer -you’re ready to launch so why the sudden hold up with browser testing and why is it going to take so long?&lt;/p&gt;
&lt;p&gt;Take this issue I ran into earlier this week. Can you spot the difference?&lt;/p&gt;
&lt;table border="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img style="width: 256px; height: 446px;" src="http://ondemanddevelopers.co.uk/media/38771/example1.png?width=256&amp;amp;height=446" alt="" data-id="2915"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img style="width: 256px; height: 446px;" src="http://ondemanddevelopers.co.uk/media/38772/example2.png?width=256&amp;amp;height=446" alt="" data-id="2916"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The headings are displaying differently that’s right! The site uses a specific font for headings which looked great on desktop and Android phones but when I checked the website on my iPhone, things didn’t look so pretty.&lt;/p&gt;
&lt;p&gt;There were no errors being thrown to indicate that there was an issue. Emulating various mobile devices with my browser hadn’t revealed the problem - it all looked great. It was only when I deployed my local website to a server and picked up my phone to take a look that I was aware of an issue. This is one of those cases that you really only discover by manually checking a website on an actual device.&lt;/p&gt;
&lt;p&gt;In this scenario the issue is cosmetic, but in some cases, it might be critical - a page can look right visually, but if a button can’t be clicked then it’s game over as far as your visitor is concerned especially if you’re building e-commerce sites like this one.&lt;/p&gt;
&lt;p&gt;It’s crucial to test your website in as many different devices, browsers and operating systems as is practical to give your visitors the best possible experience. The process of doing this is called front-end testing (in web-speak, ‘front-end’ generally refers to the parts of your website that you can see and interact with - text, links, buttons, images, forms etc. ‘Back-end`’ is about serving up the data that drives your site).&lt;/p&gt;
&lt;h2&gt;What do we check?&lt;/h2&gt;
&lt;p&gt;There are a number of things to consider when testing a web page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Visuals - does it match the design as closely as possible?&lt;/li&gt;
&lt;li&gt;Functionality - does everything work as expected? Is the content readable? Can buttons be clicked? Are there javascript errors that would prevent users logging in or adding an item to a cart?&lt;/li&gt;
&lt;li&gt;Accessibility - could the page be used by someone with impaired vision, or someone unable to use a mouse or a keyboard?&lt;/li&gt;
&lt;li&gt;Performance - does the page load and respond quickly?&lt;/li&gt;
&lt;li&gt;Knock-on effects - could there be any unexpected consequences from this work on other pages of the site? Styles and scripts are usually available across multiple sections of your site, and changes can bleed through in ways that aren’t immediately obvious.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Responsive testing&lt;/h2&gt;
&lt;p&gt;When we talk about responsive testing, we’re talking about all the checks listed above being performed on a variety of devices, screen sizes and operating systems. We generally build our sites to support four main sizes, all of which need to be tested on each supported browser/device!&lt;/p&gt;
&lt;p&gt;Let’s take a look at the top web browsers in 2020:&lt;/p&gt;
&lt;p&gt;&lt;img style="width: 0px; height: 0px;" src="http://ondemanddevelopers.co.uk/media/38773/web-browsers-2020.png?width=0&amp;amp;height=0" alt="" data-id="2917"&gt;&lt;img style="width: 500px; height: 413.5254988913525px;" src="http://ondemanddevelopers.co.uk/media/38773/web-browsers-2020.png?width=500&amp;amp;height=413.5254988913525" alt="" data-id="2917"&gt;&lt;br&gt;&lt;a href="https://www.smashingmagazine.com/2020/02/cross-browser-testing-efficient-lambdatest/"&gt;https://www.smashingmagazine.com/2020/02/cross-browser-testing-efficient-lambdatest/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My default development setup when I’m working is the latest version of Chrome on a desktop PC -which covers only 23.71% of browsers in the chart above. Mobile browsers are incredibly important, with Chrome for Android being the most popular and Safari on iOS coming in at number 3. Not to mention most websites are now well over 70% mobile traffic.&lt;/p&gt;
&lt;p&gt;So, let’s just take those top 3 browsers and go test our website. We use a tool called&lt;a href="https://www.browserstack.com/"&gt;Browserstack &lt;/a&gt;for this because it provides actual devices for testing, not just emulators or simulators which often don’t tell the whole story (as we saw in my screenshots above).&lt;/p&gt;
&lt;p&gt;Starting with Chrome for Android (our #1 browser above), browserstack has 63 devices from 6 different manufacturers and a number of operating system versions:&lt;br&gt;&lt;img src="http://ondemanddevelopers.co.uk/media/38775/web-browser-2020-androidchrome.png?width=500&amp;amp;height=247.22838137472283" alt="" data-id="2919"&gt;&lt;/p&gt;
&lt;p&gt;Looking at Chrome on a PC, we’ve got 48 versions of Chrome and 5 different Windows operating system versions to choose from:&lt;br&gt;&lt;img style="width: 500px; height: 250.55432372505544px;" src="http://ondemanddevelopers.co.uk/media/38770/web-browser-2020-chromepc.png?width=500&amp;amp;height=250.55432372505544" alt="" data-id="2914"&gt;&lt;/p&gt;
&lt;p&gt;Not to mention 46 versions of Chrome and 10 different MacOS versions:&lt;br&gt;&lt;img style="width: 500px; height: 254.9889135254989px;" src="http://ondemanddevelopers.co.uk/media/38774/web-browser-2020-macchrome.png?width=500&amp;amp;height=254.9889135254989" alt="" data-id="2918"&gt;&lt;/p&gt;
&lt;p&gt;Our third browser, Safari on iPhone, can be tested on 25 different devices:&lt;br&gt;&lt;img style="width: 500px; height: 254.9889135254989px;" src="http://ondemanddevelopers.co.uk/media/38776/web-browser-2020-iphone.png?width=500&amp;amp;height=254.9889135254989" alt="" data-id="2920"&gt;&lt;/p&gt;
&lt;p&gt;All this, and we haven’t even started looking at other browsers (Firefox, Edge, Internet Explorer 11…).&lt;/p&gt;
&lt;p&gt;Unless your project has an unlimited budget, testing every possible setup after every change just isn’t practical. To keep things moving in a reasonable timeframe, we may limit our testing to just the most up-to-date browsers on a few recent devices. We also focus on the areas of the site which may have been impacted -and have a quick check over the others just to make sure.&lt;/p&gt;
&lt;p&gt;Data from Google Analytics or similar can help us choose the most important targets for your website which helps streamline the process but even so, thorough testing takes time.&lt;/p&gt;
&lt;p&gt;Generally we test each page/journey in 38 different device/browser combinations (unless requested otherwise):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PC&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Chrome (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;Edge (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;Firefox (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;IE11: 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;IE10: 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;Mac&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Safari (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;Chrome (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;li&gt;Firefox (latest): 1024x768 / 1280x1024 / 1920x1280&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;iPhone 6&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Safari (latest): Portrait / Landscape&lt;/li&gt;
&lt;li&gt;Chrome (latest): Portrait / Landscape&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;iPhone 11&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Safari (latest): Portrait / Landscape&lt;/li&gt;
&lt;li&gt;Chrome (latest): Portrait / Landscape&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;iPad Pro&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Safari (latest): Portrait / Landscape&lt;/li&gt;
&lt;li&gt;Chrome (latest): Portrait / Landscape&lt;/li&gt;
&lt;/ol&gt;
&lt;li&gt;Android&lt;/li&gt;
&lt;ol&gt;
&lt;li&gt;Chrome (latest): Portrait / Landscape&lt;/li&gt;
&lt;/ol&gt;
&lt;/ol&gt;
&lt;p&gt;That means even the most basic test which takes 5 minutes, will result in over 3 hours of testing per page! The best of it is we may find a bug on the last test which means we need to run through the entire test plan from the start!&lt;/p&gt;
&lt;p&gt;Once the initial version of the page has been created and tested, we can automate the process for future runs using tools like &lt;a href="https://www.selenium.dev/"&gt;Selenium&lt;/a&gt;. However, only helps speed up testing later down the line.&lt;/p&gt;
&lt;p&gt;So there you have it. Front end testing is almost invisible when done well, and it takes time, but it’s essential for keeping your website functioning as beautifully, quickly and error-free as possible. It’s well worth investing the time so that your visitors have the best possible experience regardless of the device or browser they’re using.&lt;/p&gt;
&lt;p&gt;So in conclusion, there are some quick wins when testing but full end-to-end browser and device testing takes time but it's time very well spent!&lt;/p&gt;</description>
      <pubDate>Thu, 09 Jul 2020 13:17:45 +0100</pubDate>
      <a10:updated>2020-07-09T13:17:45+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2908</guid>
      <link>http://ondemanddevelopers.co.uk/blog/could-not-load-file-or-assembly-xxx-or-one-of-its-dependencies/</link>
      <category>Development</category>
      <title>Could not load file or assembly xxx or one of its dependencies</title>
      <description>&lt;p&gt;We hit the fairly common issue of not being able to resolve a dependency the other day and tried all the usual resolutions -checking that the version numbers across all projects match, check the values in the configs haven't corrupted during an install, clean and rebuild the solution, updated/reinstalled the nuget packages and nothing worked, we just kept hitting&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Could not load file or assembly 'Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The fix was ridiculously simple, in short, one of the app.configs had some out of date dependencies in the redirects -in this instance pointing at a newer version of Autofac from a failed upgrade so the fix was simply to correct the mappings. I didn't know that the app.config would affect the compiled assembly but it must overwrite the referenced DLL as the referenced versions were correct&lt;/p&gt;
&lt;p&gt;So in summary, the references were changed from:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;dependentAssembly&amp;gt;
        &amp;lt;assemblyIdentity name=&amp;quot;Autofac&amp;quot; publicKeyToken=&amp;quot;17863af14b0044da&amp;quot; culture=&amp;quot;neutral&amp;quot;/&amp;gt;
        &amp;lt;bindingRedirect oldVersion=&amp;quot;0.0.0.0-3.5.0.0&amp;quot; newVersion=&amp;quot;3.5.0.0&amp;quot;/&amp;gt;
      &amp;lt;/dependentAssembly&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;dependentAssembly&amp;gt;
        &amp;lt;assemblyIdentity name=&amp;quot;Autofac&amp;quot; publicKeyToken=&amp;quot;17863af14b0044da&amp;quot; culture=&amp;quot;neutral&amp;quot;/&amp;gt;
        &amp;lt;bindingRedirect oldVersion=&amp;quot;0.0.0.0-2.6.3.862&amp;quot; newVersion=&amp;quot;2.6.3.862&amp;quot; /&amp;gt;
      &amp;lt;/dependentAssembly&amp;gt;&lt;/code&gt;&lt;/p&gt;
</description>
      <pubDate>Mon, 11 May 2020 08:51:27 +0100</pubDate>
      <a10:updated>2020-05-11T08:51:27+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2907</guid>
      <link>http://ondemanddevelopers.co.uk/blog/sitefinity-project-stuck-at-warming-up/</link>
      <category>Development</category>
      <title>Sitefinity project stuck at warming up</title>
      <description>&lt;p&gt;We've had the opportunity recently to start working with the enterprise grade CMS Sitefinity in a little more depth than our previous Ucommerce consultancy which has been a welcome break. It's been pretty straight forward to get up and running in contrast to some of the other Enterprise Grade CMS systems like Kentico and Sitecore (in a good way) but we did hit one snag which wasn't immediately obvious.&lt;/p&gt;
&lt;p&gt;If you get stuck at the installation screen which looks like this:
&lt;img src="http://ondemanddevelopers.co.uk/media/38767/2020-04-30_16-24-09-19.jpg" alt="Sitefinity" /&gt;&lt;/p&gt;
&lt;p&gt;Then it's likely you've hit the same issue we did. If you open Chrome's developer tools, you may notice that there's an erroring calls to &lt;code&gt;status&lt;/code&gt; -or if you're lucky you'll get the error screen with the error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The connection string name is missing for the MySqlSiteMapProvider (C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config line 275) ---&amp;gt; The connection string name is missing for the MySqlSiteMapProvider
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The fix is pretty simple, open up your &lt;code&gt;web.config&lt;/code&gt; file and look for the &lt;code&gt;&amp;lt;sitemap&lt;/code&gt; node then add a &lt;code&gt;&amp;lt;clear/&amp;gt;&lt;/code&gt; block at the start of the providers to clear your &lt;code&gt;machine.config&lt;/code&gt; setting:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;siteMap defaultProvider=&amp;quot;SitefinitySiteMap&amp;quot;&amp;gt;
	&amp;lt;providers&amp;gt;
		&amp;lt;clear /&amp;gt;
		&amp;lt;add name=&amp;quot;SitefinitySiteMap&amp;quot; type=&amp;quot;Telerik.Sitefinity.Web.SitefinitySiteMap, Telerik.Sitefinity&amp;quot; /&amp;gt;
	&amp;lt;/providers&amp;gt;
&amp;lt;/siteMap&amp;gt;&lt;/code&gt;&lt;/p&gt;
</description>
      <pubDate>Thu, 30 Apr 2020 16:15:16 +0100</pubDate>
      <a10:updated>2020-04-30T16:15:16+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2905</guid>
      <link>http://ondemanddevelopers.co.uk/blog/super-fast-internet-over-3g4g-without-broadband/</link>
      <category>Start-up Advice</category>
      <category>Business</category>
      <title>Super fast internet over 3G/4G... without broadband</title>
      <description>&lt;p&gt;With more of us working from home these days, getting decent broadband speeds can be tricky -especially if you work out in the countryside like we do however with a little time and money it doesn't need to be dial-up slow. &lt;/p&gt;
&lt;p&gt;Over the years we've tried a number of difference solutions including BT's Broadband, BT Fibre, Airband's long range wifi, some of which were better than others but we've settled on a 4G mobile reception solution and get average speeds of over 20MB which is more than enough to stream Netflix.&lt;/p&gt;
&lt;p&gt;Offers change all the time so the best thing to do would be to pop into your local Three, EE and Vodafone stores and ask them about their 4G Home Mobile Broadband (not ADSL/Fibre Broadband).&lt;/p&gt;
&lt;p&gt;Both Three and Vodafone are currently offering Unlimited plans and I think EE were offering the same. &lt;a href="http://www.three.co.uk/store/broadband/home-broadband"&gt;Three are currently offering a home hub&lt;/a&gt; which seems to bundle it all together as too are &lt;a href="https://www.vodafone.co.uk/gigacube/"&gt;Vodafone with their Gigacube&lt;/a&gt; which should simplify things -although if you have poor reception inside your house you may want to go the more DIY route. If you're going the DIY route, grab a data only SIM plan.&lt;/p&gt;
&lt;p&gt;Once you've chosen a mobile provider (Vodafone, Three, EE etc) then you'll need a router which will connect to the mobile network and give you internal WiFi. There are lots of options out there but I think the best balance of cost vs capability is the &lt;a href="https://www.amazon.co.uk/Unlocked-B535-232-External-Antennas-Worldwide/dp/B07ZFSX8LB"&gt;Huawei B535 3G/4G router which you can get on Amazon here&lt;/a&gt;. Make sure it's unlocked.&lt;/p&gt;
&lt;p&gt;To get the best signal (it really does make a huge difference) I would recommend installing an antenna as well &lt;a href="https://www.amazon.co.uk/Poynting-4G-XPOL-A0001-Cross-Polarised-Antenna/dp/B00C1DGFPS"&gt;we have this external omni directional antenna&lt;/a&gt; which works really well. You plug it in where the supplied antennas plug in. You'll want to try a few spots outside rather than just popping the antenna as high as you can due to the shape of mobile radio waves.&lt;/p&gt;
&lt;p&gt;I hope that helps someone looking for a good alternative to the classic wired broadband suppliers.&lt;/p&gt;
</description>
      <pubDate>Mon, 13 Apr 2020 20:52:52 +0100</pubDate>
      <a10:updated>2020-04-13T20:52:52+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2903</guid>
      <link>http://ondemanddevelopers.co.uk/blog/error-running-ucommerce-scratch-indexer-illegal-duplicate-key-definitions/</link>
      <category>uCommerce</category>
      <category>SQL Server</category>
      <title>Error running Ucommerce scratch indexer Illegal duplicate key definitions</title>
      <description>&lt;p&gt;We hit an interesting issue indexing the Ucommerce data the other day which had us going for a while.&lt;/p&gt;
&lt;p&gt;It turns out that when upgrading Ucommerce to 7.0.6, some new payment providers were added to `uCommerce_Definition` with duplicate Guids which then caused an issue. We found it by searching the database for the value `c94644e4-f213-48a6-98eb-741aca16155f`.&lt;/p&gt;
&lt;p&gt;In our instance it was&lt;/p&gt;
&lt;table border="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Id&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Adyen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;eWAY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;Ideal (Ing Bank)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;Schibsted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The fix was to remove this duplicate. If you want to _just update_ the data you can run this (assuming the ids are the same!):&lt;/p&gt;
&lt;pre class="brush:sql"&gt;UPDATE uCommerce_Definition 
SET Guid=NEWID() 
WHERE DefinitionId IN (4,9,19,21)
&lt;/pre&gt;
&lt;p&gt;It would probably be worth checking your data though as I suspect the Guids will be different so you can find these using this:&lt;/p&gt;
&lt;pre class="brush:sql"&gt;SELECT * 
FROM uCommerce_Definition d 
WHERE EXISTS(
    SELECT * 
    FROM uCommerce_Definition id 
    WHERE id.Guid=d.Guid AND id.DefinitionId&amp;lt;&amp;gt;d.DefinitionId
)&lt;/pre&gt;
&lt;p&gt;The error it threw was when indexing/scratch indexing and it looks like this: the key bit is after the `/` i.e. in `definitions/c94644e4-f213-48a6-98eb-741aca16155f`, `c94644e4-f213-48a6-98eb-741aca16155f` is the Guid you need to look for.&lt;/p&gt;
&lt;pre&gt;System.AggregateException: One or more errors occurred. ---&amp;gt; Raven.Abstractions.Exceptions.ConcurrencyException: Illegal duplicate key definitions/c94644e4-f213-48a6-98eb-741aca16155f ---&amp;gt; Microsoft.Isam.Esent.Interop.EsentKeyDuplicateException: Illegal duplicate key
   at Microsoft.Isam.Esent.Interop.Api.JetUpdate(JET_SESID sesid, JET_TABLEID tableid, Byte[] bookmark, Int32 bookmarkSize, Int32&amp;amp; actualBookmarkSize)
   at Microsoft.Isam.Esent.Interop.Update.Save(Byte[] bookmark, Int32 bookmarkSize, Int32&amp;amp; actualBookmarkSize)
   at Raven.Storage.Esent.StorageActions.DocumentStorageActions.InsertDocument(String key, RavenJObject data, RavenJObject metadata, Boolean overwriteExisting)
   --- End of inner exception stack trace ---
   at Raven.Storage.Esent.StorageActions.DocumentStorageActions.InsertDocument(String key, RavenJObject data, RavenJObject metadata, Boolean overwriteExisting)
   at Raven.Database.Actions.DocumentActions.&amp;lt;&amp;gt;c__DisplayClass1c.b__17(IStorageActionsAccessor accessor)
   at Raven.Storage.Esent.TransactionalStorage.ExecuteBatch(Action`1 action, EsentTransactionContext transactionContext)
   at Raven.Storage.Esent.TransactionalStorage.Batch(Action`1 action)
   at Raven.Database.Actions.DocumentActions.BulkInsert(BulkInsertOptions options, IEnumerable`1 docBatches, Guid operationId, CancellationToken token)
   at Raven.Database.Server.Controllers.BulkInsertController.&amp;lt;&amp;gt;c__DisplayClass6.b__3()
&lt;/pre&gt;</description>
      <pubDate>Fri, 02 Aug 2019 14:40:17 +0100</pubDate>
      <a10:updated>2019-08-02T14:40:17+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2901</guid>
      <link>http://ondemanddevelopers.co.uk/blog/design-with-data-how-to-get-the-longestshortest-productcategory-name-from-ucommerce/</link>
      <category>uCommerce</category>
      <category>Development</category>
      <title>Design with data -how to get the longest/shortest product/category name from Ucommerce</title>
      <description>&lt;p&gt;There's little more frustrating that finalising a store and loading in the product data only to find it doesn't look as beautiful as the design. This is usually because the designer has chosen a product title from the site which looks good in the design rather than testing the extremes (too long/short).&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Here's a little script which will help if you're working with existing Ucommerce data, it lists the longest and shortest titles for the products, categories and discount codes. If you wanted to, you could add the product descriptions to this as well.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;It will output something like this:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;table border="0" cellspacing="0" cellpadding="2"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;DataType&lt;/th&gt;
&lt;th&gt;RowNum&lt;/th&gt;
&lt;th&gt;LengthType&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Length&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Shortest&lt;/td&gt;
&lt;td&gt;Equine&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Longest&lt;/td&gt;
&lt;td&gt;Tack Room Essentials&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category Description&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Shortest&lt;/td&gt;
&lt;td&gt;Equine&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Category Description&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Longest&lt;/td&gt;
&lt;td&gt;Tack Room Essentials&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount Code&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Shortest&lt;/td&gt;
&lt;td&gt;TEST&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discount Code&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Longest&lt;/td&gt;
&lt;td&gt;EXAMPLE&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Shortest&lt;/td&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Longest&lt;/td&gt;
&lt;td&gt;Horse Worm Count Kit &amp;amp; Pinworm Test - commercial yards&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product Description&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Shortest&lt;/td&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product Description&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Longest&lt;/td&gt;
&lt;td&gt;Horse worm count and pinworm test kit for commercial yards&lt;/td&gt;
&lt;td&gt;
&lt;p&gt;59&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you would like the top/bottom x for each, just change "WHERE RowNum&amp;lt;=1" to the number you would like.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="brush:sql"&gt;SELECT d.DataType
	 , d.RowNum
	 , d.LengthType
	 , d.Name
	 , d.Length
FROM 
(
	SELECT 'Product' AS DataType,'Shortest' AS LengthType, P.Name, LEN(P.Name) AS [Length], RowNum = ROW_NUMBER() OVER (ORDER BY LEN(P.Name) ASC) FROM uCommerce_Product p WHERE p.DisplayOnSite=1 UNION ALL
	SELECT 'Product','Longest', P.Name, LEN(P.Name), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(P.Name) DESC) FROM uCommerce_Product p WHERE p.DisplayOnSite=1 UNION ALL
	SELECT 'Product Description','Shortest', pd.DisplayName, LEN(Pd.DisplayName), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(Pd.DisplayName) ASC) FROM uCommerce_ProductDescription pd JOIN uCommerce_Product p ON pd.ProductId = p.ProductId WHERE p.DisplayOnSite=1 AND LEN(pd.DisplayName)&amp;gt;1 UNION ALL
	SELECT 'Product Description','Longest', pd.DisplayName, LEN(Pd.DisplayName), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(Pd.DisplayName) DESC) FROM uCommerce_ProductDescription pd JOIN uCommerce_Product p ON pd.ProductId = p.ProductId WHERE p.DisplayOnSite=1 AND LEN(pd.DisplayName)&amp;gt;1 UNION ALL
	SELECT 'Category','Shortest', c.Name, LEN(c.Name), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Name) ASC) FROM uCommerce_Category c WHERE c.DisplayOnSite=1 UNION ALL
	SELECT 'Category','Longest', c.Name, LEN(c.Name), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Name) DESC) FROM uCommerce_Category c WHERE c.DisplayOnSite=1 UNION ALL
	SELECT 'Category Description','Shortest', c.Name, LEN(cd.DisplayName), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Name) ASC) FROM uCommerce_Category c JOIN uCommerce_CategoryDescription cd ON c.CategoryId = cd.CategoryId  WHERE c.DisplayOnSite=1 AND LEN(cd.DisplayName)&amp;gt;1 UNION ALL
	SELECT 'Category Description','Longest', c.Name, LEN(cd.DisplayName), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Name) DESC) FROM uCommerce_Category c JOIN uCommerce_CategoryDescription cd ON c.CategoryId = cd.CategoryId WHERE c.DisplayOnSite=1 AND LEN(cd.DisplayName)&amp;gt;1 UNION ALL
	SELECT 'Discount Code','Shortest', c.Code, LEN(c.Code), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Code) ASC) FROM uCommerce_VoucherCode c WHERE c.NumberUsed &amp;lt; c.MaxUses UNION ALL
	SELECT 'Discount Code','Longest', c.Code, LEN(c.Code), RowNum = ROW_NUMBER() OVER (ORDER BY LEN(c.Code) DESC) FROM uCommerce_VoucherCode c WHERE c.NumberUsed &amp;gt; c.MaxUses
	
) d
WHERE RowNum &amp;lt;= 1
ORDER BY DataType, LengthType DESC, RowNum ASC
&lt;/pre&gt;</description>
      <pubDate>Tue, 16 Jul 2019 16:34:17 +0100</pubDate>
      <a10:updated>2019-07-16T16:34:17+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2898</guid>
      <link>http://ondemanddevelopers.co.uk/blog/turn-negative-reviews-to-your-advantage/</link>
      <category>Business</category>
      <category>eCommerce</category>
      <title>Turn negative reviews to your advantage</title>
      <description>&lt;p&gt;Nothing reassures a new customer quite like the experience of an existing customer, but its not always roses and that's ok if handled sensitively.&lt;/p&gt;
&lt;p&gt;No one likes a negative review, especially after having worked hard to service a customer, but you should not shy away from them as they can highlight problems in your product, service or processes which you can resolve. They should be seen as a critical feedback loop in any business (especially e-commerce websites).&lt;/p&gt;
&lt;p&gt;You should publish any negative reviews alongside your positive ones as it not only further reassures the prospective customer you're open and honest but also allows them to determine the validity of the review for themselves.&lt;/p&gt;
&lt;p&gt;Over the years I've seen lots of poor reviews which discredit themselves such as "&lt;em&gt;The sticks of chalk weren't sharpened&lt;/em&gt;" -who sharpens sticks of chalk? or "&lt;em&gt;The delivery man threw it over the fence&lt;/em&gt;" -not really warranting the product a 1* rating but a good indication you may want to change courier.&lt;/p&gt;
&lt;p&gt;Hopefully your e-commerce platform allows you to respond to the reviews, in which case, once published you should also publicly address as many of the customer's concern as possible. This not only services the disappointed customer but gives you the chance to rebuff any concerns and shows any prospective customers that you take customer concerns seriously - another confidence builder.&lt;/p&gt;
&lt;p&gt;If it's on brand and as long as you're going to keep doing it, by all means respond with a little personality like &lt;a href="https://www.tripadvisor.co.uk/ShowUserReviews-g186302-d7361856-r444016720-Hereford_Left_Bank-Hereford_Herefordshire_England.html" target="_blank"&gt;the manager at The Left Bank in Hereford started doing&lt;/a&gt; but to get the best response from prospective customers, ensure you address each of the problems without being insensitive or personal.&lt;/p&gt;
&lt;p&gt;If the review is critiquing your product, you may be alerted to issues with it and should take heed. One of our customers couldn't work out why so many of their dresses were getting returned to store. After their product review system was launched on their website, they quickly discovered that there were issues with the garment after it's first wash. Despite selling it for over a year in-store, none of their staff had fed this issue back to head office. The nature of the anonymous review system meant people were more forthcoming with feedback. They sold the dress off at cost with a caveat that it was dry clean only, fixed the issue in manufacture and had next to no more returns on that garment!&lt;/p&gt;
&lt;p&gt;You'll likely find that the honest mix of good and bad reviews helps bolster sales but if you want to chat any through, get in touch.&lt;/p&gt;</description>
      <pubDate>Wed, 22 May 2019 21:05:17 +0100</pubDate>
      <a10:updated>2019-05-22T21:05:17+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2897</guid>
      <link>http://ondemanddevelopers.co.uk/blog/cant-register-services-with-lightinject-in-umbraco-the-non-generic-method-compositionregister-type-lifetime-cannot-be-used-with-type-arguments/</link>
      <category>Umbraco</category>
      <title>Can't register services with LightInject in Umbraco - The non-generic method 'Composition.Register(Type, Lifetime)' cannot be used with type arguments</title>
      <description>&lt;p&gt;We've generally used Autofac as our go to dependency injection container in Umbraco but we like to keep projects as lean as possible so wanted to start using LightInject for Umbraco V8 projects but hit a silly error this morning which took a while to track down.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Nearly all the documentation online suggests you simply implement IUserComposer as follows and Umbraco will pick it up and all will be good in the world:&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;pre class="brush:csharp"&gt;public class SomeComposer : IUserComposer
{
    public void Compose(Composition composition)
    {
        composition.Register&amp;lt;ISomeService, SomeService&amp;gt;();
    }
}&lt;/pre&gt;
&lt;p&gt;The thing snag that caught us out though was although we had included Umbraco.Core.Composing for IUserComposer and that gave us access to composition.Register, it was complaining&lt;/p&gt;
&lt;pre&gt;The non-generic method 'Composition.Register(Type, Lifetime)' cannot be used with type arguments&lt;/pre&gt;
&lt;p&gt;When using the interfaces in the controllers we were then getting:&lt;/p&gt;
&lt;pre&gt;Server Error in '/' Application.
Missing public constructor for Type: xxx
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: Missing public constructor for Type: xxx

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[InvalidOperationException: Missing public constructor for Type: xxx]
   LightInject.MostResolvableConstructorSelector.Execute(Type implementingType) in C:\projects\lightinject\src\LightInject\LightInject.cs:5561
   LightInject.TypeConstructionInfoBuilder.Execute(Registration registration) in C:\projects\lightinject\src\LightInject\LightInject.cs:5719
   System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) +72
   LightInject.ConstructionInfoProvider.GetConstructionInfo(Registration registration) in C:\projects\lightinject\src\LightInject\LightInject.cs:5779
   LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:4026
   LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter) in C:\projects\lightinject\src\LightInject\LightInject.cs:3929
   LightInject.&amp;lt;&amp;gt;c__DisplayClass197_0.b__0(IEmitter methodSkeleton) in C:\projects\lightinject\src\LightInject\LightInject.cs:4646
   LightInject.&amp;lt;&amp;gt;c__DisplayClass153_0.b__0(IEmitter ms) in C:\projects\lightinject\src\LightInject\LightInject.cs:3856
   LightInject.&amp;lt;&amp;gt;c__DisplayClass153_0.b__0(IEmitter ms) in C:\projects\lightinject\src\LightInject\LightInject.cs:3856
   LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency) in C:\projects\lightinject\src\LightInject\LightInject.cs:4158&lt;/pre&gt;
&lt;p&gt;The fix was stupidly simple; we had missed our the Umbraco.Core namespace. So for those of you hitting the same error, you'll probably benefit from a code sample with namespaces:&lt;/p&gt;
&lt;pre class="brush:csharp"&gt;using Umbraco.Core;
using Umbraco.Core.Composing;

public class SomeComposer : IUserComposer
{
    public void Compose(Composition composition)
    {
        composition.Register&amp;lt;ISomeService, SomeService&amp;gt;();
    }
}&lt;/pre&gt;</description>
      <pubDate>Wed, 15 May 2019 10:24:52 +0100</pubDate>
      <a10:updated>2019-05-15T10:24:52+01:00</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">2883</guid>
      <link>http://ondemanddevelopers.co.uk/blog/delete-all-products-and-orders-from-ucommerce/</link>
      <category>uCommerce</category>
      <title>Delete all products and orders from Ucommerce</title>
      <description>&lt;p&gt;Here's an updated script to delete all the products, orders and any other testy type data from Ucommerce. This is an updated version of my previous script to &lt;a href="http://thesitedoctor.co.uk/blog/quickly-delete-all-products-and-orders-from-ucommerce/" target="_blank"&gt;delete all product and order data from Ucommerce&lt;/a&gt; and accommodates Ucommerce's new database schema for prices.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="brush:sql;"&gt;BEGIN TRAN&amp;nbsp;&amp;nbsp; DELETE FROM uCommerce_ProductReviewComment
DELETE FROM uCommerce_ProductReview
DELETE FROM uCommerce_OrderLineDiscountRelation
DELETE FROM uCommerce_ShipmentDiscountRelation
DELETE FROM uCommerce_Discount
UPDATE uCommerce_OrderLine SET ShipmentId = NULL
UPDATE uCommerce_PurchaseOrder SET BillingAddressId = NULL
DELETE FROM uCommerce_Shipment
DELETE FROM uCommerce_OrderAddress
DELETE FROM uCommerce_OrderProperty
DELETE FROM uCommerce_OrderLine
DELETE FROM uCommerce_PaymentProperty
DELETE FROM uCommerce_Payment
DELETE FROM uCommerce_OrderStatusAudit
DELETE FROM uCommerce_PurchaseOrder
DELETE FROM uCommerce_Address
-- NOTE: Only needed if you use our Stripe package
DELETE FROM uCommerce_StripeCustomer
DELETE FROM uCommerce_Customer&amp;nbsp;&amp;nbsp; DELETE FROM uCommerce_ProductRelation
DELETE FROM uCommerce_ProductProperty
DELETE FROM uCommerce_ProductDescriptionProperty
DELETE FROM uCommerce_ProductDescription
DELETE FROM uCommerce_CategoryProductRelation
DELETE FROM uCommerce_PriceGroupTarget
DELETE FROM uCommerce_ProductPrice
DELETE FROM uCommerce_Price
DELETE FROM uCommerce_Product&amp;nbsp;&amp;nbsp; -- Just double check things have gone
SELECT * FROM uCommerce_PurchaseOrder o
SELECT * FROM uCommerce_Product p&amp;nbsp;&amp;nbsp; -- For safety's sake, run it in a transaction just in case you change your mind
ROLLBACK TRAN
-- When happy it works, uncomment this line and comment out the ROLLBACK
-- COMMIT TRAN&lt;/pre&gt;</description>
      <pubDate>Tue, 23 Apr 2019 11:19:40 +0100</pubDate>
      <a10:updated>2019-04-23T11:19:40+01:00</a10:updated>
    </item>
  </channel>
</rss>