I could not find how to set up NHibernate with automapping conventions to detect my many to many relationship in the particular case where I'm using interfaces in references instead of concrete class. Notice that this work perfectly with One-To-Many and Many-To-One case.
Well for instance I have two entities:
public class Model4 : ModelBase, IModel4
{
public Model4()
{
Model5s = new List<IModel5>();
}
public virtual ValueInt D { get; set; }
public virtual IList<IModel5> Model5s { get; set; }
}
public class Model5 : ModelBase, IModel5
{
public Model5()
{
Model4s = new List<IModel4>();
}
public virtual ValueInt E { get; set; }
public virtual IList<IModel4> Model4s { get; set; }
}
where the interfaces are defined as:
public interface IModel4 : IModelBase
{
ValueInt D { get; set; }
IList<IModel5> Model5s { get; set; }
}
public interface IModel5 : IModelBase
{
ValueInt E { get; set; }
IList<IModel4> Model4s { get; set; }
}
I'm using conventions for references to translate the interface into class implementation when mapping:
public class ReferenceConvention : IReferenceConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IManyToOneInstance instance)
{
Type instanceType = instance.Class.GetUnderlyingSystemType();
if (instanceType.IsInterface)
{
// Assuming that the type starts with an I get the name of the concrete class
string className = instanceType.Name.Substring(1);
instance.CustomClass(instanceType.Assembly.GetType(
instanceType.FullName.Replace(instanceType.Name, className)));
}
instance.Cascade.All();
}
}
I would expect NHibernate to detect the model4 <-> model5 many-to-many relationship and to call my ManyToMany convention:
public class ManyToManyConvention : IHasManyToManyConvention
{
public void Apply(IManyToManyCollectionInstance instance)
{
//this code is not reached when using references through interfaces
}
}
NHibernate will never call the Apply method of ManyToManyConvention in the case I'm using references with interfaces (IList and IList).
However, if I use concrete implementation (IList and IList) the Apply code is called and the mapping is done as expected.
Do you know where I'm missing something in my conventions of where I'm wrong?
Related
I've got a rather complex model structure and I'm hoping I can implement the serialization with protobuf-net. I've written a couple of tests and most of them work absolutely fine, but some more complex generic class structures with inheritance on both the class type and the generic type don't work.
In the example below, the models don't have constructors, but I left them out to not overflow the post with information. Please assume that the parameterless constructors are implemented in all models. Also, I'm aware that some property names don't make sense, but it's a simplified prototype for a bigger project.
note: I know that this model is probably overcomplicated, but unfortunately I'm hugely stuck with it.
Models
[ProtoContract]
public class SaveData
{
[ProtoMember(1)]
public List<Entity> entities { get; set; }
}
[ProtoContract]
public abstract class Entity
{
[ProtoMember(2)]
public Guid ID { get; set; }
[ProtoMember(3)]
public string Name { get; set; }
}
[ProtoContract]
[ProtoInclude(910, typeof(SpecificEvent<SpecificEventArguments, SpecificEventContext>))]
public class Event<T, U> : Entity
where T : EventArguments
where U : EventContext
{
[ProtoMember(22)]
public string EventName { get; set; }
[ProtoMember(23)]
public T EventArguments { get; set; }
[ProtoMember(902)]
public U EventContext { get; set; }
}
[ProtoContract]
[ProtoInclude(30, typeof(SpecificEventArguments))]
public class EventArguments
{
[ProtoMember(24)]
public int Argument { get; set; }
}
[ProtoContract]
[ProtoInclude(901, typeof(SpecificEventContext))]
public class EventContext
{
[ProtoMember(900)]
public string SomeContext { get; set; }
}
[ProtoContract]
public class SpecificEventArguments : EventArguments
{
[ProtoMember(25)]
public int SomeID { get; set; }
}
[ProtoContract]
public class SpecificEventContext : EventContext
{
}
[ProtoContract]
public class SpecificEvent<T, U> : Event<T, U>
where T : SpecificEventArguments
where U : SpecificEventContext
{
}
When I try to write some serialization logic in a simple main cmd-script, I can't seem to figure out why the serialization doesn't work.
Logic
static void Main(string[] args)
{
SaveData saveData = new SaveData();
SpecificEvent<SpecificEventArguments, SpecificEventContext> specificEvent =
new SpecificEvent<SpecificEventArguments, SpecificEventContext>(
"specific", "event",
new SpecificEventArguments(1),
new SpecificEventContext());
saveData.entities.Add(specificEvent);
using (var file = File.Create("savedata.bin"))
{
Serializer.Serialize(file, saveData);
}
SaveData loaded = null;
using (var file = File.OpenRead("savedata.bin"))
{
loaded = Serializer.Deserialize<SaveData>(file);
}
}
I get the following exception:
System.InvalidOperationException: 'Unexpected sub-type: protobuf_net_test.Program+SpecificEvent`2[[protobuf_net_test.Program+SpecificEventArguments, protobuf-net-test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[protobuf_net_test.Program+SpecificEventContext, protobuf-net-test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
Can anyone please explain me what I'm doing wrong? All help is really appreciated!!
I'm working on an abstract algebra library for C#, but am having trouble with implementing interfaces. I have gotten the implementation to work for certain groups, but attempting to create rings is giving me some serious problems. In particular, I've got the following:
public class Scaffolding {
public interface IMonoid<T> : ISemiGroup<T> {
T Identity { get; set; }
}
public interface IGroup<T> : IMonoid<T> {
T Inverse(T a);
}
public interface IRing<T> {
IGroup<T> AdditiveStructure { get; set; }
IMonoid<T> MultiplicativeStructure { get; set; }
}
}
public class ModularMonoid : Scaffolding.IMonoid<int> {
// Implements all necessary monoid properties
}
public class ModularGroup : Scaffolding.IGroup<int> {
// Implements all necessary group properties
}
public class ModularRing : Scaffolding.IRing<int> {
public ModularGroup AdditiveStructure { get; set; }
public ModularMonoid MultiplicativeStructure { get; set; }
// Implement ring-specific properties
}
I get an error stating that 'ModularRing' does not implement interface member 'Scaffolding.IRing.AdditiveStructure'. 'ModularRing.AdditiveStructure' cannot implement 'Scaffolding.IRing.AdditiveStructure' because it does not have the matching return type of 'Scaffolding.IGroup'. I get a similar error for the MultiplicativeStructure. This is strange to me, because both the ModularGroup and ModularMonoid implement IGroup and IMonoid respectively.
Yes, those classes implement the interfaces, but that interface doesn't say "The type of the AdditiveStructure property is something that implements IGroup<T>" - it says that the type of the AdditiveStructure property is IGroup<T>. To implement the interface, you have to match return types exactly.
If you want to be able to implement the interface like that, you'd need to change the interface, potentially like this:
public interface IRing<T, TGroup, TMonoid>
where TGroup : IGroup<T>
where TMonoid : IMonoid<T>
{
TGroup AdditiveStructure { get; set; }
TMonoid MultiplicativeStructure { get; set; }
}
Then implement it as:
public class ModularRing : Scaffolding.IRing<int, ModularGroup, ModularMonoid>
{
public ModularGroup AdditiveStructure { get; set; }
public ModularMonoid MultiplicativeStructure { get; set; }
}
Alternatively, you should consider making the properties read-only. That way, if you're happy with the ModularRing users only using the IGroup<int> and IMonoid<int> definitions (rather than depending on anything extra exposed on ModularGroup and ModularMonoid) then you could stick with just the single type parameter, which would simplify things quite a lot. For example:
public interface IMonoid<T> : ISemiGroup<T>
{
T Identity { get; }
}
public interface IGroup<T> : IMonoid<T>
{
T Inverse(T a);
}
public interface IRing<T>
{
IGroup<T> AdditiveStructure { get; }
IMonoid<T> MultiplicativeStructure { get; }
}
Implementation:
public class ModularRing : Scaffolding.IRing<int>
{
public IGroup<int> AdditiveStructure { get; } = new ModularGroup();
public IMonoid<int> MultiplicativeStructure { get; } = new ModularMonoid();
}
(Or accept them in the constructor; I don't know enough about what you're trying to do with them.)
I have the following class setup
public abstract class SearchElement
{
public int Id { get; set; }
public SearchElement parent { get; set; }
public int Order { get; set; }
public UserQuery UserQuery { get; set; }
}
public class SearchGroup : SearchElement
{
public virtual ICollection<SearchElement> SearchObjects { get; set; }
public bool IsAndOperator { get; set; }
public SearchGroup()
{
this.SearchObjects = new List<SearchElement>();
}
}
public abstract class SearchCondition<IContext, OutputType> : SearchElement
{
public ComparisonTypes Comparison { get; set; }
public string Value { get; set; }
public abstract Expression<Func<OutputType, bool>> BuildConditionQuery(IContext context);
}
public class SearchPackage : SearchCondition<ISearchContext, ProjectParticipantQuestionnaireResponseGroup>
{
public override System.Linq.Expressions.Expression<Func<ProjectParticipantQuestionnaireResponseGroup, bool>> BuildConditionQuery(ISearchContext context)
{
return this.BuildCondition<ProjectParticipantQuestionnaireResponseGroup, int>(r => r.Package.Id, int.Parse(this.Value), this.Comparison);
}
}
Now for some reason, when in the EntityFramework context I specify:
public DbSet<SearchElement> SearchElements { get; set; }
The SearchGroup class gets detected and the appropriate fields get created in the SearchElement table. However, the SearchPackage class does not get detected and it's fields are not created in the SearchElement table.
I can of course create a DbSet for the SearchPackage, but there are multiple similar classes (same inheritance, although some with difference values) and I don't want to create a DbSet for each of them. Does anyone has suggestions about what I can do?
For clarity: I am using Entity Framework 6.1.3 and C# 4.5.1
EF 6 cannot map a CLR generic type. (Sorry, I can't find an authoritative reference on this right now.) This is the problem, not one of inheritance generally. When EF traverses your inheritance "tree," it gets to SearchCondition<,> and gives up.
Finding a way around this will probably require a rethinking of your object model to something more serialization-friendly. Is there a way that you can split your object-model into a set of services (that might contain generics) that interacts with a set of more easily mapped DTOs?
Another (possible, untested) option: Make an ISearchElement mapped interface. SearchPackage should implement it directly. This way, SearchPackage--and other SearchCondition<,> implementors should get "picked up" by EF inheritance traversal.
I am trying to make my method generic and I am stuck at a point and need your assistance. The code scenario is I have an abstract class say MyBaseAbs which contains common properties:
public abstract class MyBaseAbs
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
public string CommonProp3 { get; set; }
}
Now I have child classes:
public class Mychild1: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild1Prop2 { get; set; }
public string Mychild1Prop3 { get; set; }
}
and another child class:
public class Mychild2: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild2Prop2 { get; set; }
}
Now I have to create a common method which needs to perform some operations on the basis of Mychild1 and Mychild2, so what I did is:
public MyCustomClass SaveOperation<T>(T myObj)
where T : MyBaseAbs
{
SaveObject obj = new SaveObject();
}
so inside this method I need to write common code which does the mapping for SaveObject object according to the child object passed. How can I determine which object is passed and use properties accordingly.
One option would be to create a base Save function in your base class and make it virtual.
Then override the method in your child classes. This way when you call the Save method in your SaveOperation it should call the appropriate method from the correct child class.
public abstract class MyBaseAbs
{
public string CommonProp1 { get; set; }
public string CommonProp2 { get; set; }
public string CommonProp3 { get; set; }
public virtual void Save() { }
}
public class Mychild1: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild1Prop2 { get; set; }
public string Mychild1Prop3 { get; set; }
public override void Save() {
//Implementation for Mychild1
}
}
public class Mychild2: MyBaseAbs
{
public string Mychild1Prop1 { get; set; }
public string Mychild2Prop2 { get; set; }
public override void Save() {
//Implementation for Mychild2
}
}
If you can't modify your business objects, you can check the type of the concrete class in the SaveOperation method:
public MyCustomClass SaveOperation<T>(T myObj)
where T : MyBaseAbs
{
SaveObject obj = new SaveObject();
if (myObj is Mychild1) {
Mychild1 mychild1 = (Mychild1) myObj;
// Business logic for object of type Mychild1
} else if (myObje is Mychild2) {
Mychild2 mychild2 = (Mychild2) myObj;
// Business logic for object of type Mychild2
}
}
Notice that this is not a very solid solution as, if you are creating new objects that implement your abstract class, you will have to remeber to add another branch in the if statement.
As #BojanB mentioned, the obvious solution would be to create a virtual method in your base class and override it in the derived, but if you cannot modify the code there then you can create a method for each derived class and create a dictionary that maps each type to its method:
private Dictionary<Type, Action<MyBaseAbs, MyCustomClass>> _saveOperations =
new Dictionary<Type, Action<MyBaseAbs, MyCustomClass>>();
//You can then set an entry for each of your derived classes
_saveOperations[typeof(Mychild1)] = (myObj, myCustomObj) =>
{
//Mychild1-specific logic
};
public MyCustomClass SaveOperation(MyBaseAbs obj)
{
//do the common saving operations here
var result = new MyCustomClass();
//....
var actualType = obj.GetType();
if(_saveOperations.ContainsKey(actualType))
{
_saveOperations[actualType](obj, result);
}
return result;
}
You can then add an item to the dictionary for each derived class. It is the same concept as using the is operator but allows you to add methods for more derived types without modifying the original SaveOperation method
You can use C#'s As-Operator as follows:
Mychild1 child1 = myObj as Mychild1;
if(child1 != null) {
//Here you can use child1.Mychild1Prop1 forexample
}
Link to msdn: https://msdn.microsoft.com/en-us/library/cscsdfbt.aspx
Suppose, i have main class for data representation and this class have configuration field. This field must be able to answer some questions related to main class (assume, that this is one question - 'IsMainClassReadyToUse'). But inner structure of this class may be different.
Because of it, i want create abstract class Configurator and depending on situation use various Configuratos that implement its functional.
So, i have following code:
public class SimpleConfigurator : Configurator
{
public int FieldA { get; set; }
public override bool IsDataClassReadyToUse()
{
return ParentDataClass.FieldA == FieldA;
}
}
public class ComplexConfigurator : Configurator
{
public virtual List<int> FieldsB { get; set; }
public override bool IsDataClassReadyToUse()
{
return ParentDataClass.FieldsB.All(x => FieldsB.Any(y => y == x));
}
}
public abstract class Configurator
{
public int ConfiguratorId { get; set; }
public virtual DataClass ParentDataClass { get; set; }
public abstract bool IsDataClassReadyToUse();
}
public class DataClass
{
public int DataClassId { get; set; }
public virtual Configurator Configurator { get; set; }
public int FieldA { get; set; }
public virtual List<int> FieldsB { get; set; }
}
public class DataDbContext : DbContext
{
public DbSet<DataClass> DataClasses { get; set; }
}
But the problem appears when i try use DataClass instance with Configurator of type ComplexConfigurator.
Because of LazyLoading i need to load FieldsB from ComplexConfigurator, but abstract class Configurator doesn't contain such field and i can't write such code:
new DataDbContext().DataClasses
.Include(m => m.Configurator)
.Include(m => m.Configurator.FieldsB);
I tried to disable LazyLoading, adding such constructor in DataDbContext:
public DataDbContext()
{
Configuration.LazyLoadingEnabled = false;
}
But when i try get access to FieldsB it still be null.
So, how can i implement such architecture with Entity Framework?
Or maybe i should choose another architecture for such task?
I think you should try access you configurator such as
((ComplexConfigurator)yourObject.Configurator).FieldsB
But I'm afraid EF works wrong with List<int> property (when I tried do that sometimes I've got a fail) and better way is to create class Option and field List<Option> Options into your configurator instead of List with integers.
You also should check your DB scheme (there's should be a table "Configurators" with idenitifator field and all SimpleConfigurator and ComplexConfigurator's fields). May be you should add DbSet<Configurator> into your DbContext definition.
You can read this article for getting more information about inheritance and EF.