How to want to move my DAL to separate project in my MVC3 solution? - asp.net-mvc-3

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

Related

Calling 'Read' when the data reader is closed is not a valid operation error using Entity Framework database first approach

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();
}

Permission to enable certain fields depends on CurrentUserID Epicor ERP10

I need help. I need to enable certain fields depends on CurrentUserID. There is one field which is UltraCombo contains of Employee's name. When the Employee's Name is selected, the other fields should be enabled if the CurrentUserID is matched with the selected Employee's name. Otherwise, the other fields should be locked. I tried to use the CanView method in the code but I don't know how to call in SQL command. Plese help me T-T
private bool CanView(string field)
{
bool result = true;
EpiDataView edv = oTrans.EpiDataViews["CallContextClientData"] as EpiDataView;
string CurrentUser = edv.dataView[edv.Row]["CurrentUserId"].ToString();
string ConnectionString = "Data Source=RWNAERP;Initial Catalog=ERP10TESTRWNA;Persist Security Info=True;User ID=sa;Password=Epicor10";
string CompanyId = ((Ice.Core.Session)(oTrans.Session)).CompanyID;
string UserID = ((Ice.Core.Session)(oTrans.Session)).UserID;
using (SqlConnection connection1 = new SqlConnection(ConnectionString))
{
DataTable dt = new DataTable();
connection1.Open();
SqlCommand cmd = new SqlCommand("SELECT DcdUserID FROM dbo.UserFile WHERE Name=#Name AND EmpID=#EmpID", connection1);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("DcdUserID", SqlDbType.NVarChar).Value = UserID;
SqlDataAdapter sqlDa = new SqlDataAdapter(cmd);
sqlDa.Fill(dt);
if (dt.Rows.Count > 0)
{
result = false;
}
if (CurrentUser != "")
{
result = true;
}
connection1.Close();
connection1.Dispose();
}
What you are trying to do is on the client, but the client only connects to the AppServer and never to the SQL Database. Only the AppServer should connect to the database.
Assuming you are adding this code as a customisation script, it's much easier to get the current user info from the Session variable i.e.
var session = (Ice.Core.Session)oTrans.Session;
var userId = session.UserID;
var userName = session.UserName;
var userEmail = session.UserEmail;
In the Epicor.exe client disabling fields is best done with a RowRule, i.e.
var callContextClientData = oTrans.Factory("CallContextClientData");
var disableFieldsForUser = new RowRule("CurrentUserId", RuleCondition.Equals, ((Ice.Core.Session)trans.Session).UserID);
disableFieldsForUser.AddAction(RuleAction.AddControlSettings(stockDtlEpiDataView, "CallContextClientData.ShortChar01", SettingStyle.Disabled));
callContextClientData.AddRowRule(disableFieldsForUser);
It's not clear which fields you are matching or which you want to disable, but hopefully this should get you started.

How can I create a MetadataWorkspace using metadata loading delegates?

I followed this example Changing schema name on runtime - Entity Framework where I can create a new EntityConnection from a MetaDataWorkspace that I then use to construct a DbContext with a different schema, but I get compiler warnings saying that RegisterItemCollection method is obsolete and to "Construct MetadataWorkspace using constructor that accepts metadata loading delegates."
How do I do that? Here is the code that is working but gives the 3 warnings for the RegsiterItemCollection calls. I'm surprised it works since warning says obsolete not just deprecated.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model)
{
XmlReader[] conceptualReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".csdl")
)
};
XmlReader[] mappingReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".msl")
)
};
var storageReader = XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".ssdl")
);
//XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; // this would not work!!!
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet"))
{
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null)
{
schemaAttribute.SetValue(schema);
}
}
storageXml.CreateReader();
StoreItemCollection storageCollection =
new StoreItemCollection(
new XmlReader[] { storageXml.CreateReader() }
);
EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader);
StorageMappingItemCollection mappingCollection =
new StorageMappingItemCollection(
conceptualCollection, storageCollection, mappingReader
);
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories
.GetFactory(connectionData.Provider)
.CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
I was able to get rid of the 3 warning messages. Basically it wants you to register the collections in the constructor of the MetadataWorkspace.
There are 3 different overloads for MetadataWorkspace, I chose to use the one which requires to to supply a path (array of strings) to the workspace metadata. To do this I saved readers to temp files and reloaded them.
This is working for me without any warnings.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model) {
var conceptualReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".csdl"));
var mappingReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".msl"));
var storageReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".ssdl"));
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
var conceptualXml = XElement.Load(conceptualReader);
var mappingXml = XElement.Load(mappingReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet")) {
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null) {
schemaAttribute.SetValue(schema);
}
}
storageXml.Save("temp.ssdl");
conceptualXml.Save("temp.csdl");
mappingXml.Save("temp.msl");
MetadataWorkspace workspace = new MetadataWorkspace(new List<String>(){
#"temp.csdl",
#"temp.ssdl",
#"temp.msl"
}
, new List<Assembly>());
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
Not wanting to create temp files which slows the process down, I found an alternate answer to this is fairly simple. I replaced these lines of code -
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
with this one line of code -
var workspace = new MetadataWorkspace(() => conceptualCollection, () => storageCollection, () => mappingCollection);
and that works fine.

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!

Can I have more than one profiled connection with MVC Mini Profiler?

I'd like to use a mixture of both Dapper and EF 4 in my MVC3 application. Is it possible to have more than one profiled connection, or share a profiled connection?
Yes, you can use the EF profiled connection with Dapper, here's what I ended up using.
private static DatabaseContext GetDatabaseContext()
{
using (MiniProfiler.Current.Step("Create Oracle EF Context"))
{
var pConn = ProfiledConnection;
return pConn.CreateObjectContext<DatabaseContext>();
}
}
private static EFProfiledDbConnection ProfiledConnection
{
get
{
using (MiniProfiler.Current.Step("Create Oracle Profiled Connection"))
{
var connectionString =
ConfigurationManager.ConnectionStrings["DatabaseContext"].ConnectionString;
var ecsb = new EntityConnectionStringBuilder(connectionString);
var oraConn = new OracleConnection(ecsb.ProviderConnectionString);
var pConn = new EFProfiledDbConnection(oraConn, MiniProfiler.Current);
return pConn;
}
}
}
Then I used them in this way
using (var ctx = GetDatabaseContext())
{
var result = ctx.SOME_EF_ENTITY.FirstOrDefault(c => c.LOGINNAME == username && c.PASSWORD == password);
return result;
}
and
const string sql = #"
SELECT ppro_status_code_id
FROM ppro_status_codes
WHERE table_name = 'CLIENT_FC_SHARED'
AND status_code = :status_code";
using (var cnn = ProfiledConnection)
{
cnn.Open();
var data = cnn.Query<dynamic>(sql, new { status_code = code });
var result = data.First();
var codeID = result.PPRO_STATUS_CODE_ID;
return codeID;
}

Resources