I have tens of different views and on some, the data that comes from database, must be manipulated before shown to user.
for example:
var students = _repository.Students.Select(c => { c.StartDate = FixDateToCurrentYear(c.StartDate ); c.EndDate = FixDateToCurrentYear(c.EndDate); return c; }).ToList();
So basically I now have a variable, where Students StartDate and EndDate have been fixed by some logic, which is not relevant at the moment.
The problem here is that, whenever somewhere a SaveChanges() function is called, then the changed dates for students are also persisted to database.
I only want to use the changed dates for the logic in specific view. How could I handle this?
I have heard about .AsNoTracking(); but this must be put directly to DbSet item, but I want to handle it on controller side, is it possible?
Pull the students from the db first and the do a select to an anonymous type. EF won't know how to track changes on that.
var students =
_repository.Students.ToList()
.Select(s =>
new { StartDate = FixDateToCurrentYear(c.StartDate),
EndDate = FixDateToCurrentYear(c.EndDate),
Student = s });
The quickest thing I can think of is to perform your fix at the point you display it to the user, e.g. in Razor:
#FixDateToCurrentYear(student.StartDate)
In this way, you're not actually changing the value, you're just changing how it's displayed.
If you need to do more than display, you will need to create a partial buddy class, add a new property and modify its get method:
public partial class Student
{
DateTime StartDateComputed {
get
{
return FixDateToCurrentYear(StartDate);
}
set;
}
}
Now, use StartDateComputed instead of StartDate and the same for EndDate.
Related
I have matter on adding row to DataGridView in C#, I tried to add an String[] as DataGridView.Rows.Add argument but, always the same issue, now this is my final code, and It doesn't work again, always NullReferenceException:
{
ConnectDB con = new ConnectDB();
CrudDB db = new CrudDB();
try
{
DispoProf disp = new DispoProf(res.ID);
con.Connexion.Open();
List<DispoProf> liste = db.Find("dispoprof", disp, "", con.Connexion);
for (int i = 0; i < liste.Count; i += 1)
{
//string[] ligne = { liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null };
dataGridViewListerDV.Rows.Add(liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null);
}
}
catch(Exception ex)
{
Console.WriteLine("Exception :: {0} :: {1} :: {2}",ex.Message, ex.Source , ex.StackTrace);
}
finally
{
con.Connexion.Close();
}
}
And It throw a NullReferenceException at
dataGridViewListerDV.Rows.Add(liste[i].date, liste[i].heureDebut, liste[i].heureFin, null, null);
Are you sure that every liste[i] is not null, and that every starting hour and every finish hour of every liste[i] is not null.? What do you want to display if any of these values is null?
Alas you forgot to tell us the return value of db.Find(...), but I am pretty sure that either liste[i] equals null, or properties heureDebut et heureFin are nullable Datetime
If you've defined your cells such that you can display nullable Datetimes, consider to change your code:
var itemToDisplay = liste[i];
if (itemToDisplay != null)
{
dataGridViewListerDV.Rows.Add(itemToDisplay.date,
itemToDisplay.heureDebut, itemToDisplay.heureFin, null, null);
}
else
{
// decide what to do if item equals null
}
Also, if HeureDebut / HeureFine may be null, consider to change your DataGridViewColumns such that they can show nullable DateTime instead of DateTime.
There is room for improvement
First time users of DataGridViews tend to tinker directly with the Rows and Cells in the DataGridView. By doing this, you intertwine data (your model) with the way this data is displayed (your view).
For quite some time now, there is a tendency to keep those two separated. If you keep your model separated from your view, you can easily change the view, without having to change your model, for instance, if you want to show your data in a Graph, instead of a table, or if you want to save the data in an XML file, instead of a table, your model doesn't have to change. Similarly, it is much easier to unit test your model, if you don't need a Form to display it.
A third reason to keep the model separated from the view, is that it gives you the freedom to change your DataGridView without having to change your model: you can Add / Remove columns, change the way how DateTimes are displayed, show a different color for negative values: all these changes can be done without having to change your Model.
To keep your Model and your View separated, you need an adapter class in between that converts your model to the way that you want it displayed. This adapter is quite often called the ViewModel.
If you will be using WPF instead of Forms in a few years, you will see that this separation between model and view is almost enforced, by using a different language to describe the view (XAML).
But Forms also supports this separation.
First you need to define the class that will be displayed in one row. Something like this:
class WorkingHours
{
public DateTime Date {get; set;}
public TimeSpan? StartTime {get; set;}
public TimeSpan? EndTime {get; set;}
}
This way, it is certain that StartTime and EndTime are on the same day. If you have night shifts, consider:
class WorkingHours
{
public DateTime? StartTime {get; set;}
public DateTime? EndTime {get; set;}
}
But then you have problems: what Date to show if you haven't got a StartTime. Before dislaying your model, get your model straight, so that your properties are well defined: what values are always available, what values are nullable, can they ever be out-of-range?
Using visual Studio Designer, you probably have defined columns. Your columns have a property DataPropertyName, which tells you what to show:
columnDate.DataPropertyName = nameof(WorkingHours.Date);
columnStartTime.DataPropertyName = nameof(WorkingHours.StartTime);
columnFinishTime.DataPropertyName = nameof(WorkingHours.EndTime);
If your StartTime and EndTime may be null, consider to add how to show null values: Red background? or only a '-', maybe show nothing?
See: because you separate your model and your view, that changing the view does not influence your model!
We need a method to fetch your data. This is your method in your question:
private IEnumerable<WorkingHours> GetWorkingHours(...)
{
using (var dbConnection = new ConnectedDb(...))
{
... // Create DbCommand, ExecuteQuery and use DbReader to fill WorkingHours
}
}
Note: This is the only place that will change if in future you decide to change how you fetch your data, like use entity framework or Dapper, or read the working hours from an XML file? Or change the database layout: again: Model change does not influence your view.
Now that we are able to fetch the displayed data, Displaying is one statement:
this.dataGridView1.DataSource = GetWorkingHours(...).ToList();
Et voila! All fetched data is instantly displayed.
However, this is display only. Changes are not monitored. If you want to know about changes: adding / removing / changing rows, the data should be in an object that implements IBindingList, like BindingList<T>
For this, we need one line of code:
private BindlingList<WorkingHours> DisplayedWorkingHours
{
get => (BindingList<WorkingHours>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
So to display your data:
void InitDisplayedData()
{
this.DisplayedWorkingHours = new BindingList<WorkingHours>(this.GetWorkingHours().ToList());
}
Now every change made by the operator is automatically updated in the bindingList. You don't have to read Rows nor Cells, just wait until the operator indicates he finished editing the data, for instance by clicking a button:
private void OnButtonOk_Clicked(object sender, ...)
{
IReadOnlyCollection<WorkingHours> editedWorkingHours = this.DisplayedWorkingHours;
// Detect which items are added / removed / changed and process the changes:
this.ProcessEditedWorkingHours(editedWorkingHours);
}
Again: did you see, that because I separate the actual data processing from how the data is displayed, all model functionality can be tested without the form. If you ever change how the data is displayed, your model does not have to change, if you ever change your model, the display does not have to change.
If you need to process selected rows, consider to add functionality for this:
private WorkingHours CurrentWorkingHours =>
(WorkingHours)this.dataGridView1.CurrentRow?.DataBoundItem;
private IEnumerable<WorkingHours> SelectedWorkingHours =>
this.dataGridView1.SelectedRows.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<WorkingHours>();
}
Conclusion
By separating your model from your view, it is easier to change either the view or the model, without having to change the other. It is easier to unit test the model, without the view, and if problems occur, you can debug the view without a real database.
The ViewModel adapter between the Model and the View consists usually of a few one-liner methods.
Simple comme bonjour!
For reasons that I do not quite understand, I have chosen not to use an ORM Framework and have gone with a generalized ADO.NET data access layer. I initially created a single database class from which all my controllers had access. As anyone but myself could have predicted, this access object has become a monstrosity.
In an attempt to refactor my data layer, I have created a 'database adapter' class as a DI injected service and have created a 'service layer' to utilize it. So each controller now has a 'domain service' that will use the database adapter to query the database and return a generic data table. The service will then populate the result of the queries and return the domain objects back to the controller where it can assemble the view models.
I am running into an issue where I cannot seem to abstract the code designed to map the DataSets returned from the database access layer because each query may select different fields. For example, a simple reference data service:
public class ReferenceDataService : IReferenceDataService
{
private IDatabaseAdapter _dbAdapter;
public ReferenceDataService(IDatabaseAdapter dbAdapter)
{
_dbAdapter = dbAdapter;
}
public IEnumerable<ReferenceData> GetReferenceData(string table)
{
List<ReferenceData> rdList = new List<ReferenceData>();
StringBuilder sb = new StringBuilder();
sb.Append("SELECT [CODE], [LABEL] FROM [dbo].");
sb.Append(table);
sb.Append(" WHERE END_DATETIME > GETDATE()");
DataSet ds = _dbAdapter.ExecuteDataSet(sb.ToString(), null);
foreach (DataRow row in ds.Tables[0].Rows)
{
rdList.Add(PopulateRecord(row));
}
return rdList;
}
private ReferenceData PopulateRecord(DataRow row)
{
return new ReferenceData
{
ReferenceId = (int)row["REFERENCE_ID"],
Code = (string)row["CODE"],
Label = (string)row["LABEL"],
Description = (string)row["DESCRIPTION"],
BeginDatetime = (DateTime)row["BEGIN_DATETIME"],
EndDatetime = (DateTime)row["END_DATETIME"],
UpdatedBy = (string)row["UPDATED_BY"],
UpdatedOn = (DateTime)row["UPDATED_ON"],
CreatedBy = (string)row["CREATED_BY"],
CreatedOn = (DateTime)row["CREATED_ON"]
};
}
}
In this example, I have an exception thrown from the populate method, because as you can see, I am only selecting code and label for this particular method. I'd like to avoid a custom mapping for every method but I also do not want to needlessly return ALL the data from each table row to the controller. I'd like to keep the populate method generic so that any query against that table will be mapped appropriately.
I realize I'm basically almost rolling my own ORM, but I'd like to use a service pattern without it because at this point I am way too invested.
After some digging around, it appears there was a very obvious and straightforward solution that I had been missing. The DataRow instance object has the ability to check it's parent table columns for existence. By wrapping each assignment from the table row in one of these checks, then the population method will not care what was actually selected into the DataTable and will be able to populate an object regardless of the amount of data returned from the query.
So in my example, if I want to keep a generic population method for ReferenceData but use a query that only retuns the CODE and LABEL columns, the following change would keep the population of the returned business object agnostic and error free:
private ReferenceData PopulateRecord(DataRow row)
{
return new ReferenceData
{
ReferenceId = row.Table.Columns.Contains("REFERENCE_ID") ? (int)row["REFERENCE_ID"] : default(int),
Code = row.Table.Columns.Contains("CODE") ? (string)row["CODE"] : default(string),
Label = row.Table.Columns.Contains("LABEL") ? (string)row["LABEL"] : default(string),
Description = row.Table.Columns.Contains("DESCRIPTION") ? (string)row["DESCRIPTION"] : default(string),
BeginDatetime = row.Table.Columns.Contains("BEGIN_DATETIME") ? (DateTime)row["BEGIN_DATETIME"] : default(DateTime),
EndDatetime = row.Table.Columns.Contains("END_DATETIME") ? (DateTime)row["END_DATETIME"] : default(DateTime),
UpdatedBy = row.Table.Columns.Contains("UPDATED_BY") ? (string)row["UPDATED_BY"] : default(string),
UpdatedOn = row.Table.Columns.Contains("UPDATED_ON") ? (DateTime)row["UPDATED_ON"] : default(DateTime),
CreatedBy = row.Table.Columns.Contains("CREATED_BY") ? (string)row["CREATED_BY"] : default(string),
CreatedOn = row.Table.Columns.Contains("CREATED_ON") ? (DateTime)row["CREATED_ON"] : default(DateTime)
};
}
This would allow me to use PopulateRecord on a select statement that only returned CODE and LABEL (as I would want to do if I was populating a SelectItemList for a dropdown for example).
I do not know what kind of performance hit this may or may not incur so that is something to possibly consider. But this allows for the flexibility I was looking for. I hope this post will help someone else who might be looking for the same type of solution.
If there are better ways to approach this please let me know. Thanks!
I´m having a problem, I retrieve all the Loans I have stored in my database like this:
list_loans = db.Loan.Where(x => x.State.id_state != 6).ToList();
db is the Object context.
Then, I assign that list as the DataSource for my DataGridView.
dgv_Loans.Datasource = list_loans;
With that info, I add some columns. Like for example, installments left to pay. I get that value by counting the result of a query.
The user can order the result using some options. Is easy to order the result from the fields that the entity have (using linq), but I dont know how to order the results using this new columns.
I read some posts here and tried this:
dgv_Loans.Sort(dgv_Loans.Columns["installments_left"], ListSortDirection.Ascending);
By doing this, I´m getting the following exception at runtime:
"DataGridView control must be bound to an IBindingList object to be sorted."
Is there anyway to use linq to orderby created columns in a DataGridViewColumn? Or how can I solve this error?
I know there are related posts, but after reading them, I can´t find a solution to this specific problem. Thats why I showed how I implemented to get some advice..
Rather than binding directly to the list retrieved from database, what I generally do is have a view class and have all the calculated properties in that class
public class LoanView : Loan {
public LoanView(Loan loan){
}
public int InsallmentsLeft { get { return ...; } }
}
and then bind the datasource to a list of this, this keeps sorting working.
Concerning about Sort datagridview by created columns using Entity Framework
I guess you need this Presenting the SortableBindingList<T>
Usage:
loanBindingSource.DataSource = new SortableBindingList<Loan>(list_loans.ToList());
dgv_Loans.Datasource = loanBindingSource;
int ID = Convert.ToInt32(cmbDepartments.SelectedValue);
var EmployeeList = from Employee in db.Employee
where Employee.DepartmentID == ID
select new
{
Employee.FirstName,
Employee.LastName
};
dataGridView1.DataSource = EmployeeList.ToList();
You could directly give the data source to dataGridView1.DataSource but you must write ToList() at the end of your query:
int ID = Convert.ToInt32(cmbDepartmanlar.SelectedValue);
dataGridView1.DataSource = (from Employee in db.Employee
where Employee.DepartmentID == ID
select new
{
Employee.FirstName,
Employee.LastName
}).ToList();
I have a DB like this that I generated from EF:
Now I'd like to add a "fielduserinput" entity so I write the following code:
public bool AddValueToField(string field, string value, string userId)
{
//adds a value to the db
var context = new DBonlyFieldsContainer();
var fieldSet = (from fields in context.fieldSet
where fields.fieldName.Equals(field)
select fields).SingleOrDefault();
var userSet = (from users in context.users
where users.id.Equals(userId)
select users).SingleOrDefault();
var inputField = new fielduserinput { userInput = value, field = fieldSet, user = userSet };
return false;
}
Obviously it's not finished but I think it conveys what I'm doing.
Is this really the right way of doing this? My goal is to add a row to fielduserinput that contains the value and references to user and field. It seems a bit tedious to do it this way. I'm imagining something like:
public bool AddValueToField(string userId, string value, string fieldId)
{
var context = new db();
var newField = { field.fieldId = idField, userInput = value, user.id = userId }
//Add and save changes
}
For older versions of EF, I think you're doing more or less what needs to be done. It's one of the many reasons I didn't feel EF was ready until recently. I'm going to lay out the scenario we have to give you another option.
We use the code first approach in EF 4 CTP. If this change is important enough, read on, wait for other answers (because Flying Speghetti Monster knows I could be wrong) and then decide if you want to upgrade. Keep in mind it's a CTP not an RC, so considerable changes could be coming. But if you're starting to write a new application, I highly recommend reading some about it before getting too far.
With the code first approach, it is possible to create models that contain properties for a reference to another model and a property for the id of the other model (User & UserId). When configured correctly setting a value for either the reference or the id will set the id correctly in the database.
Take the following class ...
public class FieldUserInput{
public int UserId {get;set;}
public int FieldId {get;set;}
public virtual User User {get;set;}
public virtual Field Field {get;set;}
}
... and configuration
public class FieldUserInputConfiguration{
public FieldUserInputConfiguration(){
MapSingleType(fli => new {
userid = fli.UserId,
fieldid = fli.FieldId
};
HasRequired(fli => fli.User).HasConstraint((fli, u)=>fli.UserId == u.Id);
HasRequired(fli => fli.Field).HasConstraint((fli, f)=>fli.FieldId == f.Id);
}
}
You can write the code...
public void CreateField(User user, int fieldId){
var context = new MyContext();
var fieldUserInput = new FieldUserInput{ User = user, FieldId = fieldId };
context.FieldUserInputs.Add(fieldUserInput);
context.SaveChanges();
}
... or vice versa with the properties and everything will work out fine in the database. Here's a great post on full configuration of EF.
Another point to remember is that this level of configuration is not necessary. Code first is possible to use without any configuration at all if you stick to the standards specified in the first set of posts referenced. It doesn't create the prettiest names in the database, but it works.
Not a great answer, but figured I'd share.
with my Repository classes, I use LinqToSql to retrieve the data from the repository (eg. Sql Server 2008, in my example). I place the result data into a POCO object. Works great :)
Now, if my POCO object has a child property, (which is another POCO object or an IList), i'm trying to figure out a way to populate that data. I'm just not too sure how to do this.
Here's some sample code i have. Please note the last property I'm setting. It compiles, but it's not 'right'. It's not the POCO object instance .. and i'm not sure how to code that last line.
public IQueryable<GameFile> GetGameFiles(bool includeUserIdAccess)
{
return (from q in Database.Files
select new Core.GameFile
{
CheckedOn = q.CheckedOn.Value,
FileName = q.FileName,
GameFileId = q.FileId,
GameType = (Core.GameType)q.GameTypeId,
IsActive = q.IsActive,
LastFilePosition = q.LastFilePosition.Value,
UniqueName = q.UniqueName,
UpdatedOn = q.UpdatedOn.Value,
// Now any children....
// NOTE: I wish to create a POCO object
// that has an int UserId _and_ a string Name.
UserAccess = includeUserIdAccess ?
q.FileUserAccesses.Select(x => x.UserId).ToList() : null
});
}
Notes:
Database.Files => The File table.
Database.FilesUserAccess => the FilesUserAccess table .. which users have access to the GameFiles / Files table.
Update
I've now got a suggestion to extract the children results into their respective POCO classes, but this is what the Visual Studio Debugger is saying the class is :-
Why is it a System.Data.Linq.SqlClient.Implementation.ObjectMaterializer<..>
.Convert<Core.GameFile> and not a List<Core.GameFile> containing the POCO's?
Any suggestions what that is / what I've done wrong?
Update 2:
this is what i've done to extract the children data into their respective poco's..
// Now any children....
UserIdAccess = includeUserIdAccess ?
(from x in q.FileUserAccesses
select x.UserId).ToList() : null,
LogEntries = includeUserIdAccess ?
(from x in q.LogEntries
select new Core.LogEntry
{
ClientGuid = x.ClientGuid,
ClientIpAndPort = x.ClientIpAndPort,
// ... snip other properties
Violation = x.Violation
}).ToList() : null
I think that all you need to do is to put another Linq query in here:
q.FileUserAccesses.Select(x => x.UserId).ToList()
i.e. You want to select data from the FileUserAccess records - which I'm assuming are Linq to SQL classes, so to do this you can have something like:
(from fua in q.FileUserAccesses
select new PocoType
{
UserID = fua.UserID,
Name = fua.User.UserName // Not sure at this point where the name comes from
}).ToList()
That should get you pointed in the right direction at least.
What is the type of UserIdAccess? How is it not 'right'? Are you getting the 'wrong' data? if so have you checked your database directly to make sure the 'right' data is there?