Entity and Component has one-to-many relationship. Entity stored in db correctly, with all components. But when I trying load it back this Entity in Child method Components not loads. If i try use lazy loading, code failing on entity.GetComponent because of Entity.Components isn't initialized. After exception Components are initialized and has zero elements. If I disable lazy loading, Components are initialized and has zero elements. I wrote example for building one-to-many relationship and using lazy initialization, and it works fine.
public static void Main(string[] args)
{
Parent();
Child();
}
private static void Child()
{
using (var db = new EntitiesContext())
{
var entities = from entity in db.Entities
select entity;
foreach (Entity entity in entities)
{
Position pos = entity.GetComponent<Position>();
Core core = entity.GetComponent<Core>();
}
}
}
private static void Parent()
{
Entity entity = new Entity();
entity.AddComponent(new Position(10, 10));
entity.AddComponent(new ObjectName("Entity" + 1));
entity.AddComponent(new Core(100));
using (var db = new EntitiesContext())
{
db.Entities.Add(entity);
db.SaveChanges();
}
}
public class EntitiesContext : DbContext
{
public DbSet<TypeMaskPair> MappedTypes { get; set; }
public DbSet<Entity> Entities { get; set; }
public EntitiesContext()
: base("EntitiesDb")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasMany(entity => entity.Components)
.WithRequired(component => component.EntityObj)
.HasForeignKey(component => component.EntityId)
.WillCascadeOnDelete();
//this.Configuration.LazyLoadingEnabled = false;
}
}
public abstract class Component
{
[Key]
public int Id { get; set; }
[Required]
public int EntityId { get; set; }
[Required]
public virtual Entity EntityObj { get; set; }
private static DbMasksMapper componentsMap;
static Component()
{
componentsMap = new DbMasksMapper(typeof(Component));
}
public TypeMask GetMask()
{
return componentsMap.GetMask(this.GetType());
}
public static TypeMask GetMask<T>() where T : Component
{
return componentsMap.GetMask(typeof(T));
}
}
public class Entity
{
[Key]
public int Id { get; set; }
public virtual List<Component> Components { get; set; }
[NotMapped]
public TypeMask Mask { get; private set; }
public string TypeMaskString
{
get{ return Mask.ToString(); }
set{ Mask = new TypeMask(value); }
}
public Entity()
{
Components = new List<Component>();
Mask = new TypeMask();
}
public void AddComponent(Component component)
{
Components.Add(component);
component.EntityObj = this;
Mask |= component.GetMask();
}
public void DeleteComponent(TypeMask componentMask)
{
if (ContainsComponent(componentMask))
{
int removeIndex = Components.FindIndex(c => c.GetMask() == componentMask);
Components.RemoveAt(removeIndex);
Mask &= ~componentMask;
}
}
public Component GetComponent(TypeMask componentMask)
{
return Components.Find(c => c.GetMask() == componentMask);
}
public T GetComponent<T>() where T : Component
{
return (T) GetComponent(Component.GetMask<T>());
}
public bool ContainsComponent<T>() where T : Component
{
return ContainsComponent(Component.GetMask<T>());
}
public bool ContainsComponent(TypeMask componentMask)
{
return (Mask & componentMask) == componentMask;
}
}
class Position : Component
{
public Position(int x = 0, int y = 0)
{
X = x;
Y = y;
}
public int X { get; set; }
public int Y { get; set; }
}
class Cargo : Component
{
public Cargo(int capacity = 0)
{
Capacity = capacity;
}
public int Capacity { get; set; }
}
class Core : Component
{
public Core(int power = 0)
{
Power = power;
}
public int Power { get; set; }
}
class ObjectName : Component
{
public ObjectName(string name = "")
{
Name = name;
}
public string Name { get; set; }
}
I saw similar questions, but didn't found any answer.
Where is a mistake?
Solution
All works after I wrote default constructor for inherited components. But, I don't understand why constructor with default arguments doesn't suitable. Without any argument it should work like default constructor. Can anyone explain it? Seems I doing it wrong
Related
I have 3 classes as you can see :
public class car
{
public int Id { set; get; }
public string Name { set; get; }
public List<CarTest> CarTest { get; set; }
public car(string name, int id, List<CarTest> carTests)
{
Id = id;
Name = name;
CarTest = carTests;
}
public car()
{
}
}
public class CarTest
{
public OVTest OV { get; set; }
public string CarOV { get; set; }
}
public class OVTest
{
public string Name { get; set; }
}
I want to edit my value as you can see :
static void Main(string[] args)
{
myctx myc = new myctx();
var cartests = new List<CarTest>();
var cartest = new CarTest();
var OV = new OVTest();
OV.Name = "editedName";
cartest.OV = OV;
cartest.CarOV = "2OV";
cartests.Add(cartest);
var editcar = new car("ee5155fe",2, cartests);
myc.ChangeTracker.TrackGraph(editcar, e =>e.Entry.State = EntityState.Modified);
myc.SaveChanges();
Console.ReadLine();
}
But I get this error :
System.InvalidOperationException: 'The property 'CarTestId' on entity type 'OVTest' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.'
This is my config:
public class CarConfig : IEntityTypeConfiguration<car>
{
public void Configure(EntityTypeBuilder<car> builder)
{
builder.OwnsMany(u => u.CarTest);
builder.OwnsMany(u => u.CarTest).OwnsOne(c=>c.OV);
}
}
I am using EF Core 2.2.2 in my project, and I am getting this error when trying to fetch any data from DB, I am not sure what is the reason of this, but it works fine when trying to insert new item to DB:
Error Message :
"A column has been specified more than once in the order by list.
Columns in the order by list must be unique"
I think the issue happened because of using nested OwnsMany many times but not sure:
public class Program
{
private static void Main(string[] args)
{
using(var context = new OwnsManyIssueContext())
{
var accidentEstimationDetails = new List<AccidentEstimationDetails>
{
new AccidentEstimationDetails(true, "test1"),
new AccidentEstimationDetails(false, "test2"),
new AccidentEstimationDetails(true, "test3")
};
var accidentEstimation = new AccidentEstimation(12, 11, accidentEstimationDetails);
var partiesLiabilityAmounts = new List<PartyAmountDetails>
{
new PartyAmountDetails(1,11,120),
new PartyAmountDetails(1,12,121),
new PartyAmountDetails(1,10,122)
};
var accident = new Accident(accidentEstimation, partiesLiabilityAmounts);
context.Accidents.Add(accident);
context.SaveChanges();
var dbAggregate = context.Accidents.FirstOrDefault();
}
}
}
public class Accident
{
public int Id { get; private set; }
public AccidentEstimation AccidentEstimation { get; private set; }
private List<PartyAmountDetails> _partiesAmountDetails;
public IReadOnlyCollection<PartyAmountDetails> PartiesAmountDetails => _partiesAmountDetails;
private Accident()
{
_partiesAmountDetails = new List<PartyAmountDetails>();
}
public Accident(AccidentEstimation accidentEstimation, List<PartyAmountDetails> partiesLiabilityAmount)
{
AccidentEstimation = accidentEstimation;
_partiesAmountDetails = partiesLiabilityAmount;
}
}
public class AccidentEstimation
{
public int EstimationPartyId { get; private set; }
public int FollowerId { get; private set; }
private readonly List<AccidentEstimationDetails> _details;
public IReadOnlyCollection<AccidentEstimationDetails> Details => _details;
private AccidentEstimation()
{
_details = new List<AccidentEstimationDetails>();
}
public AccidentEstimation(int estimationPartyId, int followerId, List<AccidentEstimationDetails> details)
{
EstimationPartyId = estimationPartyId;
FollowerId = followerId;
_details = details;
}
}
public class AccidentEstimationDetails
{
public bool IsApproved { get; private set; }
public string Name { get; private set; }
private AccidentEstimationDetails()
{
}
public AccidentEstimationDetails(bool isApproved, string name)
{
IsApproved = isApproved;
Name = name;
}
}
public class PartyAmountDetails
{
public int ItemTypeId { get; private set; }
public decimal Amount { get; private set; }
public decimal? Discount { get; private set; }
private PartyAmountDetails()
{
}
public PartyAmountDetails(int itemTypeId, decimal amount, decimal? discount)
{
ItemTypeId = itemTypeId;
Amount = amount;
Discount = discount;
}
}
public class AccidentTypeConfiguration : IEntityTypeConfiguration<Accident>
{
public void Configure(EntityTypeBuilder<Accident> builder)
{
builder.ToTable("Accidents");
builder.Property(b => b.Id).ValueGeneratedOnAdd(); ;
builder.HasKey(b => b.Id);
builder.OwnsOne(a => a.AccidentEstimation, a =>
{
a.Property(p => p.FollowerId).HasColumnName("FollowerId");
a.Property(p => p.EstimationPartyId).HasColumnName("EstimationPartyId");
a.OwnsMany(dd => dd.Details, dd =>
{
dd.ToTable("AccidentEstimationDetails");
dd.Property<int>("Id");
dd.HasKey("Id");
});
});
builder.OwnsMany(a => a.PartiesAmountDetails, a =>
{
a.ToTable("PartiesLiabilityAmounts");
a.Property<long>("Id");
a.HasKey("Id");
});
}
}
public class OwnsManyIssueContext : DbContext
{
public DbSet<Accident> Accidents { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"ConnectionString");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new AccidentTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
I have these two classes:
public class LeadPerformanceItem
{
public string name { get; set; }
public int visitors { get; set; }
public decimal visitorspercentoftotal
{
get
{
// ?
}
}
}
public class LeadPerformanceItemCollection
{
public List<LeadPerformanceItem> items {get;set;}
public int totalvisitors
{
get
{
return items.Sum(x => x.visitors);
}
}
}
Is there anyway my visitorspercentoftotal property could be automatically calculated as items are added and removed from the collection?
public class LeadPerformanceItem
{
public string name { get; set; }
public int Visitors { get; set; }
private int _totalVisitors = 0;
public void UpdateTotalVisitors(int total)
{
this._totalVisitors = total;
}
public decimal Visitorspercentoftotal => _totalVisitors != 0
? Convert.ToDecimal(Math.Round(((double) (Visitors * 100)) / _totalVisitors))
: 0;
}
public class LeadPerformanceItemCollection
{
public List<LeadPerformanceItem> Items { get; set; }
public void AddToItems(LeadPerformanceItem item)
{
Items.Add(item);
var total = Items.Sum(x => x.Visitors);
Items.AsParallel().ForAll(i => i.UpdateTotalVisitors(total));
}
public int totalvisitors
{
get { return Items.Sum(x => x.Visitors); }
}
}
[TestFixture]
public class Class1
{
[Test]
public void Test()
{
var leadPerformanceItemCollection = new LeadPerformanceItemCollection();
leadPerformanceItemCollection.Items=new List<LeadPerformanceItem>();
leadPerformanceItemCollection.AddToItems(new LeadPerformanceItem()
{
name = "test",
Visitors = 10
});
leadPerformanceItemCollection.AddToItems(new LeadPerformanceItem()
{
name = "test2",
Visitors = 25
});
Console.WriteLine(leadPerformanceItemCollection.Items[0].Visitorspercentoftotal);
Console.WriteLine(leadPerformanceItemCollection.Items[1].Visitorspercentoftotal);
}
}
result:
29%
71%
One way would be to inherit from List and hide the Add method and create your own and do the calculation there.
public class LeadPerformanceItemCollection : List<LeadPerformanceItem>
{
public new void Add(LeadPerformanceItem item)
{
//calculate percent of total here
base.Add(item);
}
}
I use AutoMapper 8.1.0 in a asp.net core project. I have an Automapper mapping that doesn't work as I expected. I reproduced the configuration so you can test it by yourself. So I have an ExpenseReport with a collection of ExpenseReportItem and this one with another collection. I have to keep the data of eTaxCollection after the mapping, but they are lost in the process.
So the question is why values of eTaxCollections are lost after calling _mapper.Map(vmodel, model) and how can I keep them?
The ignore attribute don't work. I also tried UseDestinationValue(). I lost 2 days trying to figure it out and I'm exhausted.
public void WeatherForecasts()
{
int[] excludeTaxes = new int[] { 2 };
var vmodel = new ExpenseReportCreateEditModel();
vmodel.Expenses.Add(new ExpenseReportItemModel()
{
ExcludeTaxIds = excludeTaxes,
Total = 12,
Id = 1
});
// fetch from bd
var model = new ExpenseReport();
// values will be lost after _mapper.Map...
var eTaxCollections = new HashSet<ExcludeExpenseReportItemTax>();
eTaxCollections.Add(new ExcludeExpenseReportItemTax()
{
TaxId = 1,
ExpenseReportItemId = 1
});
model.Items.Add(new ExpenseReportItem()
{
ExcludeTaxes = eTaxCollections,
ExpenseReportId = 1,
Id = 9
});
_mapper.Map(vmodel, model);
}
public class ExpenseReportCreateEditModelProfile : Profile
{
public ExpenseReportCreateEditModelProfile()
{
CreateMap<ExpenseReportCreateEditModel, ExpenseReport>()
.ForMember(d => d.Items, s => s.MapFrom(m => m.Expenses));
}
}
public class ExpenseReportItemModelProfile : Profile
{
public ExpenseReportItemModelProfile()
{
CreateMap<ExpenseReportItemModel, ExpenseReportItem>()
.ForMember(d => d.ExcludeTaxes, s => s.Ignore()); // <<<==== data are lost
}
}
public class ExpenseReportCreateEditModel
{
public int Id { get; set; }
public ICollection<ExpenseReportItemModel> Expenses { get; set; }
public ExpenseReportCreateEditModel()
{
Expenses = new HashSet<ExpenseReportItemModel>();
}
}
public class ExpenseReportItemModel
{
public int Id { get; set; }
public ICollection<int> ExcludeTaxIds { get; set; }
public decimal Total { get; set; }
public ExpenseReportItemModel()
{
ExcludeTaxIds = new HashSet<int>();
}
}
public class ExpenseReport
{
public int Id { get; set; }
public virtual ICollection<ExpenseReportItem> Items { get; set; }
public ExpenseReport()
{
Items = new HashSet<ExpenseReportItem>();
}
}
public class ExpenseReportItem
{
public int Id { get; set; }
public int ExpenseReportId { get; set; }
public virtual ICollection<ExcludeExpenseReportItemTax> ExcludeTaxes { get; set; }
public ExpenseReportItem()
{
ExcludeTaxes = new HashSet<ExcludeExpenseReportItemTax>();
}
}
public class ExcludeExpenseReportItemTax
{
public int ExpenseReportItemId { get; set; }
public virtual ExpenseReportItem ExpenseReportItem { get; set; }
public int TaxId { get; set; }
}
Thank you for any help
Edit
I execute the execution plan and perhaps this is the problem:
$typeMapDestination = ($dest ?? .New WebApplication1.Controllers.SampleDataController+ExpenseReportItem());
This is only way I can lost the values.
I have to find a solution now
Here the complete execution plan :
.If ($src == null) {
.Default(WebApplication1.Controllers.SampleDataController+ExpenseReportItem)
} .Else {
.Block() {
$typeMapDestination = ($dest ?? .New WebApplication1.Controllers.SampleDataController+ExpenseReportItem());
.Try {
.Block(System.Int32 $resolvedValue) {
.Block() {
$resolvedValue = .If (
$src == null || False
) {
.Default(System.Int32)
} .Else {
$src.Id
};
$typeMapDestination.Id = $resolvedValue
}
}
} .Catch (System.Exception $ex) {
.Block() {
.Throw .New AutoMapper.AutoMapperMappingException(
"Error mapping types.",
$ex,
.Constant<AutoMapper.TypePair>(AutoMapper.TypePair),
.Constant<AutoMapper.TypeMap>(AutoMapper.TypeMap),
.Constant<AutoMapper.PropertyMap>(AutoMapper.PropertyMap));
.Default(System.Int32)
}
};
$typeMapDestination
}
}
I'm working on a project where I have some recursive data structure and I want to create a fixture for it.
The data structure is XmlCommandElement, it has a single method ToCommand that converts XmlCommandElement to Command.
Each node on the tree can be a XmlCommandElement and/or XmlCommandPropertyElement.
Now, in order to test the behaviour of the method ToCommand I want to fetch XmlCommandElement with some arbitrary data.
I want to control the depth of the tree and the amount of instances of XmlCommandElement and/or XmlCommandPropertyElement per node.
So here is the code I'm using for the fixture:
public class XmlCommandElementFixture : ICustomization
{
private static readonly Fixture _fixture = new Fixture();
private XmlCommandElement _xmlCommandElement;
public int MaxCommandsPerDepth { get; set; }
public int MaxDepth { get; set; }
public int MaxPropertiesPerCommand { get; set; }
public XmlCommandElementFixture BuildCommandTree()
{
_xmlCommandElement = new XmlCommandElement();
var tree = new Stack<XmlCommandElementNode>();
tree.Push(new XmlCommandElementNode(0, _xmlCommandElement));
while (tree.Count > 0) {
var node = tree.Pop();
node.Command.Key = CreateRandomString();
node.Command.Properties = CreateProperties();
if (MaxDepth > node.Depth) {
var commands = new List<XmlCommandElement>();
for (var i = 0; i < MaxCommandsPerDepth; i++) {
var command = new XmlCommandElement();
tree.Push(new XmlCommandElementNode(node.Depth + 1, command));
commands.Add(command);
}
node.Command.Commands = commands.ToArray();
}
}
return this;
}
public void Customize(IFixture fixture)
{
fixture.Customize<XmlCommandElement>(c => c.FromFactory(() => _xmlCommandElement)
.OmitAutoProperties());
}
private static string CreateRandomString()
{
return _fixture.Create<Generator<string>>().First();
}
private XmlCommandPropertyElement[] CreateProperties()
{
var properties = new List<XmlCommandPropertyElement>();
for (var i = 0; i < MaxPropertiesPerCommand; i++) {
properties.Add(new XmlCommandPropertyElement {
Key = CreateRandomString(),
Value = CreateRandomString()
});
}
return properties.ToArray();
}
private struct XmlCommandElementNode
{
public XmlCommandElementNode(int depth, XmlCommandElement xmlCommandElement)
{
Depth = depth;
Command = xmlCommandElement;
}
public XmlCommandElement Command { get; }
public int Depth { get; }
}
}
And this is how I'm using it:
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
This works perfectly fine! but the issue I have with it is it isn't generic, the whole point of AutoFixture at least as far as I know is to avoid making specific fixtures.
So what I would really like to do is something like this (found it here but it doesn't work for me.):
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandElement), 3));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandPropertyElement), 4));
xmlCommandElement = fixture.Create<XmlCommandElement>();
Here is all the code for reference:
Interfaces:
public interface ICommandCollection : IEnumerable<ICommand>
{
ICommand this[string commandName] { get; }
void Add(ICommand command);
}
public interface ICommandPropertyCollection : IEnumerable<ICommandProperty>
{
string this[string key] { get; }
void Add(ICommandProperty property);
}
public interface ICommandProperty
{
string Key { get; }
string Value { get; }
}
public interface ICommand
{
ICommandCollection Children { get; set; }
string Key { get; }
ICommandPropertyCollection Properties { get; }
}
public interface ICommandConvertible
{
ICommand ToCommand();
}
Classes:
public sealed class CommandPropertyCollection : ICommandPropertyCollection
{
private readonly IDictionary<string, ICommandProperty> _properties;
public CommandPropertyCollection()
{
_properties = new ConcurrentDictionary<string, ICommandProperty>();
}
public string this[string key]
{
get
{
ICommandProperty property = null;
_properties.TryGetValue(key, out property);
return property.Value;
}
}
public void Add(ICommandProperty property)
{
_properties.Add(property.Key, property);
}
public IEnumerator<ICommandProperty> GetEnumerator()
{
return _properties.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public sealed class CommandProperty : ICommandProperty
{
public CommandProperty(string key, string value)
{
Key = key;
Value = value;
}
public string Key { get; }
public string Value { get; }
}
public sealed class Command : ICommand
{
public Command(string key, ICommandPropertyCollection properties)
{
Key = key;
Properties = properties;
}
public ICommandCollection Children { get; set; }
public string Key { get; }
public ICommandPropertyCollection Properties { get; }
}
public class XmlCommandPropertyElement : ICommandPropertyConvertible
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
public ICommandProperty ToCommandProperty()
{
return new CommandProperty(Key, Value);
}
}
Finally, the class I'm trying to test is as follow:
public class XmlCommandElement : ICommandConvertible
{
[XmlArray]
[XmlArrayItem("Command", typeof(XmlCommandElement))]
public XmlCommandElement[] Commands { get; set; }
[XmlAttribute("key")]
public string Key { get; set; }
[XmlArray]
[XmlArrayItem("Property", typeof(XmlCommandPropertyElement))]
public XmlCommandPropertyElement[] Properties { get; set; }
public ICommand ToCommand()
{
ICommandPropertyCollection properties = new CommandPropertyCollection();
foreach (var property in Properties) {
properties.Add(property.ToCommandProperty());
}
ICommand command = new Command(Key, properties);
return command;
}
}
The test itself looks like this:
namespace Yalla.Tests.Commands
{
using Fixtures;
using FluentAssertions;
using Ploeh.AutoFixture;
using Xbehave;
using Yalla.Commands;
using Yalla.Commands.Xml;
public class XmlCommandElementTests
{
[Scenario]
public void ConvertToCommand(XmlCommandElement xmlCommandElement, ICommand command)
{
$"Given an {nameof(XmlCommandElement)}"
.x(() =>
{
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
});
$"When the object is converted into {nameof(ICommand)}"
.x(() => command = xmlCommandElement.ToCommand());
"Then we need to have a root object with a key"
.x(() => command.Key.Should().NotBeNullOrEmpty());
"And 4 properties as its children"
.x(() => command.Properties.Should().HaveCount(4));
}
}
}
Thanks to Mark Seemann! the final solution looks like this:
public class RecursiveCustomization : ICustomization
{
public int MaxDepth { get; set; }
public int MaxElements { get; set; }
public void Customize(IFixture fixture)
{
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(MaxDepth));
fixture.RepeatCount = MaxElements;
}
}
And can be used like this:
xmlCommandElement = new Fixture().Customize(new RecursiveCustomization {
MaxDepth = 2,
MaxElements = 3
}).Create<XmlCommandElement>();
You can fairly easily create a small tree by changing the Fixture's recursion behaviour:
[Fact]
public void CreateSmallTree()
{
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(recursionDepth: 2));
var xce = fixture.Create<XmlCommandElement>();
Assert.NotEmpty(xce.Commands);
}
The above test passes.