C# Entity Framework and Business Logic - c#

I have a database and I'm accessing it via EF.
public partial class Project
{
public int ProjectID { get; set; }
public string Name { get; set; }
public virtual ICollection<ProjectAssets> ProjectAssets { get; set; }
}
public partial class ProjectAssets
{
public int MappingID { get; set; }
public int ProjectID { get; set; }
public int AssetID { get; set; }
public virtual Project Project { get; set; }
public virtual Asset Asset { get; set; }
}
public partial class Asset
{
public int AssetID { get; set; }
public string Name { get; set; }
public short Type { get; set; }
public virtual ICollection<ProjectAssets> ProjectAssets { get; set; }
}
So, my program have only 1 active Project in the time.
I want to be able to bind to Project and display as a tree or some other way all Assets and I want to be able to create new Asset or add existing Asset what belongs to other project.
If I will use the generated entities I would not be able to manage that all so I need some rules. I've already found good impl. of repositories, but still don't know how to create rules.
Do I need to create something like:
public class WorkProject : Project
{
public WorkProject(Project projject){...}
WorkAsset CreateAsset(){...}
void AddAsset(Asset asset){...}
}

As far as I understand your question, you want to control the graph of entities which are related to a project object. You can leave the repository layer intact and put another layer (business layer) on top of it to enforce the rules.

Related

Using Data annotations to create navigation property from class to itself?

Using Entity Framework Code first I have a class that holds data for a drop-down list. The same class holds records that are sub-items for the items in the main list. Ultimately this will create a cascading set of drop-down lists.
I am trying to figure out how to make the navigation property for the class link back to itself. The issue class is the one that I am using to populate the drop-down list. The Complaint class also has a link to the Issues class but does not need a link back to the subcategory.
public class Issue
{
public Issue()
{
Complaints = new List<Complaint>();
SubIssues = new List<Issue>();
}
[Key]
public int IssueID { get; set; }
public string Name { get; set; }
public bool IsSubCategory { get; set; }
[ForeignKey("IssueID")]
public ICollection<Issue> SubIssues { get; set; }
public virtual ICollection<Complaint> Complaints { get; set; }
}
public class Complaint
{
public Complaint()
{
}
public int ComplaintID { get; set; }
public string Name {get; set;}
[ForeignKey("IssueID")]
public virtual Issue Issue { get; set; }
}
I did something similar, but actually did only have a parent reference in the children. Either way this should work.
public class Folder
{
[Key]
public int Id { get; set; }
// Some Property
public string Name { get; set; }
// They foreignkey for Many-side
public virtual Folder Parent { get; set; }
// The list for One-side (Not tested in my application)
public virtual ICollection<Folder> SubFolders { get; set; }
}
It is same as a regular one-to-many relation, just all the references are within same entity.

Mapping many to one table relation to view

