Calling 'Read' when the data reader is closed is not a valid operation error using Entity Framework database first approach - asp.net-web-api

I am creating a Web API that will fetch information from a table using Entity Framework database-first approach using stored procedures. ListAllTeams_Result is the complex type object created in Entity Framework. I am looping through the import function GetAllTeams() and populating the complex type. I am getting an error in my business layer when trying to access the data access layer
The error that I am getting is the following code
var team = _teamRepository.GetAllTeams();
The result of the query cannot be enumerated more than once.
Note: this error is in the inner stack and doesn't stop the application from executing
foreach (var t in team)
Calling 'Read' when the data reader is closed is not a valid operation.
Note : This stops execution
Business Layer
public IEnumerable<TeamDto> GetTeam()
{
var team = _teamRepository.GetAllTeams();
if (team != null)
{
foreach (var t in team.ToList())
{
yield return Mapper.Map<TeamDto>(t);
}
}
yield break;
}
DataAccess layer:
public IEnumerable<ListAllTeams_Result> GetAllTeams()
{
using (var mcrContext = new MCREntities())
{
return (from team in mcrContext.ListAllTeams("")
select new ListAllTeams_Result
{
TeamID = team.TeamID,
TeamDescription = team.TeamDescription,
CountryCode = team.CountryCode,
CreatedBy = team.CreatedBy,
CreatedDate = team.CreatedDate,
ModifiedBy = team.ModifiedBy,
ModifiedDate = team.ModifiedDate
});
}
}

I have found what the problem is. I had to add ToList in the return
using (var mcrContext = new MCREntities())
{
return (from team in mcrContext.ListAllTeams("")
select new ListAllTeams_Result
{
TeamID = team.TeamID,
TeamName = team.TeamName,
TeamDescription = team.TeamDescription,
CountryCode = team.CountryCode,
CreatedBy = team.CreatedBy,
CreatedDate = team.CreatedDate,
ModifiedBy = team.ModifiedBy,
ModifiedDate = team.ModifiedDate
}).ToList();
}

Related

Entity Framework cycle of data

