Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 4 years ago.
Improve this question
I am working on a c# application where you can select movies from a combobox and it displays the movie info and the actors that were in it in a datagrid, gets all the information from a MySql database. Now I've googled most of what I need to learn and implement it into my code. The blocks of code look massive and were wondering if there was any way to dry it up a bit, such as what variables can I reuse if any, etc.
//film select
var queryfilmsearch = "SELECT title, description, release_year, rental_rate, length, rating FROM film WHERE title LIKE + #value";
MySqlCommand cmdfilmsearch = new MySqlCommand(queryfilmsearch, PubVar.connection);
cmdfilmsearch.Parameters.AddWithValue("#value", comBoxFilm.Text);
MySqlDataAdapter adpfilmserach = new MySqlDataAdapter(cmdfilmsearch);
DataSet dsfilmsearch = new DataSet();
adpfilmserach.Fill(dsfilmsearch);
dataGridView1.ReadOnly = true;
dataGridView1.DataSource = dsfilmsearch.Tables[0];
//actor from film select
var queryfilmactor = "SELECT first_name, last_name FROM actor INNER JOIN film_actor ON actor.actor_id = film_actor.actor_id INNER JOIN film ON film.film_id = film_actor.film_id WHERE film.title LIKE + #value";
MySqlCommand cmdfilmactor = new MySqlCommand(queryfilmactor, PubVar.connection);
cmdfilmactor.Parameters.AddWithValue("#value", comBoxFilm.Text);
MySqlDataAdapter adpfilmactor = new MySqlDataAdapter(cmdfilmactor);
DataSet dsfilmactor = new DataSet();
adpfilmactor.Fill(dsfilmactor);
dataGridView2.ReadOnly = true;
dataGridView2.DataSource = dsfilmactor.Tables[0];
It's basically the same code just different query and variable names
This is a lot of boilerplate, but it doesn't have to be.
First off, you're using your connections wrong. They implement IDisposable. You should take special care in handling them properly. This means that storing them as a variable is usually a bad idea. Open and close them as quickly as possible, and don't share them.
Second, you're using raw ADO.NET. While it's good to have an idea of how ADO.NET works (since it's the building block for most relational database code in .NET), it's better to use an abstraction. You end up writing less boilerplate. More concise, easier to read and refactor code. We often use Object Relational Mappers to accomplish this. There's two styles: Micro, and "full". There are several micro ORM's out there for .NET: Dapper, Npoco, PetaPoco etc. Stack Overflow (the website) uses Dapper. Then there's full ORM's such as Entity Framework and NHibernate.
You're using DataSet and DataTable. Those are poor abstractions. They don't follow Object Oriented Programming principles very well, they're too flexible, and they're inefficient. It's better to create custom classes and then use some form of ORM to map from your code to your custom classes.
You're also doing data access directly in your Web Forms. That's never a good idea. Database access should be done in a separate layer. That makes it easier to swap out either the data layer or the presentation layer, and makes it easier to re-use your data access code throughout other parts of your presentation layer.
Keeping all this in mind, we might end up with something like below. I chose Dapper. And I assumed Web Forms, but I think you'll get the idea no matter what your presentation layer is:
Data Repository
public class MySqlFilmRepository : IFilmRepository
{
readonly string _connectionString { get; set; }
public FilmRepsitory(string connectionString)
{
_connectionString = connectionString;
}
public List<Film> SearchFilmsByTitle(string title)
{
using (var connection = new MySqlConnection(_connectionString))
{
List<Film> films = connection.Query<Film>("SELECT title, description, release_year, rental_rate, length, rating FROM film WHERE title LIKE #Title", new { Title = title }).AsList();
return films;
}
}
public List<Actor> GetActorsForFilm(string filmTitle)
{
using (var connection = new MySqlConnection(_connectionString))
{
List<Actor> actors = connection.Query<Actor>("SELECT first_name, last_name FROM actor INNER JOIN film_actor ON actor.actor_id = film_actor.actor_id INNER JOIN film ON film.film_id = film_actor.film_id WHERE film.title LIKE #FilmTitle", new { FilmTitle = filmTitle }).AsList();
return actors;
}
}
}
public interface IFilmRepository
{
List<Film> SearchFilmsByTitle(string title);
List<Actor> GetActorsForFilm(string filmTitle);
}
Model classes
public class Film
{
public string Title { get; set; }
public string Description { get; set; }
public int ReleaseYear { get; set; }
public decimal RentalRate { get; set; }
public int Length { get; set; }
public string Rating { get; set; }
}
public class Actor
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Web Forms code:
IFilmRepository _filmRepository;
public void Page_Load (object sender, EventArgs e)
{
_filmRepository = new MySqlFilmRepository(ConfigurationManager.ConnectionStrings["MySqlConnectionString"].ConnectionString);
}
protected void SearchButton_Click(object sender, EventArgs e)
{
dataGridView1.DataSource = _filmRepository.SearchFilmsByTitle(SearchTextBox.Text);
dataGridView2.DataSource = _filmRepository.GetActorsForFilm(SearchTextBox.Text);
}
It ends up being more code, but now you have a centralized place to get your data calls, and you're handling your connections properly, and you have less boilerplate code to query your database, and it's more flexible. This is more code, but it's also not really repeating itself very much.
Related
I tried to find a solution for this but couldn't. Here's the problem:
I'm loading data of a bunch of users and creating an object for each user. Each user object has many object properties. Here's the structure:
public class User {
public int ID { get; set; }
public string Name { get; set; }
public City City { get; set; }
public Office Office { get; set; }
}
The City class:
public class City {
public int ID { get; set; }
public string Name { get; set; }
public string Keyword { get; set; }
}
The Office class:
public class Office {
public int ID { get; set; }
public string Address { get; set; }
public int CityID { get; set; }
}
The user object has many other similar properties like City & Office which are basically class objects.
Now here's the main issue. Whenever I try to load all the users into a collection of dictionary, StackOverflow exception occurs at SqlCon.Open() (See the "Fetch" function I've written below). Here's how I'm loading everything:
//Code to load users
Dictionary<int, User> Users = new Dictionary<int, Users>();
DataTable usersData = new DataTable();
//The Fetch function has two version. The first one; which is mentioned in this post, returns the result as Dictionary<string, object>().
//The second version of the function returns the result in the form of the a DataTable and is only used when multiple rows are required from the database. The following returns a set of rows in a DataTable.
Globals.MainDatabase.Fetch("SELECT * FROM users", out usersData);
foreach (DataRow row in usersData.Rows) {
User user = new User();
user.ID = Convert.ToInt32(row["id"]);
user.Name = row["name"].ToString();
user.City = Cities.Get(Convert.ToInt32(row["city_id"]));
user.Office = Offices.Get(Convert.ToInt32(row["office_id"]));
Users.Add(user.ID, user);
}
The methods "Cities.Get(Int32 id)" and "Offices.Get(Int32 id)" uses the following function to fetch data from the database.
public void Fetch(string query, out Dictionary<string, object> results) {
var dict = new Dictionary<string, object>();
try {
using (SqlConnection SqlCon = new SqlConnection(ConnectionString)) {
using (SqlCmd = new SqlCommand()) {
SqlCmd.Connection = SqlCon;
SqlCmd.CommandType = CommandType.Text;
SqlCmd.CommandText = query;
SqlCon.Open();
DataTable temp = new DataTable();
using (SqlDataAdapter SqlAdp = new SqlDataAdapter(SqlCmd)) {
SqlAdp.SelectCommand = SqlCmd;
SqlAdp.Fill(temp);
}
DataRow row = temp.Rows[0];
temp = null;
dict = row.Table.Columns
.Cast<DataColumn>()
.ToDictionary(col => col.ColumnName, col => row.Field<object>(col.ColumnName));
}
}
}
catch (Exception ex) {
HandleException(ex, "An error occurred when tried to fetch data.", query);
}
results = dict;
dict = null;
}
I realize that this "Fetch" function is being called multiple times when creating the user object. The "StackOverflow" exception occurs exactly at this line:
SqlCon.Open();
How can I solve this error? or probably I should use a better approach to do this?
A bit too long for a comment
Do you really need to load ALL data from the database? It is better to just grab the columns and rows you need.
Why are you copying DataTables in to Dictionaries? What is wrong with just using a DataTable?
99.9% of the time you will have better performance performing JOINs in the database.
Don't try to roll your own 'ORM'. Use something like Dapper if you don't want the bloat of EF or NHibernate. Or, stick to ADO (DataTable, DataAdapter etc.)
using (SqlConnection SqlCon = new SqlConnection(ConnectionString)) {
Is your connection string variable really named ConnectionString? Is it possible you are having a name clash with the type? Since it is not declared in the shown code, I assume it is a class variable, so you should respect the conventional naming convention, which would be _connectionString. What does your connection string look like?
Alright folks, I figured it out. It was all because of faulty architecture of the whole demo application. Some objects have one or more other objects as properties and due to some silly faults in the architecture; the "fetch" operation which serves as the base of fetching data from the database, was recursively called resulting into StackOverflow exception which was actually a large number of database connections being initialized ultimately growing the heap size to an extent which causes the exception.
I tried to summarize everything in the paragraph written above because posting the complete source code is useless considering the large amount of code.
Thank you everyone who helped, especially #Guffa's comment on the main post which forced me to investigate the whole issue from scratch rather than sticking to the exception stack.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I am a newber in design pattern.
Currently I am developing a system where I have a releation DB. What would be the best approach to CRUD from my DB?
My current code looks like the follow (C# code):
I defined a inteface with commons functions to all classes.
namespace Model
{
public interface ICommon
{
void insert();
void update();
void delete();
}
}
The Common class (abstract one) implements ICommon interface and few orders methods and attributes.
namespace Model
{
public abstract class Common : ICommon
{
public Guid RecId { set; get; }
public abstract void insert();
public abstract void update();
public abstract void delete();
public abstract List<Common> find();
/// <summary>
/// Insert or update the record
/// </summary>
public void save()
{
if (this.RecId == Guid.Empty)
{
this.insert();
}
else
{
this.update();
}
}
}
}
Then, the proper class (UserTable class for example) extends the Common class and implements the abstracts methods and others particulars attributes.
The way that I am doing my CRUD its from StoresProcedures and SqlParameter, SqlCommand and SqlConnection. Here it is a example:
class CustTableModel : Common
{
public string SerialNumber { set; get; }
public string ApplicationVersion { set; get; }
public string KernelVersion { set; get; }
public string Name { set; get; }
public bool Active { set; get; }
public override void insert()
{
List<SqlParameter> parameters = new List<SqlParameter>();
SqlParameter parameter;
// SerialNumber
parameter = new SqlParameter("#serialNumber", System.Data.SqlDbType.Int);
parameter.Value = this.SerialNumber;
parameters.Add(parameter);
// ApplicationVersion
parameter = new SqlParameter("#applicationVersion", System.Data.SqlDbType.Int);
parameter.Value = this.ApplicationVersion;
parameters.Add(parameter);
// KernelVersion
parameter = new SqlParameter("#kernelVersion", System.Data.SqlDbType.Int);
parameter.Value = this.KernelVersion;
parameters.Add(parameter);
// Name
parameter = new SqlParameter("#name", System.Data.SqlDbType.Int);
parameter.Value = this.Name;
parameters.Add(parameter);
// Active
parameter = new SqlParameter("#active", System.Data.SqlDbType.Bit);
parameter.Value = this.Active;
parameters.Add(parameter);
DBConn.execute("CUSTTABLE_INSERT", parameters); // The code of DBConn is below.
}
}
Just to a better understanding, here it is the DBConn class:
public class DBConn
{
protected SqlConnection sqlConnection;
protected string command { set; get; }
protected List<SqlParameter> parameters { set; get; }
protected void openConnection()
{
this.sqlConnection = new SqlConnection();
this.sqlConnection.ConnectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=JYL_SOAWS_DB;Integrated Security=True";
this.sqlConnection.Open();
}
protected void closeConnection()
{
if (this.sqlConnection.State == System.Data.ConnectionState.Open)
{
this.sqlConnection.Close();
}
}
/// <summary>
/// Executa o processo no banco.
/// </summary>
/// <returns>Quantidade de registros afetados.</returns>
protected SqlDataReader run()
{
SqlCommand command = new SqlCommand();
SqlDataReader ret;
this.openConnection();
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Connection = this.sqlConnection;
command.CommandText = this.command;
if (this.parameters != null)
{
foreach (SqlParameter parameter in this.parameters)
{
command.Parameters.Add(parameter);
}
}
ret = command.ExecuteReader();
this.closeConnection();
return ret;
}
/// <summary>
/// Interface da classe Ă outros objetos.
/// </summary>
/// <param name="commandName">Nome da store procedure a ser executada.</param>
/// <param name="parameters">A lista com os parĂ¢metros e valores.</param>
/// <returns>Numero de registros afetados.</returns>
public static SqlDataReader execute(string commandName, List<SqlParameter> parameters = null)
{
DBConn conn = new DBConn();
conn.command = commandName;
conn.parameters = parameters;
return conn.run();
}
}
I am pretty sure that there is a better way.
Could anyone help me? Thanks is advance.
You have hit upon two subtly different patterns here.
The first is the repository pattern - a way of abstracting away your business logic from your data access
The second is the Active Record pattern, whereby an entity is responsible for maintaining its own state in a database.
I would recommend you stay away from ActiveRecord in C# (you may or may not know about the Inversion of Control pattern right now, but it is very useful and fairly incompatible with AR).
I would suggest you look at something like dapper.net if you are starting out (I still use it in my smaller projects). It is a Micro-ORM which takes lots of the boilerplate away from using a database, without being opinionated or difficult to learn (I use and like EntityFramework & NHibernate, but they aren't anywhere as easy to pick up for a beginner).
Along with this, I would create a repository (a class with Create(Foo entity), Read(Guid entityId), Update(Foo entity) & Delete(Guid entityId) methods).
As an aside, be careful when using Guids as a primary key, as they can cause an interesting situation: Since most Guid implementations (almost always) have a non-sequential layout, and data is physically ordered by primary key, such inserts with can cause a lot of disk IO as the database reorders data pages on disk to accommodate new data inserted at some arbitrary position within the table. A good strategy for Guid generation for use as a primary key is to use a Guid Comb generator
Good Luck!
This is the best pattern. I advise not using an ORM. especially EF.
public class MyModel
{
public string Id {get;set;}
//public valuetype PropertyA {get;set;} //other properties
}
public interface IMyModelRepository
{
List<MyModel> GetModels();
MyModel GetModelById(string id);
void AddMyModel(MyModel model);
//other ways you want to get models etc
}
public class MyModelRepositorySql : IMyModelRepository
{
public List<MyModel> GetModels()
{
//SqlConnection etc etc
while (SqlDataReader.Read())
{
results.Add(this.populateModel(dr));
}
return results;
}
protected MyModel populateModel(SqlDataReader dr)
{
//map fields to datareader
}
public MyModel GetModelById(string id)
{
//sql conn etc
this.populateModel(dr);
}
}
Here's my reasoning:
Using the repository pattern allows you to inject ways of persisting your data which doesn't require a database. This is essential for unit testing, but also you will find it very useful if you can inject a mock repository into your project for integration testing.
Although ORMs might seem easy at first and save you a lot of typing, they cause problems in the long run. You only need to search stack overflow for entity framework questions to see the kind of knots people get themselves tied in when they hit a query that runs in a sub optimal way.
In any large project you will run across a data fetch requirement which requires some optimized way of retrieving data, which will muck up your carefully designed object graph/injectable generic repository or clever cutting edge ORM.
POCO objects are good. Complex objects (objects which have other objects as properties) are a pain in the arse when you attempt to serialise them or recursively add to the databases, etc. Keep your underlying data models POCO and only group them together in services or viewmodels using LINQ.
Well done for using GUID ids btw! Don't listen to those fools who think they will never run out of ints! (store as varchar(50) and let the DBA sort the indexing out) the problem with any DB generated id is you have to be able to create objects without connecting to the database.
For performing CRUD operations I would recommend Repository pattern with Entity framework.
Entity Framework is an ORM provided by microsoft. It deals with database using set of POCO Classes (Entity) to perform insert/update/delete/create operations.
To Execute queries against these entities, Language Integrated Query (LINQ) will be used. LINQ uses similar syntax of SQL and it returns database results as collection of Entities.
Here is a sample
Repository pattern with EF
Cheers!
In this sample code (C# winForms app) there is a Employee class with SearchEmployee() method and a DataService class with GetByEmployeeID() method. When searching for a employee, SearchEmployee() method will call GetByEmployeeID() method to talk to database. I have minimized the dependency between Employee class and DataService class by using constructor injection. (in its simplest way with out using an Interface)
But there is a dependency between Form class and Employee class as I new employee object from From class.
Will that dependency be a problem or isn't?
If that dependency should be avoided, What is the simplest way to achieve this?
I prefer not to use a pattern like MVP as I'm not familiar with it.
Class Form
{
public Form()
{
InitializeComponents();
}
private void btnSave_Click(object sender, EventArgs e)
{
Employee newEmp = new Employee (new DataService()); //Making a dependency
newEmp = newEmp.SearchEmployee (txtEmployeeID.Text);
txtEmployeeName.Text = newEmp.EmployeeName;
txtEmployeeAddress.Text = newEmp.EmployeeAddress;
}
}
Class Employee
{
string EmployeeID { get; set; }
string EmployeeName { get; set; }
string EmployeeAddress { get; set; }
DataService _DS;
public Employee(DataService DS) //Constructor injection of dataservice object
{
this._DS = DS;
}
public Employee SearchEmployee (string employeeID)
{
this.EmployeeID =employeeID;
DataTable DT= _DS.GetByEmployeeID(EmployeeID);
this.EmployeeName = DT.Rows[0].ItemArray[1].ToString();
this.EmployeeAddress = DT.Rows[0].ItemArray[2].ToString();
return this; //Returning an employee object to the caller
}
}
//This class responsible for database transaction
class DataService
{
public DataTable GetByEmployeeID(string employeeID)
{
using (SqlConnection newCon = new SqlConnection(db.GetConnectionString))
{
SqlCommand Cmd = new SqlCommand("SELECT..WHERE emp_id=#employeeID", newCon);
Cmd.Parameters.Add("#employeeID", SqlDbType.varChar).Value = employeeID;
newCon.Open();
SqlDataReader rdr = Cmd.ExecuteReader();
DataTable results = new DataTable();
results.Load(rdr);
return results;
}
}
}
Actually, a class representing an entity should contain information relevant to that entity.
Any method that belong to the management of the entity, like looking for a specific object, telling which ones contain a set of properties and the like should be in a different class.
To make my point clear:
You can have your "Employee" with only the 3 string properties and then an "EmployeeManager" which is responsible of searching for employees, containing a list with all employees, looking by id, etc.
That way, you objects will be only information carriers and you will brake the dependency between them.
In your case, it makes more sense to have the "SearchEmployee" method on the Data Service.
Will that dependency be a problem or isn't? - It's not a problem here until how you want to get things done.
If that dependency should be avoided..? - Yes. Your program is having only one unit of work which is GetByEmployeeID(string employeeID). Dependency Injection(DI) is supposed to use when an employee object will need some other objects like department(which becomes dependency of employee object & will be injected via constructor pattern). In your program, dependency works like a service so there is almost no possibility that it alters its behavior depending on the caller. Also DI simplifies testing/mocking objects, testing employee object will eliminate the need of testing it's dependencies i.e. department.
What is the simplest way to achieve this?. I prefer not to use a pattern like MVP as I'm not familiar with it. -
Well, simplest requires solid base/architecture, then after your program will be able to accomplish this task in quite a few lines of code. You can use ORM(object relational mapper) frameworks like Microsoft entity framework which simplifies domain/data/repository/unit-of-work part.
I am looking for design advice for the following scenario:
I have a code-first EF5 MVC application. I am building a full-text search function which will incorporate multiple weighted columns from many tables. As I cannot create view with an index from these tables (some of them contain text / binary columns), I have created a stored procedure which will output the ID of my object (eg. PersonID) and the rank associated with that object based on the search terms.
My current approach is to create a helper class for executing full text searches which call the stored procedure(s) and load all the objects from the context based on the returned IDs.
My questions are:
Does my approach seem sensible / follow reasonable best practice?
Has anyone else done something similar with any lessons learned?
Is there a way to do this more efficiently (i.e. have the results of the stored procedure return/map to the entities directly without an additional look-up required?)
UPDATE
Moved my detailed implementation from an edit of the question into its own answer to be more in line with what is recommended frequently # meta.stackexchange.com
Seeing as you can't use SQL methods like containstable with entityframework code first which the rest of your application could be using you could be 'forced' to do something with a storedprocedure like your describe. Whether it's best practice I don't know. However it it gets the job done I don't see why it wouldn't be sensible.
Yes - I have and still am working on a project build around EF codefirst where I had to do a fairly complex search that included several search parameters marked as 'must have' and several values marked as 'nice to have' and in from that return a weighted result.
Depending on the complexity of the result set I don't think you need to do a second roundtrip to the database and I will show you a way I have been doing it below.
Bear in mind that below is simply an example:
public List<Person> GetPeople(params string[] p)
{
var people = new List<Person>();
using (var db = new DataContext())
{
var context = ((IObjectContextAdapter)db).ObjectContext;
db.Database.Connection.Open();
var command = db.Database.Connection.CreateCommand();
command.CommandText = "SomeStoredProcedureReturningWeightedResultSetOfPeople";
command.CommandType = System.Data.CommandType.StoredProcedure;
//Add parameters to command object
people = context.Translate<Person>(command.ExecuteReader()).ToList();
}
return people;
}
Even though the storedprocedure will have a column for the weight value it won't get mapped when you translate it.
You could potentially derive a class from Person that includes the weight value if you needed it.
Posting this as an answer rather than an edit to my question:
Taking some of the insight provided by #Drauka's (and google) here is what I did for my initial iteration.
Created the stored procedure to do the full text searching. It was really too complex to be done in EF even if supported (as one example some of my entities are related via business logic and I wanted to group them returning as a single result). The stored procedure maps to a DTO with the entity id's and a Rank.
I modified this blogger's snippet / code to make the call to the stored procedure, and populate my DTO: http://www.lucbos.net/2012/03/calling-stored-procedure-with-entity.html
I populate my results object with totals and paging information from the results of the stored procedure and then just load the entities for the current page of results:
int[] projectIDs = new int[Settings.Default.ResultsPerPage];
foreach (ProjectFTS_DTO dto in
RankedSearchResults
.Skip(Settings.Default.ResultsPerPage * (pageNum - 1))
.Take(Settings.Default.ResultsPerPage)) {
projectIDs[index] = dto.ProjectID;
index++;
}
IEnumerable<Project> projects = _repository.Projects
.Where(o=>projectIDs.Contains(o.ProjectID));
Full Implementation:
As this question receives a lot of views I thought it may be worth while to post more details of my final solution for others help or possible improvement.
The complete solution looks like:
DatabaseExtensions class:
public static class DatabaseExtensions {
public static IEnumerable<TResult> ExecuteStoredProcedure<TResult>(
this Database database,
IStoredProcedure<TResult> procedure,
string spName) {
var parameters = CreateSqlParametersFromProperties(procedure);
var format = CreateSPCommand<TResult>(parameters, spName);
return database.SqlQuery<TResult>(format, parameters.Cast<object>().ToArray());
}
private static List<SqlParameter> CreateSqlParametersFromProperties<TResult>
(IStoredProcedure<TResult> procedure) {
var procedureType = procedure.GetType();
var propertiesOfProcedure = procedureType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var parameters =
propertiesOfProcedure.Select(propertyInfo => new SqlParameter(
string.Format("#{0}",
(object) propertyInfo.Name),
propertyInfo.GetValue(procedure, new object[] {})))
.ToList();
return parameters;
}
private static string CreateSPCommand<TResult>(List<SqlParameter> parameters, string spName)
{
var name = typeof(TResult).Name;
string queryString = string.Format("{0}", spName);
parameters.ForEach(x => queryString = string.Format("{0} {1},", queryString, x.ParameterName));
return queryString.TrimEnd(',');
}
public interface IStoredProcedure<TResult> {
}
}
Class to hold stored proc inputs:
class AdvancedFTS :
DatabaseExtensions.IStoredProcedure<AdvancedFTSDTO> {
public string SearchText { get; set; }
public int MinRank { get; set; }
public bool IncludeTitle { get; set; }
public bool IncludeDescription { get; set; }
public int StartYear { get; set; }
public int EndYear { get; set; }
public string FilterTags { get; set; }
}
Results object:
public class ResultsFTSDTO {
public int ID { get; set; }
public decimal weightRank { get; set; }
}
Finally calling the stored procedure:
public List<ResultsFTSDTO> getAdvancedFTSResults(
string searchText, int minRank,
bool IncludeTitle,
bool IncludeDescription,
int StartYear,
int EndYear,
string FilterTags) {
AdvancedFTS sp = new AdvancedFTS() {
SearchText = searchText,
MinRank = minRank,
IncludeTitle=IncludeTitle,
IncludeDescription=IncludeDescription,
StartYear=StartYear,
EndYear = EndYear,
FilterTags=FilterTags
};
IEnumerable<ResultsFTSDTO> resultSet = _context.Database.ExecuteStoredProcedure(sp, "ResultsAdvancedFTS");
return resultSet.ToList();
}
Is this a standard, good practice way of doing things? Basically return a list of itself? Should the actual fields (id, title, etc) be a separate class? (I've seen people call it DTO objects)
I'm starting a project & I want to try & get some of these fundamentals down--
Thanks!!
public class Calendar
{
public int id { get; set; }
public string title { get; set; }
public List<calendar> GetAll()
{
var list = new List<calendar>();
var db = new mssql2();
db.set("s1");
string sql = #"select * from [cal]";
var dr = db.dr(sql);
while (dr.Read())
{
var e = new calendar();
e.id = (int)dr["id"];
e.title = dr["title"].ToString();
list.Add(e);
}
return list;
}
}
You seem to be mixing your Domain model with your Data Access layer.
Keep Calendar as it's own class, and maybe make another class called CalendarService or CalendarRepository that returns you a list of Calendar objects.
Here is an example:
public class Calendar
{
public Calendar() { }
public Calendar(int id, string title)
{
Id = id;
Title = title;
}
public int Id { get; set; }
public string Title { get; set; }
}
public class CalendarService
{
public static List<Calendar> GetAll()
{
var list = new List<Calendar>();
var db = new mssql2();
db.set("s1");
string sql = #"select * from [cal]";
var dr = db.dr(sql);
while (dr.Read())
{
// Use the constructor to create a new Calendar item
list.Add(new Calendar((int)dr["id"], dr["title"].ToString()));
}
return list;
}
}
The general idea is for the classes to represent domain objects, and class members various properties of those domain objects. Class functions would represent what the objects can do.
In your case, it might be more fitting to remove the get_all() to a some class abstracting database operations. Calendar would have the functionalities of a calendar (getting/setting some dates, getting skip years, getting/setting some appointments); depending of what you want to accomplish with a calendar.
You're tightly coupling data access, and your "get_all" method isn't even using anything from the object of type calendar. If, as in this case, your method doesn't use any data from the instance of the class to which it belongs, then that method should either not be there, or should be a static method. My preference would be for the former -- have a class whose intent is to retrieve a calendar or calendars from the database. It is a more sensible organization of code, is more testable, can be more easily abstracted from the data layer, and it also makes your data object more portable.