Using Generic Repository and Stored Procedures - c#

I am working on an existing application the uses the Generic Repo pattern and EF6 database first.
I am calling a stored proc that returns a complex type that is not an existing entity in my entity models and therefore I am not sure what type to give.
This is how my sp is being called from my service layer
_unitOfWork.Repository<Model>()
.SqlQuery("sp_Get #FromDateTime, #ToDateTime, #CountyId",
new SqlParameter("FromDateTime", SqlDbType.DateTime) { Value = Request.FromDateTime },
new SqlParameter("ToDateTime", SqlDbType.DateTime) { Value = Request.TripToDateTime },
new SqlParameter("CountyId", SqlDbType.Int) { Value = Convert.ToInt32(Request.County) }
).ToList();
Do I create an Entity in my data layer to map to or what is the best approach for stored procedures returning complex types.
If so is there custom mapping needed or is it just a case of creating the Entity class
thank you

If you have an entity with those fields you can call SqlQuery method as you show above, if not, then I suggest creating a new class to map the result:
public class Result
{
public int CountyId { get; set; }
public DateTime FromDateTime { get; set; }
public DateTime ToDateTime { get; set; }
}
I don't know how is implemented the UnitOfWork pattern in your case, but I assume that you have access to your Context. In your UnitOfWork class you could create a generic method like this:
public class UnitOfWork
{
private YourContext Context { get; set; }
public DbRawSqlQuery<T> SQLQuery<T>(string sql, params object[] parameters)
{
return Context.Database.SqlQuery<T>(sql, parameters);
}
}
This way, you can execute your store procedures as I show below:
var result= _unitOfWork.SqlQuery<Result>("sp_Get #FromDateTime, #ToDateTime, #CountyId",
new SqlParameter("FromDateTime", SqlDbType.DateTime) { Value = Request.FromDateTime },
new SqlParameter("ToDateTime", SqlDbType.DateTime) { Value = Request.TripToDateTime },
new SqlParameter("CountyId", SqlDbType.Int) { Value = Convert.ToInt32(Request.County) }
).ToList();

The purpose of the Repository Pattern is to abstract away the storage & retrieval of data to protect your client code e.g. business layer (service layer in your case) from needing to know anything about how data is persisted. SQL statements, for example, would only exist inside your Repository classes, and not ripple throughout your code.
If you expose SQL, Stored Procedure names and parameters to your client code your are not getting much benefit from the Repository Pattern, and if fact you can't really call it a Repository at all. You lose the benefit of being able to mock the repository and test your business layer independently of your data access layer. This means integration tests (requiring a full database instance) are required to verify business logic.
Consider re-factoring so that you have a CountryRepository class which has a GetCountry(int CountryId, DateTime fromDate, DateTime toDate) method that returns a Country entity, or similar. I think you'll agree the readability of your code will be much improved compared to the code in your question.
public class CountryRepository
{
public Country GetCountry(int CountryId, DateTime fromDate, DateTime toDate)
{
// EF or ADO.NET code here
}
}
Client code would then be e.g.
var c = unitOfWork.CountryRepository.GetCountry(1, DateTime.Now.AddYears(-1), DateTime.Now);
See also this SO question

public virtual IEnumerable<T> GetWithRawSql(string query, params object[] parameters)
{
return DbSet.SqlQuery(query, parameters).ToList();
}
Interface
IEnumerable<T> GetWithRawSql(string query, params object[] parameters);

