How to associate class property with instance property - c#

I have some models for Person, TaskListitem and Task as shown below.
I am currently using BindingList to bind to datagrid to show task in datagrid. I am using it because I have to show DeveloperName and ReviewerName in the grid.
Background: this is a code from Task Tracking Program (learning project). Client is written in C#.
Server is written in Ruby. Server stores data in activerecord tables for People, Tasks.
Client also locally caches the same in sqlite DB.
Currently Globals.People is defined as
public static class Globals
{
...
public static List<Person> People = new List<Person>();
...
}
and the classes Person, TaskListitem and Task as shown below:
public class Person : TimeStamp, IEquatable<Person>
{
public string name { get; set; }
public string trigram { get; set; }
public bool active { get; set; }
public eLevel level { get; set; }
public string internal_object_id { get; set; }
public string token { get; set; }
public int unread_objects_count { get; set; }
public int documents_count { get; set; }
public int work_tasks_count { get; set; }
public int review_tasks_count { get; set; }
public bool Equals(Person obj)
{
...
}
public override bool Equals(object obj)
{
...
}
public override int GetHashCode()
{
...
}
}
public class TaskListItem : Task
{
public string taskName
{
get { return this.name + " - " + this.title; }
}
public string DeveloperName
{
get { return Globals.People.Find(x => x.id == this.developer_id).name; }
}
public string ReviewerName
{
get { return Globals.People.Find(x => x.id == this.reviewer_id).name; }
}
}
public class Task : TimeStamp
{
public Task()
{
...
if (Globals.People.Count == 0) Globals.People.Add(new Person { id = 0, name = "DummyUser", level = eLevel.Master });
developer_id = Globals.People.First().id;
reviewer_id = Globals.People.First().id;
...
}
public TaskListItem AsTaskListItem()
{
...
}
public int developer_id { get; set; }
public int reviewer_id { get; set; }
public string name { get; set; }
public string title { get; set; }
...
}
But then as I was learning I found that Static object must not be used to preserve state. So I started searching for an alternative to my implementation. I was able to solve it partially by returning DeveloperName and ReviewerName along with task. So I don't have to use to Static List.
But now I am facing a problem that when I retrieve Tasks from Cache. I am unable to merge it with retrieved data of People.
How Do I Merge it. My above partial solution requires me to retrieve redundant data along with each Task (i.e. Person names with each Task as client has already retrieved all people data).
Complete Code
Please comment for suggestions and improvements.

Related

How to access a property in another class?

