How to map a String property into the first element of a List<String> property? - mapstruct

I have a CXF generated class which has a collection as its only property. I need to map a single value from my DTO into an element of that collection, as in:
class DTO {
String dto;
}
class A {
String a;
}
class B {
List<A> b;
}
interface Mapper {
#Mappings({
#Mapping(source="dto", target="b.a")
})
B getBfromDTO(DTO dto);
}
Should this really work?
If so, how could I configure this mapping?

I couldn't find out a way to do this as a mapping, and from other answers from #Gunnar I now understand this is not possible, but I found a workaround using the annotation #AfterMapping.
#AfterMapping allow to specify methods to be run after a mapping occurs, so I created mappings for the non-collection properties of my objects and an #AfterMapping for filling collections in the objects.
class DTO {
String a;
String x;
}
class A {
String a;
}
class B {
String x;
List<A> b;
}
abstract class Mapper {
#Mappings({
#Mapping(source="x", target="x")
})
protected abstract B getBfromDTO(DTO dto);
#AfterMapping
protected void fillCollections(DTO dto, #MappingTarget B b) {
b.getB().add(dto.a);
}
}

Related

Using custom collection types as backing fields

Is it possible to have a custom collection type as a backing field in Entity Framework Core 3?
I have an entity A that has a OwnsMany relationship with an entity B. Since there are many business rules that needed to be checked before adding B to A, I have to rely on methods like AddB, GetB and RemoveB in order to enforce this rules.
public class Id { ... }
public class B{
private A _a;
internal B(A a){
_a = a;
}
private B() {
_a = default!;
}
}
public class A {
private List<B> _bs;
public IReadOnlyCollection<B> Bs => _bs.AsReadOnly();
public B AddB() {
// validate stuff
var b = new B(this);
_bs.Add(b);
return b;
}
public B GetB(Id id) {
//some linq to find B or throw if id not found
}
public B? FindB(Id id) {
return null if id not found
}
public void RemoveB(B b)
=> _bs.Remove(b);
}
Here the thing is that, as the business rules for managing B, A class is becoming more populated with methods like DoSomethingForB, most of them being linq queries or methods that perform calculations over the collection. And if I add another collection of class C then the complexity of A increases. So I would like to delegate linq queries and Get and Find methods to collection classes instead of relying on the generic List<T> method.
public class Id { ... }
public class B{
private A _a;
internal B(A a){
_a = a;
}
private B() {
_a = default!;
}
}
public class BCollection : IEnumerable<B> {
private List<B> _items;
public B GetB(Id id) {
//some linq to find B or throw if id not found
}
public B? FindB(Id id) {
return null if id not found
}
internal void Add(B b) => _items.Add(b);
// add some other methods like Remove etc.
}
public class A {
private BCollection _bs;
public BCollection Bs => _bs;
public A() {
_bs = new BCollection();
}
public B AddB() {
// validate stuff
var b = new B(this);
_bs.Add(b);
return b;
}
}
However, I am unsure whether this is a supported scenario on EF Core 3, specifically if EF Core 3 will know how to reconstruct my BCollection type, or if I have to follow some specific rules (perhaps implement an interface or do some special configuration in the DbContext configuration).
Thanks in advance.

JPA link field value

I have three entities, for example A, B, C. Entity A is parent for B, with inheritance type joined. Entity B aggregates entity C with ManyToOne relationship.
Structure looks like next:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
class A {
String str;
String cStr;
}
#Entity
class B extends A {
#ManyToOne
C c;
}
#Entity
class C {
String str;
}
I don't know if it is possible at all on entity level but, I need to link value of str from C to the filed A.cStr. How it should looks like: I create entity B with populated value of c, store it and value from C.str is populating into filed A.cStr. And when I fetch A from datebase I can see A.cStr with same value as C.str has.
No, it's not possible. All you need to do to get the C string from A is to implement a method and override it in B:
in A:
public String getCString() {
return null;
}
in B:
#Override
public String getCString() {
return c.getStr();
}
Of course, the instances of A that are not B instances won't have any CString.

In Autofac, how do I propagate Keys through Adapters