IQueryable<Cm_Customer> customerQuery = _uow.SqlQuery<Cm_Customer>(#" DECLARE #UserId INT = {0}
EXEC Cm_GetCustomersByUserId #UserId", filter.UserId).AsQueryable();
IQueryable<Cm_Customer> custs = customerQuery.IncludeMultiple(k => k.Cm_CustomerLocations,
k => k.Cm_CustomerSalesmans,
k => k.Cm_CustomerMachineparks,
k => k.Cm_CustomerAuthenticators,
k => k.Cm_CustomerInterviews,
k => k.Cm_CustomerRequest,
k => k.Cm_MachineparkRental).AsQueryable();

Related

How to retrieve a database model for Entity Framework Core with ValueConversions

I've got an asp.net core web api project, where I use Entity Framework. I take in a DTO from the web and convert (using AutoMapper) to a database entity.
At this point I could do some post processing of the entity on the server, before it hits the database. Because of the database structure and limitations in Entity Framework, I need to pass this entity to a stored procedure. At this point I want to get the database model WITH conversions applied.
Basically, this flow...
Controller takes in DTO -> AutoMapper to Entity -> allows me to work with objects and do things before saving -> Save, but using a stored procedure.
My model has a conversion, so how do I get the representation I want for the database at the point where I perform the query?
The problem is I get "false" as a parameter in the controller, this gets converted to a boolean in the entity model, this gets converted to a string ("false") when I want to save it, how do I apply the conversion defined in the entity framework model so that I can save "Y" or "N" as expected?
Simplified example below...
To clarify the question, I need to be able to get the database representation of the model before calling the stored proc, with the code I have below, ToString will be called, so I will get "false" not "N". I have a way to do this when retrieving data, using ValueConversions (that is database -> model). If I was using SaveChanges, EF Core would take care of the conversion (model -> database), but when using raw SQL (in the case a stored proc), how do I get the database representation. At this point, if my model has a boolean property, I want to pass "Y" or "N" to the raw SQL as a parameter... does that make it clearer?
public class TodoDto
{
[ReadOnly(true)]
public long Id{ get; set; }
public string Item { get; set; }
public bool Done { get; set; }
public DateTime? DateStamp { get; set; }
// other properties, model is more complex, but removed to keep it simple
}
public class TodoEFCoreModel
{
[Column("TodoId"), ReadOnly(true)]
public long Id { get; set; }
[Column("TodoItem")]
public string Item { get; set; }
public bool? Done { get; set; }
public DateTime? DateStamp { get; set; }
// other properties
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var yesNoConversion = new YesNoToBoolConverter();
modelBuilder
.Entity<TodoEFCoreModel>()
.Property(x => x.Done)
.HasConversion(yesNoConversion);
}
public ActionResult PostToDo(TodoDto todo)
{
// code is then roughly
var databaseTodoEntity = _mapper.Map<TodoDto, TodoEFCoreModel>(todo);
// here I can check databaseTodoEntity boolean property
// and/or manipulate the model
// when it comes to saving I need to use a stored procedure, I can do this using ExecuteSqlCommandAsync...
await _dbContext.Database.ExecuteSqlCommandAsync("begin CreateTodo(Item => :p0, Done => :p1, DateStamp => :p2); end;", parameters: new[]
{
new OracleParameter("p0", OracleDbType.VarChar2, databaseTodoEntity.Item, ParameterDirection.Input),
// The problem is here, with this code I get "false", instead of the conversion that Entity Framework would apply if I were to be able to call "SaveChanges" on the db context...
new OracleParameter("p1", OracleDbType.Varchar2, databaseTodoEntity.Done, ParameterDirection.Input),
new OracleParameter("p2", OracleDbType.Date, databaseTodoEntity.DateStamp, ParameterDirection.Input)
});
}
EF Core internally uses RelationalTypeMapping class instances which
Represents the mapping between a .NET type and a database type.
and for specific entity property can be obtained with FindRelationalMapping extension method.
Note that this method is considered part of the "internal" API, so you need
using Microsoft.EntityFrameworkCore.Internal;
and be ok with the typical warning
This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases.
Now, along with the Converter and other useful properties, you'd also get an access to some useful methods like CreateParameter, which can be used directly in your scenario. It will do all the necessary conversions and parameter preparation as with EF Core generated commands.
For instance:
var sql = "begin CreateTodo(Item => :p0, Done => :p1, DateStamp => :p2); end;";
var entityType = _dbContext.Model.FindEntityType(typeof(TodoEFCoreModel));
var dbCommand = _dbContext.Database.GetDbConnection().CreateCommand();
object[] parameters =
{
entityType.FindProperty("Item").FindRelationalMapping()
.CreateParameter(dbCommand, "p0", databaseTodoEntity.Item),
entityType.FindProperty("Done").FindRelationalMapping()
.CreateParameter(dbCommand, "p1", databaseTodoEntity.Done),
entityType.FindProperty("DateStamp").FindRelationalMapping()
.CreateParameter(dbCommand, "p2", databaseTodoEntity.DateStamp),
};
await _dbContext.Database.ExecuteSqlCommandAsync(sql, parameters);
Note that DbCommand instance is used only as DbParameter factory (DbCommand.CreateParameter). The created parameters are not added to that command, so it can safely be discarded afterwards.

ABPOData + EF Model with custom calculated properties

I'm using Web API + AbpOData + EF and need to calculate some properties of the objects returned from the database on the server.
The basic code looks something like this:
[AbpApiAuthorize(AppPermissions.OData_Permission_Consume)]
public class ActivityLogsController : AbpODataEntityController<ActivityLogs>
{
[EnableQuery(PageSize = 50000)]
public override IQueryable<ActivityLogs> Get()
{
var objectContext = new MyObjectContext(); //EF
return objectContext.ActivityLogs.GetAll();
}
}
I'm just returning values from database, all's fine.
However what I need is to Convert two datetime value to local time. Like below
[AbpApiAuthorize(AppPermissions.OData_Permission_Consume)]
public class ActivityLogsController : AbpODataEntityController<ActivityLogs>
{
[EnableQuery(PageSize = 50000)]
public override IQueryable<ActivityLogs> Get()
{
var objectContext = new MyObjectContext(); //EF
return objectContext.ActivityLogs.Select(d => new ActivityLogs()
{
Id = d.ID,
Activity = d.Activity,
StartTime = d.StartTime.Value.AddHours(5),
EndTime = d.EndTime.Value.AddHours(5),
Duration = d.Duration
});
}
}
I getting below error
The entity or complex type 'ActivityLogs' cannot be constructed in a LINQ to Entities query.
how i can impliment this using abp odata framework(.net zero). keeping in mind that we need to return the same IQueryable that's returned from EF call.
The error is caused by impossibility to transform AddHours method to SQL.
You have 2 options:
Create a view in DB where you will keep your additional logic.
Add your business for DateTime properties in your client side.

Calling Stored Procedure From Db Context with an non mapped entity

What I am trying to understand is how to call a stored procedure using a DbContext and return an object that is not an entity, OK let me explain a little bit more in case I am not getting the terminology correct
I have a C# Code First Data Context, it has the entity models and mapping models for the tables in the database. I also have about ten stored procedures that return reporting data, none of these stored procedure map to any of the entities I have.
What I would like to do is create a class which represents a row generated by the stored procedure and some how call its corresponding stored procedure with parameters and get a List out.
I have tried this asan attempted to understand what is happening with a simple stored procedure returning 5 rows of data and only two columns
public class ReportA
{
public string Amount { get; set; }
public string Name { get; set; }
}
private MyContext _context = new MyContext();
public List<ReportA> GetReportA(DateTime startDate)
{
var parameters = new[]
{
new SqlParameter("#Startdate", SqlDbType.DateTime)
{
IsNullable = false,
Value = startDate
}
};
var result = _context.Database.SqlQuery<List<reportA>>(
"dbo.ReportA #Startdate", parameters);
return result
}
it fires but I get nothing back. Any advice or help would be appreciated
You should use the following code:
var result = _context.Database.SqlQuery<ReportA>(
"dbo.ReportA #Startdate", parameters);
instead of this code:
var result = _context.Database.SqlQuery<List<ReportA>>(
"dbo.ReportA #Startdate", parameters);
Notice I'm using SqlQuery<ReportA> and not SqlQuery<List<ReportA>>. The template of SqlQuery method should be the type that the entities will be mapped and not the type of your result.

Mapping entities to DTOs without duplicated code

I'm trying to get my head around this issue where I am using the Entity Framework (6) in an N-tier application. Since data from the repository (which contains all communication with the database) should be used in a higher tier (the UI, services etc), I need to map it to DTOs.
In the database, there's quite a few many-to-many relationships going on, so the datastructure can/will get complex somewhere along the line of the applications lifetime. What I stumbled upon is, that I am repeating the exact same code when writing the repository methods. An example of this is my FirmRepository which contains a GetAll() method and GetById(int firmId) method.
In the GetById(int firmId) method, I have the following code (incomplete since there's a lot more relations that needs to be mapped to DTOs):
public DTO.Firm GetById(int id)
{
// Return result
var result = new DTO.Firm();
try
{
// Database connection
using (var ctx = new MyEntities())
{
// Get the firm from the database
var firm = (from f in ctx.Firms
where f.ID == id
select f).FirstOrDefault();
// If a firm was found, start mapping to DTO object
if (firm != null)
{
result.Address = firm.Address;
result.Address2 = firm.Address2;
result.VAT = firm.VAT;
result.Email = firm.Email;
// Map Zipcode and City
result.City = new DTO.City()
{
CityName = firm.City.City1,
ZipCode = firm.City.ZipCode
};
// Map ISO code and country
result.Country = new DTO.Country()
{
CountryName = firm.Country.Country1,
ISO = firm.Country.ISO
};
// Check if this firm has any exclusive parameters
if (firm.ExclusiveParameterType_Product_Firm.Any())
{
var exclusiveParamsList = new List<DTO.ExclusiveParameterType>();
// Map Exclusive parameter types
foreach (var param in firm.ExclusiveParameterType_Product_Firm)
{
// Check if the exclusive parameter type isn't null before proceeding
if (param.ExclusiveParameterType != null)
{
// Create a new exclusive parameter type DTO
var exclusiveParameter = new DTO.ExclusiveParameterType()
{
ID = param.ExclusiveParameterType.ID,
Description = param.ExclusiveParameterType.Description,
Name = param.ExclusiveParameterType.Name
};
// Add the new DTO to the list
exclusiveParamsList.Add(exclusiveParameter);
}
}
// A lot more objects to map....
// Set the list on the result object
result.ExclusiveParameterTypes = exclusiveParamsList;
}
}
}
// Return DTO
return result;
}
catch (Exception e)
{
// Log exception
Logging.Instance.Error(e);
// Simply return null
return null;
}
}
This is just one method. The GetAll() method will then have the exact same mapping logic which results in duplicated code. Also, when more methods gets added, i.e. a Find or Search method, the same mapping needs to be copied again. This is, of course, not ideal.
I have read a lot about the famous AutoMapper framework that can map entites to/from DTOs, but since I have these many-to-many relations it quickly feels bloated with AutoMapper config code. I've also read this article, which make sense in my eyes: http://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/
Is there any other way of doing this without copy/pasting the same code over and over again?
Thanks in advance!
You can make an extension method on Entity firm (DB.Firm) like this,
public static class Extensions
{
public static DTO.Firm ToDto(this DB.Firm firm)
{
var result = new DTO.Firm();
result.Address = firm.Address;
result.Address2 = firm.Address2;
//...
return result;
}
}
Then you can convert DB.Firm object anywhere in your code like firm.ToDto();
An alternate strategy is to use a combination of the class constructor and an explicit and/or implicit conversion operator(s). It allows you to cast one user-defined entity to another entity. The feature also has the added benefit of abstracting the process out so you aren't repeating yourself.
In your DTO.Firm class, define either an explicit or implicit operator (Note: I am making assumptions about the name of your classes):
public class Firm {
public Firm(DB.Firm firm) {
Address = firm.Address;
Email = firm.Email;
City = new DTO.City() {
CityName = firm.City.City1;
ZipCode = firm.City.ZipCode;
};
// etc.
}
public string Address { get; set;}
public string Email { get; set; }
public DTO.City City { get; set; }
// etc.
public static explicit operator Firm(DB.Firm f) {
return new Firm(f);
}
}
You can then use it in your repository code like this:
public DTO.Firm GetById(int id) {
using (var ctx = new MyEntities()) {
var firm = (from f in ctx.Firms
where f.ID == id
select f).FirstOrDefault();
return (DTO.Firm)firm;
}
}
public List<DTO.Firm> GetAll() {
using (var ctx = new MyEntities()) {
return ctx.Firms.Cast<DTO.Firm>().ToList();
}
}
Here's the reference in MSDN.
About mapping: it actually does not really matter if you use Automapper or prepare you mappings completely manually in some method (extension one or as explicit casting operator as mentioned in other answers) - the point is to have it in one place for reusability.
Just remember - you used FirstOrDefault method, so you actually called the database for a Firm entity. Now, when you are using properties of this entity, especiallly collections, they will be lazy loaded. If you have a lot of them (as you suggest in your question), you may face a huge amount of additional call and it might be a problem, especcially in foreach loop. You may end up with dozen of calls and heavy performace issues just to retrieve one dto. Just rethink, if you really need to get such a big object with all its relations.
For me, your problem is much deeper and considers application architecture. I must say, I personally do not like repository pattern with Entity Framework, in addition with Unit Of Work pattern. It seems to be very popular (at least of you take a look at google results for the query), but for me it does not fit very well with EF. Of course, it's just my opinion, you may not agree with me. For me it's just building another abstraction over already implemented Unit Of Work (DbContext) and repositories (DbSet objects). I found this article very interesing considering this topic. Command/query separation way-of-doing-things seems much more elegant for me, and also it fits into SOLID rules much better.
As I said, it's just my opinion and you may or may not agree with me. But I hope it gives you some perpective here.

Responsibility of Domain Object

I have a domain in which employees can have a list of roles.. There is a new role adding feature also.. While adding a new role, we need to check whether the employee already has a “VP” role. If it is already present new role should not be added. This logic need to be present in the Employee Domain entity.
I started it by adding a method name IsNewRoleAllowed() which will return a Boolean.. If it is true, the business layer will insert the new role to database.
But to be more natural OO, I decided to change the Employee object’s responsibility by making a function AddRole. Instead of returning the Boolean, it will perform the role adding responsibility.
I achieved the above by receiving an Action<int, int> as parameter. It is working fine.
QUESTION
Is it a correct practice to pass the DAL method to entity?
UPDATE
#ThomasWeller added to important points to which I agree...
Having a role is a pure concept of the BL. It has nothing to do with the DAL.
In this approach, the BL would have a dependency of code that resides in the DAL. It (BL) even should work when a DAL does not even physically exist.
But, since I am not using ORM, how would I modify the code to work like the suggested approach?
REFERENCES
Grouping IDataRecord individual records to a collection
CODE
Domain Entities
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public List<Role> Roles { get; set; }
//Add Role to Employee
public int AddRole(Role role, Action<int, int> insertMethod)
{
if (!Roles.Any(r => r.RoleName == "VP"))
{
insertMethod(this.EmployeeID, role.RoleID);
return 0;
}
else
{
return -101;
}
}
//IDataRecord Provides access to the column values within each row for a DataReader
//IDataRecord is implemented by .NET Framework data providers that access relational databases.
//Factory Method
public static Employee EmployeeFactory(IDataRecord record)
{
var employee = new Employee
{
EmployeeID = (int)record[0],
EmployeeName = (string)record[1],
Roles = new List<Role>()
};
employee.Roles.Add(new Role { RoleID = (int)record[2], RoleName = (string)record[3] });
return employee;
}
}
BusinessLayer.Manager
public class EmployeeBL
{
public List<Employee> GetEmployeeList()
{
List<Employee> employees = EmployeeRepositoryDAL.GetEmployees();
return employees;
}
public void AddRoleToEmployee(Employee emp, Role role)
{
//Don't trust the incoming Employee object. Use only id from it
Employee employee = EmployeeRepositoryDAL.GetEmployeeByID(emp.EmployeeID);
employee.AddRole<Employee>(role, EmployeeRepositoryDAL.InsertEmployeeRole);
//EmployeeRepositoryDAL.InsertEmployeeRole(emp.EmployeeID, role.RoleID);
}
}
DAL
public static void InsertEmployeeRole(int empID, int roleID)
{
string commandText = #"INSERT INTO dbo.EmployeeRole VALUES (#empID, #roleID)";
List<SqlParameter> commandParameters = new List<SqlParameter>()
{
new SqlParameter {ParameterName = "#empID",
Value = empID,
SqlDbType = SqlDbType.Int},
new SqlParameter {ParameterName = "#roleID",
Value = roleID,
SqlDbType = SqlDbType.Int}
};
CommonDAL.ExecuteNonQuery(commandText, commandParameters);
}
No. Having a role is a pure concept of the BL in the first place, it has nothing to do with the DAL. Also, in your approach, the BL would have a dependency of code that resides in the DAL, which would be the wrong direction. The BL should be persistence agnostic (i.e. it shouldn't depend in any way on something that would happen in the DAL - it even should work when a DAL does not even physically exist.). Furthermore, the responsibility of the DAL is only to persist objects - not to handle any collections that reside in memory.
Keep it as simple as possible, and just do:
public int AddRole(Role role)
{
if (!Roles.Any(r => r.RoleName == "VP"))
{
Roles.Add(role.RoleName);
return 0;
}
else
{
return -101;
}
}
... in your Employee class, and let the DAL handle all persistence related questions (if you use an ORM it will do cascading updates anyway).
Is it a correct practice to pass the DAL method to entity?
I avoid injection of DAL logic into my Domain Model.
It is not needed to update Data Base once Domain Entity (e.g. Employee) is updated.
The common solution is:
Load entities to update from DB into memory (Identity Map, PoEAA).
Create/ update/ delete entities in memory
Save all changes into DB
In order to track new/dirty/deleted entities Unit of Work pattern
is used usually:
The Unit Of Work Pattern And Persistence Ignorance
Unit of Work and Repository Design Pattern Implementation

Categories

Resources