Cyclic dependency check using EF Core - c#

I have the following requirement whilst using EF Core 3. I have Exercises with (Id = 1 Pushup), (Id = 2 Squat), (Id = 3 Weight Lift) and users. Once one exercise is completed, one ore more new exercise(s) are created. A multilevel hierarcy can be created with n level.
I have an issue though that can cause cyclic dependencies to the exercices.
e.g Pushup (1) is completed. Squat (2) is created. Squat (2) is completed then Weight Lift (3) is created. There is also a dependency on Weight Lift (3) that once it is completed it creates Pushup(1) with leads to cyclic dependency and I need to avoid that.
I gave a very simple example. In my project, dependencies can go up to level 50 and I dont know how to check for cyclic reference and avoid them.
Below is the code:
public class Exercise
{
public Exercise()
{
ExerciseDependencyExercises = new HashSet<ExercisesDependency>();
ExerciseDependencyTargetExercises = new HashSet<ExercisesDependency>();
UserExercises = new HashSet<UserExercise>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<ExercisesDependency> ExerciseDependencyExercises { get; set; }
public virtual ICollection<ExercisesDependency> ExerciseDependencyTargetExercises { get; set; }
public virtual ICollection<UserExercise> UserExercises { get; set; }
}
public class UserExercise
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public long UserId { get; set; }
public long ExerciseId { get; set; }
}
public class ExercisesDependency
{
public long ExerciseId { get; set; }
public virtual Exercise Exercise { get; set; }
public long TargetExerciseId { get; set; }
public virtual Exercise TargetExercise { get; set; }
}

Easiest solution I can think of is to not have your requirements dictate primary keys values (which should only be used to uniquely identify a record).
I don't know/understand why you have this requirement. But I would suggest just continue numbering, and while creating the new records to calculate whether it should be an exercise of type 1, 2 or 3. e.g. 1=>1, 2=>2, 3=>3, 4=>1, 5=>2, 6=>3, 7=>1.
Then a simple: var type = ((id - 1) %3) + 1; returns that list of numbers.

Related

How do I relate the data from my entities? ASP.NET MVC 5 EF 6

I'm building an ASP.NET MVC application and I'm not quite sure how to make some relations between my entities.
I have the following entities
namespace Entities
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
public class Classroom
{
public int Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
}
public class CoffeeSpace
{
public int Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
}
}
My problem is this: a training event is going to happen in a school. The people will be distributed in classrooms with variable capacity and the training will happen in two steps. There will also happen two coffee breaks with two distinct areas.
The application should allow to:
Register people with name and surname;
Register classrooms with name and capacity;
Register coffee areas with name and capacity.
The difference in the number of people in each classroom should be of at maximum 1 person. To stimulate the knowledge exchange, half the people in each classroom should move to another classroom between each step of the training.
When consulting a person registered in this event, the application should return the classroom this person will stay in each step of the training and the area where they will do each coffee break.
When consulting a classroom or coffee area registered in this event, the application should return a list of people that will be in that classroom/area in each step/break of the event.
I'm new to programming in general, and I know there are some kind of relations you can do between your entities using entity framework, but looking at it I'm completely lost and I can't seem to understand what type of relation I should use in this problem.
I would resolve this problem with middle tables 'between' Person and Classroom, would name it PersonxClass:
public class PersonxClass
{
public int Id { get; set; }
public int PersonId { get; set; }
public int ClassId { get; set; }
}
in this entity I will save persons that are in a specific Classroom. good, second step, I will do another entity 'between' Person and CoffeeSpace, would name it PersonxCoffee:
public class PersonxCoffee
{
public int Id { get; set; }
public int PersonId { get; set; }
public int CoffeeId { get; set; }
}
Equals that the before case, in this entity i will save data of persons for each CoffeeSpace.
I wait that this info is help for you.

Why EF tries to convert List<Type> to Type when saving an object with a list of related objects (1 to many)?

