How to get prices with ProductEntity? - shopware

I created my own Entity called "MyProductEntity" (and also Definition/Collection) which has a relation to some Product (eg. a product is linked with another product)
The problem is that MyProductEntity->connectedProduct doesn't contain prices (eg. calculatedPrices). Does anyone know how to load the prices? I read somewhere that I should use SalesChannelProductEntity instead of ProductEntity? But I am not sure how? Any hints?
Here are (I hope) the relevant parts from my code:
class MyProductEntity extends Entity
{
protected string $productId;
protected string $productVersionId;
protected ProductEntity $product;
protected string $connectedProductId;
protected string $connectedProductVersionId;
protected ProductEntity $connectedProduct;
//I tried to use here "SalesChannelProductEntity" instead of "ProductEntity"
//...
//other properties
}
in some subscriber: (eg.: for ProductPageLoadedEvent::class) I load MyProductEntity like this:
public function getMyProducts(ProductPageLoadedEvent $event): array
{
$page = $event->getPage();
$salesChannelContext = $event->getSalesChannelContext();
$product = $page->getProduct();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productId', $product->getId()));
$criteria->addAssociation('connectedProduct');
$myProducts = $this->myProductRepository->search($criteria, $salesChannelContext);
//$this->myProductRepository = defined via services.xml / in constructor:
// EntityRepository $myProductRepository
}

As you already found out the field calculatedPrices and similar are exclusive to the SalesChannelProductDefinition and SalesChannelProductEntity. You could simply create your own sales channel definition in addition to the regular definition:
class SalesChannelMyProductDefinition extends MyProductDefinition implements SalesChannelDefinitionInterface
<service id="Foo\MyPlugin\SalesChannel\SalesChannelMyProductDefinition">
<tag name="shopware.sales_channel.entity.definition"/>
</service>
Then use the repository of your new sales channel definition:
$myProduct = $this->getContainer()->get('sales_channel.my_product.repository')
->search($criteria, $salesChannelContext)->first();
// should then be an instance of SalesChannelProductEntity
$salesChannelProduct = $myproduct->getConnectedProduct();

Related

Conflicts in AutoMapper and AutoFixture

I have 2 classes, Class1 should be mapped to Class2. I do mapping with AutoMapper. I'd like to test my configuration of the mapper and for this purposes I'm using AutoFixture. Source class Class1 has property of type IList<>, destination class Class2 has a similar property but of type IEnumerable<>. To simplify test preparation I'm using AutoFixture (with AutoMoqCustomization) to initialize both source and destination objects. But after initializing property of type IEnumerable<> with AutoFixture, AutoMapper can't map the property.
Error text:
Error mapping types.
Mapping types: Class1 -> Class2 ConsoleApplication1.Class1 ->
ConsoleApplication1.Class2
Type Map configuration: Class1 -> Class2 ConsoleApplication1.Class1 ->
ConsoleApplication1.Class2
Property: Items
Could anybody help me to configure either AutoMapper or AutoFixture to make the mapping work? As a workaround I can assign null to the destination property, but I do not want to do this in the each test.
Simplified example of code:
public class AutoMapperTests
{
public static void TestCollectionsProperty()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ItemClass1, ItemClass2>();
cfg.CreateMap<Class1, Class2>();
});
var src = new Class1();
src.Items = new List<ItemClass1>()
{
new ItemClass1() { Text = "111" },
new ItemClass1() { Text = "222" }
};
var fixture = new Fixture();
var dst = fixture.Create<Class2>();
Mapper.Map(src, dst); //Error at this line of code
}
}
public class Class1
{
public IList<ItemClass1> Items { get; set; }
}
public class Class2
{
public IEnumerable<ItemClass2> Items { get; set; }
}
public class ItemClass1
{
public string Text { get; set; }
}
public class ItemClass2
{
public string Text { get; set; }
}
It's not really an AutoFixture issue per se. You can reproduce it without AutoFixture by instead creating dst like this:
var dst = new Class2();
dst.Items = Enumerable.Range(0, 1).Select(_ => new ItemClass2());
This will produce a similar error message:
Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Int32,Ploeh.StackOverflow.Q45437098.ItemClass2]' to type 'System.Collections.Generic.IList1[Ploeh.StackOverflow.Q45437098.ItemClass2]'
That ought to be fairly self-explanatory: WhereSelectEnumerableIterator<int, ItemClass2> doesn't implement IList<ItemClass2>. AutoMapper attempts to make that cast, and fails.
The simplest fix is probably to avoid populating dst:
var dst = new Class2();
If you must use AutoFixture for this, you can do it like this:
var dst = fixture.Build<Class2>().OmitAutoProperties().Create();
Unless the Class2 constructor does something complex, however, I don't see the point of using AutoFixture in that scenario.
If, on the other hand, you do need dst to be populated, you just need to ensure that dst.Items is convertible to IList<ItemClass2>. One way to do that would be like this:
var dst = fixture.Create<Class2>();
dst.Items = dst.Items.ToList();
You could create a Customization to make sure that this happens automatically, but if you need help with that, please ask a new question (if you don't find one that already answers that question).
Here is a working example for your problem. As #Mark Seemann already told, Mapper.CreateMap has been deprecated, so this example is using the new structure.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ItemClass1, ItemClass2>();
cfg.CreateMap<Class1, Class2>();
});
var src = new Class1();
src.Items = new List<ItemClass1>()
{
new ItemClass1() { Text = "111" },
new ItemClass1() { Text = "222" }
};
var dest = Mapper.Map<Class1, Class2>(src);
AM requires IList because you're mapping to an existing list and that works by calling IList.Add.