I am working with a WPF .Net Core 3 project.
In my UnbalancedViewModel I need to access an ID from another class (TestRunDto.cs).
UnbalancedViewModel
public class UnbalancedViewModel : ViewModelBase, IUnbalancedViewModel
{
private TestRunApi _testRunApi;
public UnbalancedViewModel(TestRunApi testRunApi, INotificationManager notifications)
{
_testRunApi = testRunApi;
}
private void StartTestRunJobExecuted(object obj)
{
_testRunApi.StartTestRun(1); ////I need the Id from TestRunDto (TestRunDto.Id)
}
}
TestRunApi
public async Task<TestRunLiveValueDto> GetTestRunLiveValue(int jobRunId)
{
await using var dbContext = new AldebaDbContext(_connectionString);
return await TestRunInteractor.GetTestRunLiveValue(jobRunId, dbContext);
}
public async Task StartTestRun(int testRunId)
{
await using var dbContext = new AldebaDbContext(_connectionString);
await TestRunInteractor.StartTestRun(dbContext, testRunId);
}
TestRunLiveValueDto
public class TestRunLiveValueDto
{
public TestRunDto TestRun { get; }
public bool ShowInstantaneousValue { get; set; }
public bool EnableStart { get; set; }
public bool EnableStop { get; set; }
public bool EnableMeasure { get; set; }
public int RecipeRpm { get; }
public string ActualRecipeName { get; }
public int DefaultSetOfPlaneId { get; }
public ICollection<BalancePlaneDto> ListBalancePlane { get; }
public ICollection<SetOfPlaneDto> ListSetOfPlane { get; }
public ICollection<SensorVibrationDto> SensorVibrations { get; set; }
public ICollection<EstimationDto> InstantaneousValues { get; set; }
public ICollection<EstimationDto> EstimationsValues { get; set; }
private TestRunLiveValueDto(TestRunDto testRun, bool enableStart, bool enableStop, int recipeRpm, ICollection<SensorVibrationDto> sensorVibrations)
{
EnableStart = enableStart;
EnableStop = enableStop;
TestRun = testRun;
RecipeRpm = recipeRpm;
SensorVibrations = sensorVibrations;
}
public static TestRunLiveValueDto Create(TestRunDto testRun, bool enableStart, bool enableStop, int recipeRpm, ICollection<SensorVibrationDto> sensorVibrations)
=> new TestRunLiveValueDto(testRun, enableStart, enableStop, recipeRpm, sensorVibrations);
}
TestRunDto
public class TestRunDto
{
public int Id { get; set; }
public int JobRunId { get; set; }
public string Name { get; set; }
public int TestRunNumber { get; set; }
public RunState State { get; set; }
public ICollection<BalancePlaneDto> BalancePlanes { get; set; } // Todo remove
private TestRunDto(int id, int jobRunId, RunState state, string name, int testRunNumber)
{
Id = id;
JobRunId = jobRunId;
Name = name;
TestRunNumber = testRunNumber;
State = state;
}
public static TestRunDto Create(int id, int jobRunId, RunState state, string name, int testRunNumber)
=> new TestRunDto(id, jobRunId, state, name, testRunNumber);
}
I have been trying to understand this, but I can not get a hold of the proper method to do this. Do I first declare a new TestRunDto class in my viewmodel or am I supposed to access it some other way?
You need to ensure class A has a reference to an instance of class B to access the properties, for example one way of doing this is to pass class A to B in a method where you can manipulate or access properties.
public class FooA
{
public string PropertyA { get; set; }
}
public class FooB
{
public string PropertyB { get; set; }
public void CanAccessFooA(FooA a)
{
a.PropertyA = "See, I can access this here";
}
}
Another is to pass class A to B in the constructor (known as dependency-injection)
public class FooB
{
FooA _a;
public FooB(FooA a)
{
// Pass instance of FooA to constructor
// (inject dependency) and store as a member variable
this._a = a;
}
public string PropertB { get; set; }
public void CanAccessFooA()
{
if (this._a != null)
this._a.PropertyA = "See, I can access this here";
}
}
Exactly how to structure your code is up to you, but the principle remains the same: Class B can only access Class A if it has a reference to an instance of it.
Look into 'Dependency Injection' as there are many techniques to achieve this.
Edit
One such technique might be abstracting the code to provide the ID to both, like so
public class IdProvider
{
public int Id { get; set; }
}
public class FooA
{
private int _id;
public FooA(IdProvider idProvider)
{
_id = idProvider.Id;
}
}
public class FooB
{
private int _id;
public FooB(IdProvider idProvider)
{
_id = idProvider.Id;
}
}
Now both classes have the same ID;
StartTestRun takes the tesRunId as it's parameter.
public async Task StartTestRun(int testRunId)
{
I think you need to call StartTestRunJobExecuted with this testRunId.
You will to change
private void StartTestRunJobExecuted(object obj)
to
private void StartTestRunJobExecuted(int testRunIdn)
{
_testRunApi.StartTestRun(testRunId); ////I need the Id from TestRunDto (TestRunDto.Id)
}
(This based on me guessing).

An Entity Object Cannot be Referenced by multiple instances

I am trying to insert a room into my database from a textbox input (int), a combobox that shows all the buildings from the database and a combobox that shows all the floors from the database, I am encountering this error:
"An entity object cannot be referenced by multiple instances of IEntityChangeTracker."
and not quite sure how to solve this.
FormAddRoom.cs:
http://pastebin.com/Cst5bBbD
Data Models:
public class Building
{
public int BuildingID { get; set; }
public string Name { get; set; }
public Hotel Hotel { get; set; }
public virtual ICollection<Floor> Floors { get; set; }
}
public class Floor
{
public int FloorID { get; set; }
public string Name { get; set; }
public virtual Building Building { get; set; }
public virtual ICollection<Room> Rooms { get; set; }
}
public class RoomType
{
public int RoomTypeID { get; set; }
public string Name { get; set; }
}
Databasehandler methods:
public static List<Building> GetAllBuildingsByHotel(string hotel)
{
HotelDbContext context = new HotelDbContext();
var buildings = context.Buildings.Where(b => b.Hotel.Name == hotel).ToList();
return buildings;
}
public static List<Floor> GetAllFloorsFromBuilding(int buildingid)
{
HotelDbContext context = new HotelDbContext();
var floors = context.Floors.Where(f => f.Building.BuildingID == buildingid).ToList();
return floors;
}
public static int InsertRoom(Room room)
{
HotelDbContext context = new HotelDbContext();
context.Rooms.Add(room);
context.SaveChanges();
return room.RoomID;
}
If you're using tracked entities (the default EF settings), you can't get an object from one instance of a context and put it in another. You need to do all your work against a single context instance.

Readonly nested object properties

I'm having a problem defining these 2 classes:
public class Article
{
public Article(long ID, string Name, ArticleFamily Family)
{
//...Initializer...
}
public ArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
I have a collection of Article and each one belongs to a family.
Now, given that I have a certain ArticleFamily object I should be able to change its Description and it gets eventually persisted to a DataBase. (I left out that part for simplicity)
But I should not be able to do this:
Article art = SomeMethodReturningArticle();
art.Family.Description = "SomeOtherValue";
I should be able to change the Family of an Article entirely, replacing it with a new ArticleFamily object, but I shouldn't be able to change just the description.
Should I create a copy of the ArticleFamily class with readonly properties like this:
public class ArticleFamilyReadonly
{
ArticleFamily _family;
public ArticleFamilyReadonly(ArticleFamily Family)
{
_family = Family;
}
public int ID { get { return _family.ID; } }
//etc...
}
How can I do this in a clean way?
Here's what I threw together in LinqPad:
void Main()
{
var art = new Article(1,"2", new ArticleFamily(1, "Test"));
art.Family.Description = "What?"; // Won't work
var fam = art.Family as ArticleFamily;
fam.Description = "This works"; // This works...
}
public class Article
{
public Article(long ID, string Name, IArticleFamily Family)
{
//...Initializer...
}
public IArticleFamily Family { get; set; }
//Other props...
}
public class ArticleFamily : IArticleFamily
{
public ArticleFamily(int ID, string Description)
{
//...Initializer...
}
public int ID { get; private set; }
public string Description { get; set; }
}
public interface IArticleFamily
{
int ID { get; }
string Description { get;}
}
Cannot edit directly from the Article object unless cast to ArticleFamily object.

Entity Framework objects being duplicated in the database

I am working with Entity Framework (code first) for the first time and I have a little problem.
I have a class called Taxi & one called Driver
Taxi has a reference to the Driver, you can see both classed below
public partial class Taxi
{
public Taxi()
{
}
public int TaxiId { get; set; }
public Driver Driver { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Color Colour { get; set; }
public string NumPlate { get; set; }
public int MaxPassengers { get; set; }
}
public partial class Driver
{
public Driver()
{
}
public int DriverId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string HomePhone { get; set; }
public string MobilePhone { get; set; }
public DateTime JoinedFirm { get; set; }
}
And I am saving the changed Taxi like this:
using (var db = new DataModel())
{
db.Configuration.ProxyCreationEnabled = false;
db.Taxis
.Where(x => x.TaxiId == CurrenltySelectedTaxi.TaxiId)
.ToList()
.ForEach(x =>
{
x.Make = CurrenltySelectedTaxi.Make;
x.Model = CurrenltySelectedTaxi.Model;
x.NumPlate = CurrenltySelectedTaxi.NumPlate;
x.Colour = CurrenltySelectedTaxi.Colour;
x.MaxPassengers = CurrenltySelectedTaxi.MaxPassengers;
x.Driver = CurrenltySelectedTaxi.Driver;
});
db.SaveChanges();
}
My problem is that the Driver gets duplicated the in database every time I save a taxi.
You can see the database here:
Can someone point me in the right direction,
Thanks
EDIT:
The drivers are in a combo box and are selected like this
private void cmbTaxiDriver_SelectedIndexChanged(object sender, EventArgs e)
{
using (var db = new DataModel())
{
db.Configuration.ProxyCreationEnabled = false;
Driver listSelected = (Driver) cmbTaxiDriver.SelectedItem;
CurrenltySelectedTaxi.Driver = db.Drivers.Where(x => x.DriverId == listSelected.DriverId).ToArray()[0];
}
}
And the combo box is populated like this:
listDrivers.Items.AddRange(db.Drivers.ToArray());
The only thing that I can possibly find based on the content you have provided is that CurrenltySelectedTaxi.Driver is a detached or new driver. Be careful to ensure that the object pointed to by CurrenltySelectedTaxi.Driver is an attached driver record.
An option you can do to help determine the status of that record is to put a breakpoint on that line and then look at the DbEntry record status.
Your design doesn't create a relationship between Taxi and Driver that's going to translate to a database. Try something like this:
public partial class Taxi
{
public Taxi()
{
}
public int TaxiId { get; set; }
public int DriverId {get; set; }
public virtual Driver Driver { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public Color Colour { get; set; }
public string NumPlate { get; set; }
public int MaxPassengers { get; set; }
}
You may also want to add the following to the Driver object:
public virtual IEnumerable<Taxi> Taxis { get; set; }
So it turns out my issue is because i followed the tutorial on MSDN that says to recreate your context EVERYTIME you use it. Although this 'may' be good practice, in this instance it caused the problem.
If I make one context for the entire class it works as expected/

Updating List<T> in DbContext

I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.

Categories

Resources