When I try to save a Consumption object by calling context.Consumption.Add(myConsumption) and then context.SaveChanges(), I get a System.InvalidCastException. I have no clue why EF wants to cast the List<Payment> to Payment, since the Payments property of Consumption is of type List<Payment>.
public class Consumption {
public int Id { get; set; }
...
public virtual List<Payment> Payments { get; set; }
}
public class Payment {
...
[ForeignKey("Id")]
public Consumption Consumption { get; set; }
}
I think you have things a little out of wack
Assuming you have the following design
public class Consumption
{
public int Id { get; set; }
...
public virtual List<Payment> Payments { get; set; }
}
public class Payment {
public int Id { get; set; }
public Consumption Consumption { get; set; }
public int ConsumptionId { get; set; }
}
EF is smart enough to figure out the relationship and database scaffolding based on naming convention
However if you need to use Annotations, then you need to make a decision of how you are telling EF about your relationship
public class Payment {
public int Id { get; set; }
public Consumption Consumption { get; set; }
[ForeignKey("Consumption")] // point to the FK to the navigation property
public int ConsumptionId { get; set; }
}
Or
public class Payment {
public int Id { get; set; }
[ForeignKey("ConsumptionId")] // point to the navigation property to the FK
public Consumption Consumption { get; set; }
public int ConsumptionId { get; set; }
}
Or
public class Consumption
{
public int Id { get; set; }
...
[ForeignKey("ConsumptionId")] // Point to the dependent FK
public virtual List<Payment> Payments { get; set; }
}
My suspicion is that in your database (and this should be easy to check) you just have a singular foreign key in your Consumption table for payment
You seem to be pointing to the Primary key of your Payments table. However musings aside, it surprises me that EF even let you do this migration (if i'm reading this correctly)

Why is my EntityFramework insert so slow?

I've found a bottleneck in my application to be the insert operation for one particular entity (into three tables, through navigation properties). The classes are defined as follows:
public class TrackerState
{
public int Id { get; set; }
[Index]
public int TrackerId { get; set; }
[Index]
public DateTime DateRecorded { get; set; }
public DateTime DatePublished { get; set; }
public DateTime DateReceived { get; set; }
public LocationStatus LocationStatus { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public double Altitude { get; set; }
public double Accuracy { get; set; }
public string Source { get; set; }
public double Speed { get; set; }
public double Heading { get; set; }
public int PrimaryOdometer { get; set; }
public int SecondaryOdometer { get; set; }
public int OperationalSeconds { get; set; }
public virtual IList<AnalogState> AnalogStates { get; set; }
public virtual IList<DigitalState> DigitalStates { get; set; }
}
public class AnalogState
{
public int TrackerStateId { get; set; }
public virtual TrackerState TrackerState { get; set; }
public int Index { get; set; }
public int Value { get; set; }
}
public class DigitalState
{
public int TrackerStateId { get; set; }
public virtual TrackerState TrackerState { get; set; }
public int Index { get; set; }
public bool Value { get; set; }
}
The AnalogState and DigitalState classes use the TrackerStateId and their Index as a composite primary key.
The tables are currently very small:
TrackerStates: 2719
AnalogStates: 0
DigitalStates: 32604
When I insert into the tables manually, through SQL management studio, the operation runs in a fraction of a second. When I insert through Entity Framework, using the following code, it can take up to 15 seconds, and the amount of time taken is very dependent on the number of digital values included in the tracker state - e.g. a tracker state with 0 digital values takes between 0.1 to 0.5 seconds, and a tracker state with 64 digital values takes between 10 and 15 seconds.
public async Task<int> AddAsync(TrackerState trackerState)
{
using (var context = ContextFactory.CreateContext())
{
context.TrackerStates.Add(trackerState);
await context.SaveChangesAsync();
return trackerState.Id;
}
}
Based on this, it seems like Entity Framework is doing something very slow in the background, but I can't figure out why. 0.5 seconds is pretty slow for a transaction, considering how often this is going to be done. 15 seconds is just too damn slow. Things I have tried so far, to no success:
Disabling change tracking. I didn't expect this to do much, as I am using a separate context for each transaction anyway.
Inserting the tracker state first, then the digital states in a separate step. Entity Framework is probably doing this internally anyway.
Update 1
I'm using EntityFramework 6.1.3. I couldn't figure out how to view the SQL being executed, but I updated the repository's store method to use SQL instead of EF:
context.Database.ExecuteSqlCommand("INSERT INTO DigitalStates ([TrackerStateId], [Index], [Value]) VALUES (#Id, #Index, #Value)",
new SqlParameter("Id", entity.Id),
new SqlParameter("Index", digital.Index),
new SqlParameter("Value", digital.Value));
This part alone is accounting for the majority of the time. It takes 3 seconds to insert 7 entries.
Saving all the digital states in one transaction made a huge difference:
if (trackerState.DigitalStates.Count > 0)
{
var query = "INSERT INTO DigitalStates ([TrackerStateId], [Index], [Value]) VALUES "
+ string.Join(",", trackerState.DigitalStates.Select(state => String.Format("({0}, {1}, {2})", entity.Id, state.Index, state.Value ? 1 : 0)));
context.Database.ExecuteSqlCommand(query);
}
For some reason, letting Entity Framework add the collection automatically seemed to be making a request to the database for each digital state that was added, although I was under the impression that it should have been one transaction, triggered by the context's SaveChanges() method. This fix has changed it from linear time to approximately constant time, relative to the size of the collection. Now my next question is, why?

Designing tables to avoid circular reference

Working in one project (Catering theme ) when I was designing the database I didn't take care about some thing , and now Is very hard to avoid some kine of errors(Circular error).
Suppose I have following scenario :
I have Meal object that should be composed from a list of semi-finished products (we will call it Product ) and list of simple Resources.
One Product is composed from a list of Resoruces and list of products.
So in real example this will look like this:
Meal: Pizza that contains list of Resoruces(cheese,dough) and list of Products : in our case will be just :Sauce.
Sauce will be composed from List of Resources(salt,Some cheese ,tomato Sauce) and a List of Products (in our case will be just one "Chopped tomatoes with salt")
So now I have following classes:
public class Resource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductToProduct
{
public int Id { get; set; }
public Product MainProduct { get; set; }
public Product Component { get; set; }
public double Quantity { get; set; }
}
public class ProductToResource
{
public int Id { get; set; }
public Product Product { get; set; }
public Resource Resource { get; set; }
public double Quantityt { get; set; }
}
public class Meal
{
public int Id { get; set; }
public string Name { get; set; }
public IList<MealToProduct> MealToProducts { get; set; }
public IList<MealToResource> MealToResources { get; set; }
}
public class MealToResource
{
public int Id { get; set; }
public Meal Meal { get; set; }
public Resource Resource { get; set; }
public double Quantity { get; set; }
}
public class MealToProduct
{
public Meal Meal { get; set; }
public Product Product { get; set; }
public double Quantity { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ProductToResource> ProdcutToResources { get; set; }
public IList<ProductToResource> ProductToProducts { get; set; }
}
My problem is in relation between Product to Product.
Suppose I will have Product1, Product2 , Product3 , Product4.
Product 1 will be composed from something and Prodcut2, Product4.
Product2 will be composed from something and Prodcut3.
Prodcut 3 will be composed from something and Prodcut4.
Prodcut 4 will be composed from something and Prodcut1 , in this case when I will try to calcualte Cost for Product1 , or Product 4 I will get an Circular error.
So my problem is in ProductToProduct table.
My question is how I must to design tables to avoid this kind of errors .
I AM VERY SORRY FOR MY EXPLICATION BUT IT IS VERY HARD TO EXPLAIN THIS PROBLEM.
PLEASE ASK ME IF SOMETHING IS UNCLEAR.
THANKS FOR YOUR ATTENTION.
Note:This is not so important for this case but I am working in ASP.Net mvc , orm is Fluent Nhibernate.
Here's an example of a function you could use to detect whether a parent-child relationship exists. I have assumed that the product relationships are described in a table called ProductLink, which has two foreign keys to Product: ParentProductId and ChildProductId.
This function uses a recursive query to determine the complete list of products which are children of the product denoted by the argument #ParentProductId, then does a simple test to see whether #ChildProductId appears in that list.
create function dbo.ProductRelationshipExists
(
#ParentProductId int,
#ChildProductId int
)
returns bit
as
begin
declare #ChildExists bit = 0;
with ProductChildCTE as
(
-- Base case: Get the parent's direct children.
select ChildProductId from ProductLink where ParentProductId = #ParentProductId
-- Recursive case: Get the children's children.
union all
select
ProductLink.ChildProductId
from
ProductChildCTE
inner join ProductLink on ProductChildCTE.ChildProductId = ProductLink.ParentProductId
)
select #ChildExists = 1 from ProductChildCTE where ChildProductId = #ChildProductId;
return #ChildExists;
end
When someone tries to insert a record into ProductLink, you could use a test like this to determine whether the proposed parent and child are already in the table as child and parent, respectively, and disallow the insertion if so.
This was just a quick write-up to illustrate one possible approach; I should mention that I don't know how well the performance of this thing will scale as the table gets larger. Hopefully it will suffice for your case. If not, let me know how to improve it.

Limit one-to-many with Entity Framework

I have classes:
public class Game
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[InverseProperty("Game")]
public virtual ICollection<GameMember> Members { get; set; }
//...
}
public class GameMember
{
[Key]
[Column(Order = 0)]
public Guid GameID { get; set; }
[InverseProperty("Members")]
[ForeignKey("GameID")]
public virtual Game Game { get; set; }
[Key]
[Column(Order = 1)]
public Guid UserID { get; set; }
public virtual User User { get; set; }
//...
}
As you can see, I am trying to make a simple online game. Users can join the game (in that case a new GameMember will be created) and get to the lobby. When everybody is ready, the game starts. I am wondering if there is simple way to limit the number of members. It would be great if I can just apply some attribute like [Max(4)] to ICollection<GameMember> Members.
No, that's not possible - there is no equivalent construct in SQL.
You will need to create four properties, one per allowed member.

Categories