I've got a domain model structured like this:
public class Foo
{
private readonly HashSet<int> _intSet;
public IReadOnlySet<int> Intset => _intSet;
public Foo(HashSet<int> intSet)
{
_intSet = intSet;
}
}
Now, I'd like to use EF's Fluent API to persist it, but I keep getting the InvalidOperationException stating that:
No suitable constructor was found for entity type 'Foo'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'intSet' in 'Foo(HashSet<int> intSet).'
I've tried using the ModelBuilder options shown below, unfortunatelly to no avail:
modelBuilder
.Entity<Foo>()
.Property(p => p.Intset)
.HasField("_intSet")
.UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction);
Is there any actual way to make this work? Note that I definitely do not want to use DataAnnotations.
EF models should have a parameterless constructor
Something like this should work:
public class Foo
{
public Hashset<int> Intset { get; set;}
}
But you will lose that read-only policy.
If you want to keep that feature you should make some DTO class which would look like this
public class FooDto
{
private readonly HashSet<int> _intSet;
public IReadOnlySet<int> Intset => _intSet;
public FooDto(HashSet<int> intSet)
{
_intSet = intSet;
}
}
and then use that DTO with some mapping in vanilla style like this (or use mapping library like Automapper)
var fooDto = new FooDto(foo.Intset);
Related
I am trying to register class maps using MongoDb's C# driver. In my case I have a number of classes which derive from a base class. All have constructors which require parameters, and their properties are get-only. Interestingly, MongoDb throws an error when mapping a derived class of this kind unless its properties at least have a private setter.
See below for a minimal way to reproduce the exception in question, which occurs during configuration in startup:
public class Base
{
public Base(int myBaseProperty)
{
MyBaseProperty = myBaseProperty;
}
public int MyBaseProperty { get; }
}
public class Derived : Base
{
public Derived(int myBaseProperty, string myDerivedProperty) : base(myBaseProperty)
{
MyDerivedProperty = myDerivedProperty;
}
public string MyDerivedProperty { get; }
}
public class DerivedWithSetter : Base
{
public DerivedWithSetter(int myBaseProperty, string myDerivedProperty) : base(myBaseProperty)
{
MyDerivedProperty = myDerivedProperty;
}
public string MyDerivedProperty { get; private set; }
}
**IN STARTUP**
BsonClassMap.RegisterClassMap<Base>(cm =>
{
cm.AutoMap();
cm.MapCreator(b => new Base(b.MyBaseProperty));
});
BsonClassMap.RegisterClassMap<DerivedWithSetter>(cm =>
{
cm.AutoMap();
cm.MapCreator(d => new DerivedWithSetter(d.MyBaseProperty, d.MyDerivedProperty));
});
BsonClassMap.RegisterClassMap<Derived>(cm =>
{
cm.AutoMap();
cm.MapCreator(d => new Derived(d.MyBaseProperty, d.MyDerivedProperty));
});
This results in the following ArgumentOutOfRangeException when run:
The memberInfo argument must be for class Derived, but was for class Base. (Parameter 'memberInfo')
Specifically, the exception occurs when the class Derived is mapped, while DerivedWithSetter (identical aside from the private set) is mapped properly.
Primarily, I am curious why this is the case, and how I may resolve the issue (I wouldn't want to have to add private set to all my properties just so that database mapping works).
Am I using the driver in an unintended way? Or is there some other explanation?
Edit
If I change the constructor of Derived to not require an argument to be passed on to the base class, then the exception no longer occurs. For example:
public Derived(string myDerivedProperty) : base(2)
{
MyDerivedProperty = myDerivedProperty;
}
Here the literal value 2 is passed to the base constructor instead of an argument int myBaseProperty. In this case there is no exception. This might indicate then that it has something to do with how MongoDb evaluates constructors of derived classes when automapping properties. The addition of a private setter may be "solving" the issue simply by allowing MongoDb to completely circumvent the constructor. Is this intended behavior though, or should MongoDb hypothetically be able to automap via constructors in this way?
I've got a class
public class MyEntity
{
public IDateTimeProvider DateTimeProvider { get; }
public MyEntity(IDateTimeProvider dateTimeProvider)
{
DateTimeProvider = dateTimeProvider;
}
protected MyEntity()
{
}
}
When I map this to the DbContext, I want it to ignore this column (as it's not a primitive type). When it loads from the database, I want it to populate this column with an instance of DateTimeProvider.
Does anyone know how to do this?
I want to say something like:
modelBuilder
.Entity<MyEntity>()
.Property(x => DateTimeProvider)
.Ignore()
.OnLoadInject(new DateTimeProvider());
If you do not want to create a column in the database, you can use the [NotMapped] annotation on the property. More info:
http://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-first.aspx
I need to implement a pluggable system where Automapper profiles can be provided by many DLL.
The object to be mapped has a list of persons:
public class CompanySrc
{
public List<PersonSrc> Persons {get;set;}
}
public class CompanyDest
{
public List<PersonDest> Persons {get;set;}
}
PersonSrc and PersonDest are abstract classes that can be extended in each DLL:
DLL1:
public class EmployeeSrc : PersonSrc
{
...
}
public class EmployeeDest : PersonDest
{
...
}
DLL2:
public class ManagerSrc : PersonSrc
{
...
}
public class ManagerDest : PersonDest
{
...
}
The idea was to implement something similar to this:
public class DLL1Profile : Profile
{
public DLL1Profile()
{
CreateMap<PersonSrc, PersonDest>()
.Include<EmployeeSrc, EmployeeDest>();
CreateMap<EmployeeSrc, EmployeeDest>();
}
}
public class DLL2Profile : Profile
{
public DLL2Profile()
{
CreateMap<PersonSrc, PersonDest>()
.Include<ManagerSrc, ManagerDest>();
CreateMap<ManagerSrc, ManagerDest>();
}
}
Mapping is done in the following way
var mc = new MapperConfiguration(cfg =>
{
cfg.CreateMap<CompanySrc, CompanyDest>()
cfg.AddProfile(new DLL1Profile());
cfg.AddProfile(new DLL2Profile ());
});
IMapper sut = mc.CreateMapper();
var result = sut.Map<CompanyDest>(companySrc);
but this approach is not working. When the "People" list contains an Employee and a Manager, and I try to map the whole list I get an exception.
Any suggestion?
You are seeing this problem because you have multiple calls to CreateMap<PersonSrc, PersonDest>() - only one mapping can exist.
When you are extending your base class in different DLLs, don't use .Include, use .IncludeBase instead. Include requires that the profile including your base class is able to reference the derived class, which is most likely not what you want to happen.
You should define your base mapping somewhere common, presumably where Person is defined:
CreateMap<PersonSrc, PersonDest>();
In your DLL1 profile etc, use IncludeBase instead:
CreateMap<ManagerSrc, ManagerDest>()
.IncludeBase<PersonSrc, PersonDest>();
I would like to save some work by avoiding having 2 sets of entities in my code. As of now I have the first set which is just a bunch dummy surrogate entities for EF with default constructors and settable properties, so that it can map to them. The other one is a set of real entities that I use in my business code. The real ones are immutable and fully initialized at the time of being created by using initializing constructors.
Is there a way to avoid having surrogates and map straight to the real entities by using some sort of factories in EF that are able to deal with initializing constructors without using settable properies?
It isn't possible, EF require parameterless constructor and it must be able to set properties.
For better encapsulation you can make property setters protected. EF will still be able to set property values (via generated proxy) but from the outer point of view it will look immutable.
Can't add comment, so. Now it's possible, because EF now can map private properties. And in 6.1.3 doing it by default(not sure about previous releases).
Example below.
class Program
{
static void Main(string[] args)
{
using (var context = new MyContext())
{
context.MyImmutableClassObjects.Add(new MyImmutableClass(10));
context.MyImmutableClassObjects.Add(new MyImmutableClass(20));
context.SaveChanges();
var myImmutableClassObjects = context.MyImmutableClassObjects.ToList();
foreach (var item in myImmutableClassObjects)
{
Console.WriteLine(item.MyImmutableProperty);
}
}
Console.ReadKey();
}
}
public class MyContext : DbContext
{
public DbSet<MyImmutableClass> MyImmutableClassObjects { get; set; }
}
public class MyImmutableClass
{
[Key]
public int Key { get; private set; }
public int MyImmutableProperty { get; private set; }
private MyImmutableClass()
{
}
public MyImmutableClass(int myImmutableProperty)
{
MyImmutableProperty = myImmutableProperty;
}
}
Is there a way to generate mappings for NHibernate from POCO? This reply to my question makes NHibernate look very interesting. However i have a lot of classes that look like this
public class register_email_job
{
public PK id;
public user_data user;
}
public class user_comment : BaseComment2
{
[IsNullable]
public user_comment parent;
public user_data user;
}
I would like to easily translate this into something compatible with NHibernate. I wouldnbt mind modifying some queries but i prefer not to rewrite each class to use properties and modify class in such a way i need to change how its used everywhere.
-edit- Note that i use inheritance and user_comment has an object to user_comment (thus why it must be nullable. so it doesn't infinitely recurse. null is root).
You may want to take a look at the Auto Mapping abilities of Fluent NHibernate: http://wiki.fluentnhibernate.org/Auto_mapping
In order for NHibernate to construct proxies for your entity classes, you will need to make your non-private members virtual. Public fields will not work with proxy objects, these should be converted to properties.
public class register_email_job
{
public virtual PK id { get; set; }
public virtual user_data user { get; set; }
}
Fluent NHibernate is able to create mapping from classes. It can automap based on conventions, or you can write your own mappers.
Your entities and tables may not match the default conventions, there a several ways to override them.
Using classmap, your mapping might look like this:
public class register_email_job_map : ClassMap<register_email_job>
{
public register_email_job_map()
{
Id( x => x.Id );
References( x=> x.user );
}
}
public class user_comment_map : ClassMap<user_comment>
{
public register_email_job_map()
{
// properties from BaseComment2
References( x=> x.user );
References( x=> x.parent );
}
}