I have an Account object, which has many Transactions related to it.
In one method, I get all transactions for a particular account.
var transactionlines = (from p in Context.account_transaction
.Include("account_transaction_line")
// .Include("Account")
.Include("account.z_account_type")
.Include("account.institution")
.Include("third_party")
.Include("third_party.z_third_party_type")
.Include("z_account_transaction_type")
.Include("account_transaction_line.transaction_sub_category")
.Include("account_transaction_line.transaction_sub_category.transaction_category")
.Include("z_account_transaction_entry_type")
.Include("account_transaction_line.cost_centre")
where p.account_id == accountId
&& p.deleted == null
select p).ToList();
This is meant to return me a list of transactions, with their related objects. I then pass each object to a Translator, which translates them into data transfer objects, which are then passed back to my main application.
public TransactionDto TranslateTransaction(account_transaction source)
{
LogUserActivity("in TranslateTransaction");
var result = new TransactionDto
{
Id = source.id,
Version = source.version,
AccountId = source.account_id,
// Account = TranslateAccount(source.account, false),
ThirdPartyId = source.third_party_id,
ThirdParty = TranslateThirdParty(source.third_party),
Amount = source.transaction_amount,
EntryTypeId = source.account_transaction_entry_type_id,
EntryType = new ReferenceItemDto
{
Id = source.account_transaction_entry_type_id,
Description = source.z_account_transaction_entry_type.description,
Deleted = source.z_account_transaction_entry_type.deleted != null
},
Notes = source.notes,
TransactionDate = source.transaction_date,
TransactionTypeId = source.account_transaction_type_id,
TransactionType = new ReferenceItemDto
{
Id = source.z_account_transaction_type.id,
Description = source.z_account_transaction_type.description,
Deleted = source.z_account_transaction_type.deleted != null
}
};
... return my object
}
The problem is:
An account has Transactions, and a Transaction therefore belongs to an Account. It seems my translators are being called way too much, and reloading a lot of data because of this.
When I load my transaction object, it's 'account' property has a'transactions' propery, which has a list of all the transactions associated to that account. Each transaction then has an account property... and those account peroprties again, have a list of all the transactions... and on and on it goes.
Is there a way I can limit the loading to one level or something?
I have this set:
Context.Configuration.LazyLoadingEnabled = false;
I was hoping my 'Includes' would be all that is loaded... Don't load 'un-included' related data?
As requested, here is my TranslateAccount method:
public AccountDto TranslateAccount(account p, bool includeCardsInterestRateDataAndBalance)
{
LogUserActivity("in TranslateAccount");
if (p == null)
return null;
var result =
new AccountDto
{
Id = p.id,
Description = p.description,
PortfolioId = p.institution.account_portfolio_id,
AccountNumber = p.account_number,
Institution = TranslateInstitution(p.institution),
AccountType = new ReferenceItemDto
{
Id = p.account_type_id,
Description = p.z_account_type.description
},
AccountTypeId = p.account_type_id,
InstitutionId = p.institution_id,
MinimumBalance = p.min_balance,
OpeningBalance = p.opening_balance,
OpeningDate = p.opening_date
};
if (includeCardsInterestRateDataAndBalance)
{
// Add the assigned cards collection
foreach (var card in p.account_card)
{
result.Cards.Add(new AccountCardDto
{
Id = card.id,
AccountId = card.account_id,
Active = card.active,
CardHolderName = card.card_holder_name,
CardNumber = card.card_number,
ExpiryDate = card.expiry
});
}
// Populate the current interest rate
result.CurrentRate = GetCurrentInterestRate(result.Id);
// Add all rates to the account
foreach (var rate in p.account_rate)
{
result.Rates.Add(
new AccountRateDto
{
Id = rate.id,
Description = rate.description,
Deleted = rate.deleted != null,
AccountId = rate.account_id,
EndDate = rate.end_date,
Rate = rate.rate,
StartDate = rate.start_date
});
}
result.CurrentBalance = CurrentBalance(result.Id);
}
LogUserActivity("out TranslateAccount");
return result;
}
The entity framework context maintains a cache of data that has been pulled out of the database. Regardless of lazy loading being enabled/disabled, you can call Transaction.Account.Transactions[0].Account.Transactions[0]... as much as you want without loading anything else from the database.
The problem is not in the cyclical nature of entity framework objects - it is somewhere in the logic of your translation objects.

Master details batch crud with entity framework only saving single detail record