Passing Interfaces into Dictionary key's

I have been following following coreclr for a little while and I am new to programming. My question is why do they pass interfaces into Dictionary's especially the key value?
//
// Allocate a new Dictionary containing a copy of the old values, plus the new value. We have to do this manually to
// minimize allocations of IEnumerators, etc.
//
Dictionary newValues = new Dictionary(current.m_localValues.Count + (hadPreviousValue ? 0 : 1));
My understanding is that interface is to implemented by a class. Once implemented it can call/use functions or store data in the classes properties/ variables. I am missing some understanding of interfaces and their use cases but I do not know what that it.
Why do you instantiate a variable to an interface or pass an interface into a parameter? My understanding is you will then have an instance of that variable which still can't hold values nor change state through methods.
Let me explain.
Interface is contract. It just contains method without implementation. Now it may possible that that interface is being implemented by any number of class.
public interface IEntity { int Id {get;set;} }
public class Student : IEntity { public int Id {get;set;} // Interface Property }
public class Teacher : IEntity { public int Id {get;set;} // Interface Property }
Dictionary<IEntity,object> obj = new Dictionary<IEntity,object>(); Student s = new Student(); Teacher t = new Teacher(); obj.Add(s,any object); obj.Add(t,any object);
This is because of interface that your dictionary can hold reference of both type ( Student and Teacher).
In .NET when any object is created it is uniquely identify by GetHashCode() method. // You can find more detail on this on MSDN.
Also Dictionary not means that keys must be only primitive type. This is the reason it is good if you have more than one key ( Like composite key in Database) so it allow you to identify uniquely based on your custom implementation.
Now second Generic.
public class PersonInfo<T> where T : IEntity
{
public string Name {get;set;}
public T Entity {get;set;}
}
PersonInfo<Student> student = new PersonInfo<Student>();
student.T = new Student();
student.Name = "";
PersonInfo<Teacher> Teacher = new PersonInfo<Teacher>();
teacher.T= new Teacher();
teacher.Name = "";
When you have interface. It not actually interface. You always have a reference to object with that interface. And that object is the one responsible for comparison in dictionary
The benefit is not difference from using class as a key. Dictionary can be used as list to iterate KeyValuePair to take key to do some operation. But using interface means you can store various type of class with same interface instead of just one type. Which is decoupled and more flexible

How to retrieve data using a strong typed model in LinqToSql

This code works fine.
using (ContextDB db = new ContextDB())
{
var custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
But this one doesn't
public class CustAcct
{
public int AcctNo { get; set; }
public string Company { get; set; }
public string UserName { get; set; }
}
....
....
....
using (ContextDB db = new ContextDB())
{
CustAcct custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
It returns this error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'EMailReader.Models.CustAcct'. An explicit conversion exists (are you missing a cast?)
I used Google, found many related topics but still couldn't put it to work using the available solutions
I just need to return data to a strong typed model.
EDITED:
After more research I found this solution bellow, but I wonder why I cannot retrieve directly in the list from LinqToSql.
List<CustAcct> temp = new List<CustAcct>();
IEnumerable<dynamic> items = custAcct;
foreach (var item in items)
{
temp.Add(new CustAcct()
{
AcctNo = item.AcctNo,
Company = item.Company,
UserName = item.UserName,
});
}
You are re defining those properties by creating new Class. And this will override LINQ2SQL generated class.
Just change "public class CustAcct" to "public partial class CustAcct".
This will solve your problem, and you do not need to define those properties again. Remove those from your class. Those will be automatically create for you.
If you can just post your class, and I will change it for you.
//Shyam

Is this the correct way to instantiate an object with dependencies from within a Domain Model?

I'm trying to avoid ending up with an anaemic Domain Model, so I'm attempting to keep as much logic as possible within the domain model itself. I have a method called AddIngredient, which needs to add a new KeyedObject to my Recipe Aggregate.
As the Domain Models themselves are meant to be devoid of repositories, I'm getting the ingredient via a business rule class:
public class Recipe : AggregateObject
{
public void AddIngredient(int ingId, double quantity)
{
GetIngredientMessage message = new GetIngredientMessage();
message.IngredientId = ingId;
GetIngredient handler = ServiceLocator.Factory.Resolve<GetIngredient>();
Ingredient ingredient = handler.Execute(message);
Ingredients.Add(new OriginalIngredient()
{
Ingredient = ingredient,
Quantity = quantity
});
}
}
As you can see, I'm using a line the line ServiceLocator.Factory.Resolve<GetIngredient>(); to obtain my GetIngredient business rule class. GetIngredient is a simple command handler that looks like the following:
public class GetIngredient : ICommandHandler<Ingredient, GetIngredientMessage>
{
private readonly IIngredientRepository _ingredientRepository;
public GetIngredient(IIngredientRepository ingredientRepository)
{
_ingredientRepository = ingredientRepository;
}
}
I assign my IoC factory class to the ServiceLocator.Factory, so the Domain has the ability to use its own interfaces, without seeing the concrete class implementation:
ServiceLocator.Factory = new IoCFactory();
I'm pretty sure I'm doing something wrong as it all feels a little bit bodge-like.
Can anyone spot anything blatantly wrong?
Is there a more appropriate way to instantiate a business rule handler such as GetIngredient without a static reference to my IoC Factory?
I suggest you introduce another layer into the design -- the Application layer. This layer responsibility would be to translate commands (either explicitly encapsulated in command objects or passed implicitly as int ingId, double quantity) into domain model invocations (Recipe.AddIngredient).
By doing so you'll move the responsibility of finding an ingredient by its id to a layer above domain, where you can safely make use of repositories directly without introducing unwanted coupling. The transformed solution would look something like this:
public class ApplicationLayer
{
private readonly IRecipeRepository _recipeRepository;
private readonly IIngredientRepository _ingredientRepository;
/*
* This would be called by IoC container when resolving Application layer class.
* Repositories would be injected by interfacy so there would be no coupling to
* concrete classes.
*/
public ApplicationLayer(IRecipeRepository recipeRepository, IIngredientRepository ingredientRepository)
{
_recipeRepository = recipeRepository;
_ingredientRepository = ingredientRepository;
}
public void AddIngredient(int recipeId, int ingId, double quantity)
{
var recipe = _recipeRepository.FindById(recipeId);
var ingredient = _ingredientRepository.FindById(ingId);
recipe.AddIngredient(ingredient, quantity);
}
}
And the now simplified Recipe class would look something like this:
public class Recipe : AggregateObject
{
public void AddIngredient(Ingredient ingredient, double quantity)
{
Ingredients.Add(new OriginalIngredient()
{
Ingredient = ingredient,
Quantity = quantity
});
}
}
Hope that helps.

"Lambda Parameter not in scope" exception using SimpleRepository's Single method

I'm attempting to use the SimpleRepository to perform a fetch based on a non-ID property. Here's the Customer class I'm using:
[Serializable]
public class Customer : IEntity<Guid>
{
public Guid ProviderUserKey { get; set; }
public Guid ID
{
get; set;
}
}
I'm using SimpleRepository with migrations turned on. The code that throws the "Lambda Parameter not in scope" is below:
public class CustomerRepository :
ICustomerRepository
{
private readonly IRepository _impl;
public CustomerRepository(string connectionStringName)
{
_impl = new SimpleRepository(connectionStringName,
SimpleRepositoryOptions.RunMigrations);
}
public Customer GetCustomer(string userName)
{
var user = Membership.GetUser(userName);
// Code to guard against a missing user would go here
// This line throws the exception
var customer = _impl.Single<Customer>(c => c.ProviderUserKey.Equals(user.ProviderUserKey));
// Code to create a new customer based on the
// ASP.NET Membership user would go here
return customer;
}
}
I'm not sure at what point in the LINQ expression compilation this throws, but I am running this example on an empty database. The schema generations gets far enough to create the table structure, but can't evaluate the expression.
Does anyone know what I might be doing wrong?
Thanks!
I've had reports of this - can you add this (and your code) as an issue please?

Resources