I am trying to map a many to one relationship. I feel I'm close but not quite there.
I have this sensor table:
SensorId
FK_LocationId
Name
etc...
Which holds many data records in the Data Table.
DataId
FK_SensorId
Time
Value
And I am trying to create a Model for this.
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
It should actually be the other way around, at least in my mind. Where the sensor would hold a List<Data> but there is no FK to link that in Sensor. A Data record is just mapped to a sensor by the FK of that sensor.
The problem I'm facing here is that I would have this line in my view now:
#model IEnumerable<DataVisualization.Models.Data>
And instead of looping over my sensors to display the information and then show the data (eventually a chart). I have to loop all the data, somehow organize it how I want and then display it. So I would still need:
#model IEnumerable<DataVisualization.Models.DataSensor>
But this does not give me access to the data since that is in Data and DataSensor does not expose any of that afaik. So I thought about somekind of class that maps them together:
public class DataViewModel
{
public DataSensor dataSensor { get; set; }
public List<GatheredData> gatheredData { get; set; }
}
And my view would require:
#model IEnumerable<DataVisualization.Models.DataViewModel>
This seemed an elegant way but I was not able to make it work. Probably since this would require public DbSet<DataViewModel> dataViewModel { get; set; } in the DbContext and that would produce a awkward table in my database.
So any help on how to create the Model, work with it in the Controller and displaying it in the View would be greatly appreciated.
Edit
What about this Model so I have access to the data connected to this?
public class DataSensor
{
[Key]
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
However, this creates a column DataSensor_Id in the database table. This obviously is undesirable since then it would be a one to one.
I am going to leave my other answer below because it still provides valuable information.
This is how you can accomplish just pulling the data from the database for display. You are using the database first approach. I would recommend creating a new project to test this and get accustomed to what is taking place. Now you are going to want to go to tools in the ribbon and select connect to database. Enter the relevant information to connect to the database. Now create new project, if you haven't already. For the purpose of learning create an Asp.Net5 web application with no authentication. Now, go to your NuGet Package Manager Console. Run " Install-Package EntityFramework.MicrosoftSqlServer -Pre ". Once that is completed Run " Install-Package EntityFramework.Commands –Pre " and " Install-Package EntityFramework.MicrosoftSqlServer.Design –Pre ". Once those are installed go to your Project.Json file and in the commands section add "ef": "EntityFramework.Commands" . Now, go to command prompt and cd to your projects directory. The easiest way I have found to do this is to right click your project and open folder in file explorer. Once you do that, go up one level so all you see is one folder. If you see all of the contents of your project then that is NOT the right place. Shift+RightClick on the folder and you should see the option to Open Command Window Here. Click that and once Command Prompt has opened in your project directory. Run
dnvm use 1.0.0-rc1-update1
or I have found that not to work in some cases. If that doesn't work then Run
dnvm use 1.0.0-rc1-final
If neither of these work you need to install 1.0.0-rc1. Once one of those work, Run
dnx ef dbcontext scaffold
"Server=EnterYourConnectionStringHere;Database=YourDataBaseNameHere;Trusted_Connection=True;"
EntityFramework.MicrosoftSqlServer --outputDir Models
Once that is complete you should have your models that were created from the database in the Models Directory. Go to your newly created Context Class in the Models Directory. Once in there you will see an override void OnConfiguring method. Delete that. Open your Startup.cs and put these using statements at the top.
using YourProject.Models;
using Microsoft.Data.Entity;
Now in your ConfigureServices Method in Startup.cs add
var connection =#"Server=YourConnectionString;Database=YourDatabaseName;Trusted_Connection=True;";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<YourContextName>(options => options.UseSqlServer(connection));
Then the rest is just creating controllers and views for your newly registered context.
Old Answer Below
Try using the fluent API to specify the relationship. Like this: (Hint: this is in your ApplicationDbContext.)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<DataSensor>()
.HasMany(p => p.Data)
.WithOne();
}
public DbSet<DataSensor> DataSensor { get; set; }
public DbSet<GatheredData> GatheredData { get; set; }
And your classes like this:
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
You can also do this by convention like this
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
public virtual DataSensor datasensor { get; set; }
}
or by data annotations like this
public class DataSensor
{
public int Id { get; set; }
public int DataNodeId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool active;
public bool alarm;
public virtual ICollection<DataSensor> Data { get; set; }
}
public class GatheredData
{
public int Id { get; set; }
public int DataSensorId { get; set; }
public DateTime Time { get; set; }
public float value { get; set; }
[ForeignKey("DataSensorId")]
public virtual DataSensor datasensor { get; set; }
}
If you do this by convention and it doesn't work, data annotations could help map them. If all else fails then the fluent API will override everything and map the relationship.
Now, for how to display this data. If you are just trying to display the data and not edit it then I think creating a partial view will be your best bet. In your shared folder create a blank view. Add your model to the new view.
#model IEnumerable<DataVisualization.Models.GatheredData>
Then do a foreach loop to iterate through that data.
#foreach (var item in Model)
{
<p>#Html.DisplayFor(modelItem => item.Id)</p>
<p>#Html.DisplayFor(modelItem => item.Time)</p>
<p>#Html.DisplayFor(modelItem => item.Value)</p>
}
Then back in your main View for the Datasensor put the following where you want the data.
#Html.Partial("StringNameOfThePartialView", Model.Data)
The first overload is the name of the partial view, and the second is the model data to be passed to that view.

How to pull in data from another table in repository or in automapper?