I'm using the adapter support in Autofac to convert multiple types to a desired type. I also want to preserve the keys/names/metadata attached to the adapter input types, so that they exist with the same values on the adapter output types - this is needed for using IIndex<,> to resolve instances by name.
I can't figure out how to propagate the keys/names/metadata through the adapter function, since the adapter function runs during component construction, and the metadata needs to be propagated when the container is built.
Here's an example xunit test, which fails:
/// <summary>
/// Unit test to figure out how to propagate keys through adapters.
/// </summary>
public sealed class AutofacAdapterTest
{
public class A
{
public A(string key)
{
Key = key;
}
public string Key { get; private set; }
}
public class B
{
public B(string name)
{
Name = name;
}
public string Name { get; private set; }
}
public class C : B
{
public C(string name)
: base(name)
{}
}
public class LookerUpper
{
private readonly IIndex<string, B> _bIndex;
public LookerUpper(IIndex<string, B> bIndex)
{
_bIndex = bIndex;
}
public B LookupByName(string name)
{
return _bIndex[name];
}
}
[Fact]
public void TestPropagateKeysThroughAdapters()
{
var builder = new ContainerBuilder();
// Register named types
builder.RegisterType<A>().Named<A>("A").WithParameter("key", "A");
builder.RegisterType<B>().Named<B>("B").WithParameter("name", "B");
builder.RegisterType<C>().Named<C>("C").Named<B>("C").WithParameter("name", "C");
// Adapter to convert an A to a B, since it's not a subclass
builder.RegisterAdapter<A, B>((c, a) => new B(a.Key));
// Register LookerUpper, which is the only top-level type that needs to be autowired
builder.RegisterType<LookerUpper>();
var container = builder.Build();
var lookerUpper = container.Resolve<LookerUpper>();
// Test expected results
Assert.Equal("A", lookerUpper.LookupByName("A").Name);
Assert.IsType<B>(lookerUpper.LookupByName("A")); // A should have been adapted to a B
Assert.Equal("B", lookerUpper.LookupByName("B").Name);
Assert.IsType<B>(lookerUpper.LookupByName("B"));
Assert.Equal("C", lookerUpper.LookupByName("C").Name);
Assert.IsType<C>(lookerUpper.LookupByName("C"));
Assert.Throws<ComponentNotRegisteredException>(() => lookerUpper.LookupByName("D"));
}
}
The statement lookerUpper.LookupByName("A") fails with a ComponentNotRegisteredException, because the name value "A" is not propagated through the adapter function (which adapts A -> B ). If the first two lines of Asserts are commented out, the rest of the test works as expected.
I found a workable solution to this problem by using Autofac Metadata instead of Autofac keys or names. For the call to RegisterAdapter<TFrom, TTo>(Func<TFrom,TTo>), metadata is propagated from the IComponentRegistration for TFrom to the IComponentRegistration for TTo; however the keys/names are not propagated. The omission of keys may be a bug or by design, I'll file a bug with autofac to figure out which is the case and follow up.
The unfortunate part about using metadata is I can't use an IIndex<string, B> constructor parameter, so I had to use an IEnumerable<Meta<Lazy<B>>> parameter and create my own dictionary of string -> Lazy<B> to provide similiar functionality to IIndex. Here's the code that works:
/// <summary>
/// Unit test to figure out how to propagate keys through adapters.
/// </summary>
public sealed class AutofacAdapterTest
{
internal const string LookupKey = "lookup";
public class A
{
public A(string key)
{
Key = key;
}
public string Key { get; private set; }
}
public class B
{
public B(string name)
{
Name = name;
}
public string Name { get; private set; }
}
public class C : B
{
public C(string name)
: base(name)
{}
}
public class LookerUpper
{
private readonly IDictionary<string, Lazy<B>> _bLookup;
public LookerUpper(IEnumerable<Meta<Lazy<B>>> bMetas)
{
_bLookup = bMetas.ToDictionary(meta => meta.Metadata[LookupKey].ToString(), meta => meta.Value);
}
public B LookupByName(string name)
{
return _bLookup[name].Value;
}
}
[Fact]
public void TestPropagateKeysThroughAdapters()
{
var builder = new ContainerBuilder();
// Register types that will be looked up; attach metadata for the lookup key
builder.Register((c) => new A("A")).WithMetadata(LookupKey, "A");
builder.Register((c) => new B("B")).WithMetadata(LookupKey, "B");
builder.Register((c) => new C("C")).AsSelf().As<B>().WithMetadata(LookupKey, "C");
// Adapter to convert an A to a B, since it's not a subclass
builder.RegisterAdapter<A, B>((c, a) => new B(a.Key));
// Register LookerUpper, which is the only top-level type that needs to be autowired
builder.RegisterType<LookerUpper>();
var container = builder.Build();
var lookerUpper = container.Resolve<LookerUpper>();
// Test expected results
Assert.Equal("A", lookerUpper.LookupByName("A").Name);
Assert.IsType<B>(lookerUpper.LookupByName("A")); // A should have been adapted to a B
Assert.Equal("B", lookerUpper.LookupByName("B").Name);
Assert.IsType<B>(lookerUpper.LookupByName("B"));
Assert.Equal("C", lookerUpper.LookupByName("C").Name);
Assert.IsType<C>(lookerUpper.LookupByName("C"));
Assert.Throws<KeyNotFoundException>(() => lookerUpper.LookupByName("D"));
}
}
It should also be possible to create an IRegistrationSource and some extension methods that extend what is done in RegisterAdapter<TFrom, TTo>, such that the keys in TFrom are propagated to TTo - that would be an ideal solution, but potentially more work to maintain, so I'll probably stick with this.
It was fixed in Autofac version 3.5.1.
Link to the bug
Link to the fix