I'm building a master details page with batch editing, that is multiple details records with single master record. but only one detail record is being saved into the data base. I tried to debug & found that detail loop is executing multiple time accurately but not the saving multiple data. Here is my Code for save method:
public ActionResult CMN_VAL_FORM(HRM_CMN_VLU_MST_ViewModel model)
{
//var ctx=new Entities1();
var CMN_VLU_MST_OBJ = new HRM_CMN_VLU_MST();
var CMN_VLU_DTL_OBJ = new HRM_CMN_VLU_DTL();
//using (TransactionScope transaction = new TransactionScope())
//{
using (var ctx = new Entities1())
{
var type_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_mst','CMN_VLU_TYPE_CODE') from dual").SingleOrDefault(); //A scalar function to generate the code in the format yymmdd0001
var value_code = ctx.ExecuteStoreQuery<string>("select get_pk_code('hrm_cmn_vlu_dtl','CMN_VLU_CODE') from dual").SingleOrDefault();
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_MST_OBJ.CMN_VLU_REM = model.CMN_VLU_REM;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_FOR = model.CMN_VLU_TYPE_FOR;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_SRTNM = model.CMN_VLU_TYPE_SRTNM;
CMN_VLU_MST_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_MST_OBJ.CMN_VLU_TYPE_NAME = model.CMN_VLU_TYPE_NAME;
foreach (var item in model.HRM_CMN_VLU_DTL)
{
CMN_VLU_DTL_OBJ.CMN_VLU_LEVL = item.CMN_VAL_LEVL;
CMN_VLU_DTL_OBJ.CMN_VLU_REM = item.CMN_VLU_REM;
CMN_VLU_DTL_OBJ.CMN_VLU_SLNO = item.CMN_VLU_SLNO;
CMN_VLU_DTL_OBJ.CMN_VLU_TITL = item.CMN_VAL_TITL;
CMN_VLU_DTL_OBJ.CMN_VLU_CNTN = item.CMN_VAL_CNTN;
CMN_VLU_DTL_OBJ.CMN_VLU_CODE = value_code;
CMN_VLU_DTL_OBJ.CMN_VLU_TYPE_CODE = type_code;
CMN_VLU_DTL_OBJ.ENTRY_DATE = DateTime.Now;
CMN_VLU_DTL_OBJ.MAIL_ADDR_INT = item.MAIL_ADDR_INT;
CMN_VLU_DTL_OBJ.MAIL_ADDR_EXT = item.MAIL_ADDR_EXT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_INT = item.MAIL_AUTO_SEND_INT;
CMN_VLU_DTL_OBJ.MAIL_AUTO_SEND_EXT = item.MAIL_AUTO_SEND_EXT;
CMN_VLU_DTL_OBJ.ACTIVE_STATUS = item.ACTIVE_STATUS;
CMN_VLU_MST_OBJ.HRM_CMN_VLU_DTL.Add(CMN_VLU_DTL_OBJ);
ctx.SaveChanges();
var temp_value_code = Int32.Parse(value_code);
temp_value_code++;
value_code = temp_value_code.ToString();
ctx.HRM_CMN_VLU_MST.AddObject(CMN_VLU_MST_OBJ);
}
ctx.SaveChanges();
// transaction.Complete();
//}
}
return View();
}
No Error message for the code, but not saving multiple detail records. What I'm doing wrong?
What I was doing wrong, I created the object just once & updated that same object every time while executing the foreach loop! I just moved the object declaration into the foreach loop & it works like a charm!

How to want to move my DAL to separate project in my MVC3 solution?