I am essentially am trying to find a clean way to pull in data from another table. Below is a simplified version of my model. My goal is to put the platform name in the userplatform. I would like the cleanest way to do this so I assume with automapper or directly in my repository.
When I try to put a virtual reference to Platform in User Platform my code gets an error that we have a loop of cascading deletes.
Any ideas on how to resolve this problem?
public class User
{
public int UserID { get; set; }
public virtual ICollection<UserPlatform> UserPlatform { get; set; }
}
public class UserPlatform
{
public int UserPlatformID { get; set; }
public String PlatformName { get; set; }
public int UserID { get; set; }
}
public class Platform
{
public int PlatformID { get; set; }
public string Name { get; set; }
}
Alternatives
Db: Denormalize your data so that the information is stored in your user table.
Repository: Do a join inside
Repository: Do two different queries and manually build the User object.

Three models in to one maintenance form

I want to build the one maintenance form for the following three models -
namespace mysite.Models
{
public class LevelOne
{
public int LevelOneId { get; set; }
public string Name { get; set; }
public virtual ICollection<LevelTwo> LevelTwos { get; set; }
}
}
namespace mysite.Models
{
public class LevelTwo
{
public int LevelTwoId { get; set; }
public string Name { get; set; }
public int LevelOneId { get; set; }
public virtual LevelOne LevelOne { get; set; }
public virtual ICollection<LevelThree> LevelThrees { get; set; }
}
}
namespace mysite.Models
{
public class LevelThree
{
public int LevelThreeId { get; set; }
public string Name { get; set; }
public int LevelTwoId { get; set; }
public virtual LevelTwo LevelTwo { get; set; }
}
}
The relationship is :-
LevelOne can have multiple LevelTwo's.
LevelTwo's will have a LevelOne and multiple LevelThree's.
LevelThree will have a LevelTwo.
I have cascading ddl's to select based on the relationship above.
What would be the best way to implement a maintenance form so i can add/edit/delete a level based on what is chosen in the ddl's?
Is it a case of a lot of divs being hidden and shown in javascript based on the ddl's or can something simpler be done using viewmodels or some fancy nancy .net extensions etc?
My mind hurts thinking about it :(
This could also be seen as just 1 model with 3 entities.
Level1 -> Level2 is a one to many relationship. Level2 -> Level3 is a
one to many relationship.
EF supports this. And as i clearly see from ur namespace for all
the 3 models is the same mysite.Models, i guess u already have all of
them in the same model file for EF to create the relationships.

Code first entities don't relate in database

I'm new to Code First, and try to build my data model.
All is ok, except one issue, i try to map next things:
public class Something
{
...
public virtual Layer Layer1 { get; set; }
public virtual Layer Layer2 { get; set; }
public virtual Layer Layer3 { get; set; }
...
}
public class Layer
{
...
public virtual Something Something { get; set; }
...
}
The Something class maps ok, but relation backwards from Layer to Something don't maps at all, in database table there are null, i try almost everything, for now i don't have any idea...
Why Layer can't refer to Something?
Thanks in advance.
You have three FK's, so you need three properties of type Layer in class Something, and also three properties of type Something in class Layer. You cannot somehow gather all related Something's in the Layer class in a single property.
Since you have multiple FK's between two tables, you need to specify the id's, and connect the id's with the navigation properties using data annotations.
I'm assuming 'normal' FK's, so that would mean that a Layer record could be referenced by multiple Something's. If that is the case, than the Something-properties in the Layer class would need to be collection.
Together, this results in:
public class Something {
public int SomethingId { get; set; }
[ForeignKey("Layer1")]
public int Layer1Id { get; set; }
[ForeignKey("Layer2")]
public int Layer2Id { get; set; }
[ForeignKey("Layer3")]
public int Layer3Id { get; set; }
[ForeignKey("Layer1Id")]
public Layer Layer1 { get; set; }
[ForeignKey("Layer2Id")]
public Layer Layer2 { get; set; }
[ForeignKey("Layer3Id")]
public Layer Layer3 { get; set; }
}
public class Layer {
public int LayerId { get; set; }
[InverseProperty("Layer1")]
public Something Something1 { get; set; }
[InverseProperty("Layer2")]
public Something Something2 { get; set; }
[InverseProperty("Layer3")]
public Something Something3 { get; set; }
}

Categories