ObservableCollection of generic ViewModel class

I'm creating MVVM application and in Model section I have simple base abstract class Animal and class Dog which derives from it:
public abstract class Animal
{
public int Age { get; set; }
}
public class Dog : Animal
{
public string Name { get; set; }
}
ViewModel section containts UI-friendly VM classes of them:
public abstract class AnimalVM<T> : ViewModelBase where T : Animal
{
protected readonly T animal;
public int Age
{
get { return animal.Age; }
set
{
animal.Age = value;
OnPropertyChanged("Age");
}
}
protected AnimalVM(T animal)
{
this.animal = animal;
}
}
public class DogVM : AnimalVM<Dog>
{
public string Name
{
get { return animal.Name; }
set
{
animal.Name = value;
OnPropertyChanged("Name");
}
}
public DogVM(Dog dog) : base(dog) { }
}
Suppose I have another VM class which contains ObservableCollection<AnimalVM>. The problem is how to create that kind of property which allow me to store there different types of Animal? I want to achieve something like this:
public class AnimalListVM : ViewModelBase
{
// here is a problem, because AnimalVM<Animal> isn't compatible with DogVM
readonly ObservableCollection<AnimalVM<Animal>> animals;
public ObservableCollection<AnimalVM<Animal>> Animals
{
get { return animals; }
}
public AnimalListVM(IList<Animal> animals)
{
//this.animals = ...
}
}
I can change ObservableCollection<AnimalVM<Animal>> property to ICollection property and then create list of AnimalVM using some dictionary Animal -> AnimalVM wrapper and Activator.CreateInstance() - it works but when I try to extend AnimalListVM adding another property SelectedAnimal which will be binded in sample View to e.g. DataGrid control I have another problem with type of that kind of property SelectedItem. It can't be of type AnimalVM<Animal> because when I have DogVM object in my Collection it won't fit with this and throw an exception.
Everything will be clear if only I had non-generic AnimalVM but I don't want to copy and paste similar properties in every DogVM, CatVM, BirdVM class derived from AnimalVM. How can I achieve this?
Ok, I've found a solution and of course it's very simple: just create another, non-generic abstract base class for your generic abstract base class and then derive your generic class from that newly created non-generic class. In that case you also must rewrite properties from non-generic class to generic class (to be more specific override them), but you do this only once, so you don't have to copy and paste the same code in every generic derived ViewModel (in our example in every DogVM, CatVM, BirdVM, etc.).

Can I hide my ICollection<T> fields when I have a one-to-many mapping in EF4 code-only?