I have MVC 3 application that uses a DAL (ADO.NET) that communicates to a set of tsql stored procedures? I want to add a new MVC project to my current solution. I need to have DAL in a separate project that the 2 MVC project ("Monitor" and "Audit") can share.
Here's the current DAL (which sits in a folder of the "Monitor" MVC project) code below. My issue is I have signature like IEnumerable located in the Monitor.Models and IEnumerable located in the Audit.Models. Do I need to make the DAL generic to avoid needing to make references to the models in the DAL?
Ex:
**//Is this bad practice?**
using Monitor.Models;
using Adit.Models;
namespace Monitor.DAL
{
public class QuestionDAL
{
static ILog log = log4net.LogManager.GetLogger(typeof(QuestionDAL));
private string _connectionString = WebConfigurationManager.ConnectionStrings["NexGenContext"].ToString();
public IEnumerable<AgencyTerm> SearchAgencies(string ori, string name)
{
log.Debug("Executing: SearchAgencies(string ori, string name)");
List<AgencyTerm> agencies = new List<AgencyTerm>();
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
string term = "Ori";
if (!String.IsNullOrEmpty(ori))
{
term = "Ori";
com.Parameters.Add(new SqlParameter
{
ParameterName = "#ORI",
Value = ori
});
}
if (!String.IsNullOrEmpty(name))
{
term = "legal_name";
com.Parameters.Add(new SqlParameter
{
ParameterName = "#Name",
Value = name
});
}
com.CommandText = "Review_Get_Agency_List";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
agencies = (from c in dataset.Tables[0].AsEnumerable()
select new AgencyTerm()
{
label = c[term].ToString(),
id = c["Agency_Id"].ToString()
}).ToList<AgencyTerm>();
return agencies;
}
}
public IEnumerable<User> GetUsers()
{
log.Debug("Executing: GetUsers()");
List<User> users = new List<User>();
using (var conn = new SqlConnection(_connectionString))
{
var com = new SqlCommand();
com.Connection = conn;
com.CommandType = CommandType.StoredProcedure;
com.CommandText = "Review_Get_Users";
var adapt = new SqlDataAdapter();
adapt.SelectCommand = com;
var dataset = new DataSet();
adapt.Fill(dataset);
users = (from c in dataset.Tables[0].AsEnumerable()
select new User()
{
User_ID = Convert.ToInt32(c["User_ID"]),
Department = c["Department"].ToString(),
Enabled = Convert.ToBoolean(c["Enabled"]),
Email = c["Email"].ToString(),
User_First_Name = c["User_First_Name"].ToString(),
User_Last_Name = c["User_Last_Name"].ToString(),
Location = c["Location"].ToString(),
User_Name = c["User_Name"].ToString()
}).ToList<User>();
return users;
}
}
You have two possibilities:
Either move your model into a separate library as well and then reference it from your MVC project and your DAL
Make your DAL completely generic and just push the values inside. I don't see an easy way here though since you have a lot of information in your DAL
I would go with the first option. Just extract your models to a different project and then reuse that library in both DAL and MVC-projects

How to: linq query

I am trying to get a record from database using linq but it keep return no record
it is very basic sql statment
select * where productid ='12553'
however the following code does not return any result. Please advise. thx you
private static IEnumerable<ProductModel> GetAllProduct(string productId)
{
using (var dc = new TestEntities())
{
var result = (from a in dc.Products
where a.productid == productId
select new ProductModel
{
ProductId = a.productid,
Name = a.ProductName
});
return result.Distinct().ToList();
}
}
You don't need projection here:
using (var dc = new TestEntities())
{
var result = from a in dc.Products
where a.productid == productId
select a;
return result.Distinct().ToList();
}

LINQ to Entities Select New

public static object ExecuteScalar(string SQL)
{
try
{
var A = new EGModel.EGEntity().Connection;
var command = ((EntityConnection)(A)).StoreConnection.CreateCommand();
command.CommandType = System.Data.CommandType.Text;
command.CommandText = SQL;
if (((EntityConnection)(A)).StoreConnection.State == System.Data.ConnectionState.Closed)
((EntityConnection)(A)).StoreConnection.Open();
return command.ExecuteScalar();
}
catch { return null; }
}
public object MFICHE(int ID)
{
var i = from b in IConnection.EGEntity().fiche
where (m.ID== ID)
select new { b.Date, m.Name, Addresss = IConnection.ExecuteScalar("SELECT main.F_ADDRESS(4588)") };
return i;
}
I am getting error:
LINQ to Entities does not recognize the method 'System.Object ExecuteScalar(System.String)' method, and this method cannot be translated into a store expression.
Why i am getting error?
But Addresss = "ASASAS" is runing?
The problem is that the expression tree generated from your query includes a call to your ExecuteScalar method - which the Entity Framework expression parser doesn't know anything about. It doesn't look inside that method to see what it's doing - it just knows that the call exists, and fails because it can't translate it.
You wouldn't normally want to execute a separate SQL statement for each result returned from a query? You've got an obvious "N+1 selects" problem.
If you know you've only got a single result (due to the ID constraint) you could fetch the relevant data into an object and then execute the second query:
public object MFICHE(int ID)
{
var query = from b in IConnection.EGEntity().fiche
where b.ID == ID
select new { b.Date, b.Name };
// You only expect a single result, right?
var result = query.Single();
// Shouldn't this be using something to do with the result?
var address = IConnection.ExecuteScalar("SELECT main.F_ADDRESS(4588)");
return new { result.Date, result.Name, Address = address };
}
As an aside, it's very odd to have static methods in a type beginning with I, which would usually be an interface. Additionally, this code:
catch { return null; }
is horrible - you should catch specific exceptions, log them, and normally rethrow them. It's almost never appropriate to just carry on as if nothing had gone wrong.

Resources