My domain classes that have one-to-many mappings generally take the following form (untested code):
public Customer Customer
{
// Public methods.
public Order AddOrder(Order order)
{
_orders.Add(order);
}
public Order GetOrder(long id)
{
return _orders.Where(x => x.Id).Single();
}
// etc.
// Private fields.
private ICollection<Order> _orders = new List<Order>();
}
The EF4 code-only samples I've seen expose a public ICollection when dealing with one-to-many relationships.
Is there a way to persist and restore my collections with exposing them? If not, it would appear that my domain objects will be designed to meet the requirements of the ORM, which seems to go against the spirit of the endeavour. Exposing an ICollection (with it's Add, etc. methods) doesn't seem particularly clean, and wouldn't be my default approach.
Update
Found this post that suggests it wasn't possible in May. Of course, the Microsoft poster did say that they were "strongly considering implementing" it (I'd hope so) and we're half a year on, so maybe there's been some progress?
I found that whatever was done, EF requires the ICollection<T> to be public. I think this is because when the objects are loaded from the database, the mapping looks for a collection property, gets the collection and then calls the Add method of the collection to add each of the child objects.
I wanted to ensure that the addition was done through a method on the parent object so created a solution of wrapping the collection, catching the add and directing it to my preferred method of addition.
Extending a List and other collection types was not possible because the Add method is not virtual. One option is to extend Collection class and override the InsertItem method.
I have only focussed on the Add, Remove, and Clear functions of the ICollection<T> interface as those are the ones that can modify the collection.
First, is my base collection wrapper which implements the ICollection<T> interface
The default behaviour is that of a normal collection. However, the caller can specify an alternative Add method to be called. In addition, the caller can enforce that the Add, Remove, Clear operations are not permitted by setting the alternatives to null. This results in NotSupportedException being thrown if anyone tries to use the method.
The throwing of an exception is not as good as preventing access in the first place. However, code should be tested (unit tested) and an exception will be found very quickly and a suitable code change made.
public abstract class WrappedCollectionBase<T> : ICollection<T>
{
private ICollection<T> InnerCollection { get { return GetWrappedCollection(); } }
private Action<T> addItemFunction;
private Func<T, bool> removeItemFunction;
private Action clearFunction;
/// <summary>
/// Default behaviour is to be like a normal collection
/// </summary>
public WrappedCollectionBase()
{
this.addItemFunction = this.AddToInnerCollection;
this.removeItemFunction = this.RemoveFromInnerCollection;
this.clearFunction = this.ClearInnerCollection;
}
public WrappedCollectionBase(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction) : this()
{
this.addItemFunction = addItemFunction;
this.removeItemFunction = removeItemFunction;
this.clearFunction = clearFunction;
}
protected abstract ICollection<T> GetWrappedCollection();
public void Add(T item)
{
if (this.addItemFunction != null)
{
this.addItemFunction(item);
}
else
{
throw new NotSupportedException("Direct addition to this collection is not permitted");
}
}
public void AddToInnerCollection(T item)
{
this.InnerCollection.Add(item);
}
public bool Remove(T item)
{
if (removeItemFunction != null)
{
return removeItemFunction(item);
}
else
{
throw new NotSupportedException("Direct removal from this collection is not permitted");
}
}
public bool RemoveFromInnerCollection(T item)
{
return this.InnerCollection.Remove(item);
}
public void Clear()
{
if (this.clearFunction != null)
{
this.clearFunction();
}
else
{
throw new NotSupportedException("Clearing of this collection is not permitted");
}
}
public void ClearInnerCollection()
{
this.InnerCollection.Clear();
}
public bool Contains(T item)
{
return InnerCollection.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
InnerCollection.CopyTo(array, arrayIndex);
}
public int Count
{
get { return InnerCollection.Count; }
}
public bool IsReadOnly
{
get { return ((ICollection<T>)this.InnerCollection).IsReadOnly; }
}
public IEnumerator<T> GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return InnerCollection.GetEnumerator();
}
}
Given that base class we can use it in two ways. Examples are using the original post objects.
1) Create a specific type of wrapped collection (For example, List)
public class WrappedListCollection : WrappedCollectionBase, IList
{
private List innerList;
public WrappedListCollection(Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.innerList = new List<T>();
}
protected override ICollection<T> GetWrappedCollection()
{
return this.innerList;
}
<...snip....> // fill in implementation of IList if important or don't implement IList
}
This can then be used:
public Customer Customer
{
public ICollection<Order> Orders {get { return _orders; } }
// Public methods.
public void AddOrder(Order order)
{
_orders.AddToInnerCollection(order);
}
// Private fields.
private WrappedListCollection<Order> _orders = new WrappedListCollection<Order>(this.AddOrder, null, null);
}
2) Give a collection to be wrapped using
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
private ICollection<T> wrappedCollection;
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.wrappedCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.wrappedCollection;
}
}
which can be used as follows:
{
public ICollection Orders {get { return _wrappedOrders; } }
// Public methods.
public void AddOrder(Order order)
{
_orders.Add(order);
}
// Private fields.
private ICollection<Order> _orders = new List<Order>();
private WrappedCollection<Order> _wrappedOrders = new WrappedCollection<Order>(_orders, this.AddOrder, null, null);
}
There are some other ways to call the WrappedCollection constructors
For example, to override add but keep remove and clear as normal
private WrappedListCollection<Order> _orders = new WrappedListCollection(this.AddOrder, (Order o) => _orders.RemoveFromInnerCollection(o), () => _orders.ClearInnerCollection());
I agree that it would be best if EF would not require the collection to be public but this solution allows me to control the modification of my collection.
For the problem of preventing access to the collection for querying you can use approach 2) above and set the WrappedCollection GetEnumerator method to throw a NotSupportedException. Then your GetOrder method can stay as it is. A neater method however may be to expose the wrapped collection. For example:
public class WrappedCollection<T> : WrappedCollectionBase<T>
{
public ICollection<T> InnerCollection { get; private set; }
public WrappedCollection(ICollection<T> collectionToWrap, Action<T> addItemFunction, Func<T, bool> removeItemFunction, Action clearFunction)
: base(addItemFunction, removeItemFunction, clearFunction)
{
this.InnerCollection = collectionToWrap;
}
protected override ICollection<T> GetWrappedCollection()
{
return this.InnerCollection;
}
}
Then the call in the GetOrder method would become
_orders.InnerCollection.Where(x => x.Id == id).Single();
Another way to accomplish this would be to create an associated interface for each of your POCOs to expose only what you want outside of the persistence/domain layers. You can also interface your DbContext class to also hide and control access to the DbSet collections. As it turns out, the DbSet properties can be protected, and the model builder will pick them up when it's creating tables, but when you try to access the collections they will be null. A factory method (in my example, CreateNewContext) can be used instead of the constructor to get the interfaced DbContext to conceal the DbSet collections.
There's quite a bit of extra effort in coding, but if hiding implementation details within the POCOs is important, this will work.
UPDATE: It turns out you CAN populate DBSets if they are protected, but not directly in the DBContext. They can't be aggregate roots (i.e. accessibility of the entity has to be through a collection in one of the public DBSet entities). If hiding the implementation of DBSet is important, the interface pattern I've described is still relevant.
public interface ICustomer
{
void AddOrder(IOrder order);
IOrder GetOrder(long id);
}
public Customer : ICustomer
{
// Exposed methods:
void ICustomer.AddOrder(IOrder order)
{
if (order is Order)
orders.Add((Order)order);
else
throw new Exception("Hey! Not a mapped type!");
}
IOrder ICustomer.GetOrder(long id)
{
return orders.Where(x => x.Id).Single();
}
// public collection for EF
// The Order class definition would follow the same interface pattern illustrated
// here for the Customer class.
public ICollection<Order> orders = new List<Order>();
}
public interface IMyContext
{
IEnumerable<ICustomer> GetCustomers();
void AddCustomer(ICustomer customerObject);
ICustomer CreateNewCustomer()
}
public class MyContext : DbContext, IMyContext
{
public static IMyContext CreateNewContext() { return new MyContext(); }
public DbSet<Customer> Customers {get;set;}
public DbSet<Order> Orders {get;set;}
public IEnumerable<ICustomer> GetCustomers()
{
return Customers;
}
public void AddCustomer(ICustomer customerObject)
{
if (customerObject is Customer)
Customers.Add((Customer)customerObject);
else
throw new Exception("Hey! Not a mapped type");
}
public ICustomer CreateNewCustomer()
{
return Customers.Create();
}
// wrap the Removes, Finds, etc as necessary. Remember to add these to the
// DbContext's interface
// Follow this pattern also for Order/IOrder
}
If you change the name of your _orders collection to the name of the orders table in your database, this should work. EF maps table/field names to collections/properties by convention. If you want to use a different name you could edit the mappings in the edmx file.
AFAIK you can just leave the private modifier as it is. Collections do not need to be public.