how to iterate through an Icollection In MVC3 nhibernate - asp.net-mvc-3

i am developing an application in mvc3.
I have two dropdowns and on the basis of value selected in first dropdown the second dropdown is populated.
The first dropdown is Course and on the basis of course selected the second dropdown populates the states where the course is available.
Foreg.if the course is 'MCA' the states should be Maharashtra,rajasthan and so-on.
For this i have written an ajax function which is working fine.
But the problem is i am not able to fetch multiple states at a time that is i can fetch only One state at a time.
I have written the following Code to fetch the state name:
HobbyHomeAdress Table contains ProvincialStateID which i fetch through some other method.
Then i compare that value with the value in ProvincialStateID in ProvincialState Table and fetch the data of that table but with it gives me the last record only.
public ICollection<ProvincialState> FetchStateByStateid(ICollection<HobbyHomeAddress> hobbyhomeaddresslist)
{
log.Debug("Start");
ISession session = DataAccessLayerHelper.OpenWriterSession();
ITransaction transaction = session.BeginTransaction();
ICollection<ProvincialState> provincialstate = null;
try
{
foreach (var state in hobbyhomeaddresslist)
{
provincialstate = session.CreateCriteria(typeof(ProvincialState))
.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID))
.List<ProvincialState>();
}
transaction.Commit();
}
catch (SessionException ex)
{
if (transaction != null && transaction.IsActive)
transaction.Rollback();
log.Error(ex);
provincialstate = null;
}
finally
{
if (transaction != null)
transaction.Dispose();
if (session != null && session.IsConnected)
session.Close();
log.Debug("End");
}
return provincialstate;
}

you are recreating the provincialstate collection for each state in hobbyhomeaddresslist. So you end up with a collection with a single entry, usually the last one. Instead you should create the collection upfront and after retrieving an item, just add it to that collection.
...snip
...
List<ProvincialState> provincialstate = new List<ProvincialState>();
try
{
foreach (var state in hobbyhomeaddresslist)
{
var list = session.CreateCriteria(typeof(ProvincialState))
.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID))
.List<ProvincialState>();
provincialstate.AddRange(list);
}
transaction.Commit();
}
...
Update: single query using a Disjunction.
IList<ProvincialState> provincialstate = null;
Disjunction dj = new Disjunction();
try
{
foreach (var state in hobbyhomeaddresslist)
{
dj.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID));
}
provincialstate = session.CreateCriteria(typeof(ProvincialState))
.Add(dj)
.List<ProvincialState>();
transaction.Commit();
}
if you look at the generated SQL, you should now see a single select with several where clauses instead of several selects with a single where clause.

Related

Realm.Manage update

How do I update a Manage Realm object?
If I run this twice it fails, saying the object is already managed. I understand the Exception, but how do I update/delete the object after it is managed?
using (var instance2 = Realm.GetInstance())
{
using (var transaction = instance2.BeginWrite())
{
foreach (var item in items)
{
instance2.Manage(item);
}
transaction.Commit();
}
}
There is no need to Manage an object a second time. Once it's been added to the Realm by calling Manage, it becomes a "live" object - every change you make will be persisted. So if you have a list of employees:
var instance = Realm.GetInstance();
using (var transaction = instance.BeginTransaction())
{
foreach (var e in employees)
{
instance.Manage(e);
}
transaction.Commit();
}
And then you want to, say, calculate their salaries, you could simply do:
using (var transaction2 = instance.BeginTransaction())
{
foreach (var e in employees)
{
e.Salary = CalculateSalary(e);
}
transaction2.Commit();
}
As you can see, there's no need to call Manage again, you just have to set whatever properties you want to update within a write transaction.

Many-To-Many Entity Framework Update

I have an object that has a many-to-many relationship with another object. I am trying to write an update statement that doesn't result in having to delete all records from the many-to-many table first.
My data is:
StoredProcedure - StoredProcedureId, Name
Parameter - ParameterId, Name
StoredProcedure_Parameter - StoredProcedureId, ParameterId, Order
I have a UI for updating a stored procedured object (adding/removing parameters or changing the order of the parameters).
When I save, I end up at:
var storedProcedure = context.Sprocs.FirstOrDefault(s => s.SprocID == sproc.StoredProcedureId);
if (storedProcedure == null)
{
//do something like throw an exception
} else
{
storedProcedure.Name = sproc.Name;
//resolve Parameters many to many here
//remove all Params that are not in sproc.Params
//Add any params that are in sproc.Params but not in storedProcedure.Params
//Update the Order number for any that are in both
}
I know I could simply call .Clear() on the table and then reinsert all of the values with their current state (ensuring that all parameters that were removed by the UI are gone, new ones are added, and updated Orders are changed). However, I feel like there must be a better way to do this. Do many-to-many updates with EF usually get resolved by deleting all of the elements and reinserting them?
Here there is my code that I use and it works. The difference is that instead o having your 3 tables( StoredProcedure, StoredProcedure_Parameter and Parameter ) I have the following 3 tables: Order, OrdersItem(this ensure the many-to-many relation) and Item. This is the procedure that I used for updating or add an order, or after I change an existing OrderItem or add a new one to the Order.
public void AddUpdateOrder(Order order)
{
using (var db = new vitalEntities())
{
if (order.OrderId == 0)
{
db.Entry(order).State = EntityState.Added;
}
else
{
foreach (var orderItem in order.OrdersItems)
{
if (orderItem.OrderItemsId == 0)
{
orderItem.Item = null;
if (order.OrderId != 0)
orderItem.OrderId = order.OrderId;
db.Entry(orderItem).State = EntityState.Added;
}
else
{
orderItem.Order = null;
orderItem.Item = null;
db.OrdersItems.Attach(orderItem);
db.Entry(orderItem).State = EntityState.Modified;
}
}
db.Orders.Attach(order);
db.Entry(order).State = EntityState.Modified;
}
SaveChanges(db);
}
}

Linq ResultSet to DataTable

I have a SQL Database which I imported as a ADO.NET Entity Data Model. I then populate a DataGridView using Linq. I extended two of the tables with extra columns that are calculated from other tables. For instance, I have a table Orders that has fields OrderNumber, DateApproved and RequestorID and so on. I also have a table that is the OrderDetails with Fields like SKU, OrderNUmber and QuanityOrdered. I coded a new column IsBackOrdered for the Orders Table that calculates if any Item(SKU) from the OrderDetails is backordered.
When I bound the table Orders to the DataGridView.DataSource everything works as expected. I was then directed to create a search filter for the table.
I tried to map BindingSource to the Linq query but BindingSource is expecting a DataTable. I found a neat little method that converts Linq ResultSet to a DataTable (Code below) however it barfs on my custom fields (Columns) at this line: dr[pi.Name] = pi.GetValue(rec, null) ?? DBNull.Value;
Thanks in advance for any of your view or helpful insights you would care to offer.
public static DataTable LinqToDataTable<T>(IEnumerable<T> varlist)
{
var dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null)
return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = rec.GetType().GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) ?? DBNull.Value;
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
DataSource property of the BindingSource doesn't expect DataTable. It's of type object, so you may use any list as data source. But in this case to be able to filter it you should either implement IBindingListView or use BindingList. The second case is easier of course. Take a look here for more information.

Update object in foreach loop

I am using EF4/LINQ for the first time and have run into an issue. I am looping thru the results of a LINQ query using a foreach loop as follows:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
CallOutcomeSubmission los = new CallOutcomeSubmission();
client = connectToService();
try
{
using (var context = new CallOutcomeContext())
{
// List of available actions
private static string ACTION_CALL_ATTEMPT = "Call Attempt";
DateTime oneDayAgo = DateTime.Now.AddHours(-24);
var query = from co in context.T_MMCallOutcome
join ca in context.T_Call on co.CallID equals ca.CallID
join lv in context.T_LeadVendorEmailHeader on co.LeadVendorEmailID equals lv.LeadVendorEmailID
where co.EnteredOn > oneDayAgo && co.MMLeadActionID == null
select new
{
co.CallOutcomeID,
co.CallID,
co.LeadVendorEmailID,
MMLeadID = lv.email_text,
ca.OutcomeID,
lv.FranchiseNumber,
co.MMLeadActionID,
co.LeadAction
};
// if any results found for query
if (query.Any())
{
foreach (var call in query.ToList())
{
// if the franchise exists
if (client.FranchiseExists(int.Parse(call.FranchiseNumber)))
{
switch (call.OutcomeID)
{
case 39: // Not Answered
call.LeadAction = ACTION_CALL_ATTEMPT;
break;
case 43: // Remove from Call List
break;
default: // If the OutcomeID is not identified in the case statement
break;
} // switch
}
else
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent: No franchise found with franchise ID " + call.FranchiseNumber);
}
// Save any changes currently on context
context.SaveChanges();
} // foreach
}
// if no results found from query write system log stating such
else
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent: No new entries found");
}
} // using
client.Close();
}
catch (System.TimeoutException exception)
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent:" + exception.ToString());
client.Abort();
}
catch (System.ServiceModel.CommunicationException exception)
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent:" + exception.ToString());
client.Abort();
}
}
When I try to do the assignment:
call.LeadAction = ACTION_CALL_ATTEMPT;
I get a build error of
Property or indexer 'AnonymousType#2.LeadAction' cannot be assigned to -- it is read only
I can't seem to find anything on this specific error doing a Google search and am not sure what I am doing wrong. Is it because the original query contains a join?
How can I do the assignment of call.LeadAction within the foreach loop?
I would also like to know if there are design issue withe way I have written the query or performed any of the operations since this is my first foray into EF/LINQ.
You're creating a new anonymous type - with the Linq joins and then trying to set that value. What you're really wanting to do, is update the call's LeadAction correct?
How would EF know to translate your new query back to an entity so it can go back to the database? It would have to go through alot of hoops, and it's not capable of that.
What you could do, is retrieve the Call from your database and set the LeadAction that way - I'm using Find, assuming that CallID is your PK:
case 39: // Not Answered
var thisCall = context.T_Call.Find(call.CallID)
thisCall.LeadAction = ACTION_CALL_ATTEMPT;
break;

Converting an entity model to a dataset - can this be done?

Very frustrated here ...
I can usually find an answer of some kind to complex issues in .Net somewhere on the net, but this one eludes me.
I'm in a scenario where I have to convert the result of a LINQ to Entity query into a DataSet so the data can then be processed by existing business logic, and I can't find a single working solution out ther for this.
I've tried basic approaches like the EntityCommand generating a reader, but this one does not work because DataTable.Load() thorws an excpetion (the reader generated by EntityCommand does not support GetSchemaTable() ).
I've also tried more [supposedly] friendly approaches like Entity to IDataReader(http://l2edatareaderadapter.codeplex.com/), but this one throws exceptions, has very little docs, and hasn't been touched since 2008.
Another approach I found is here (http://blogs.msdn.com/b/alexj/archive/2007/11/27/hydrating-an-entitydatareader-into-a-datatable-part-1.aspx), but does not have a working copy of the code; only snippets.
I find it hard to believe that first of all MS would not have offered this backwards-compatibility item out of the box, and second, that it would not have been created by the community either.
I'm willing to look at commercial solutions as well if any are available.
Thx!
You can convert the result into a list and use the following to convert the list to a datatable.
public DataTable ConvertToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
http://social.msdn.microsoft.com/Forums/br/csharpgeneral/thread/6ffcb247-77fb-40b4-bcba-08ba377ab9db
Hope this helps
Preetam
This might not be the greatest solution, but if your scenario have only one or two table that you need to add to the DataSet, why not build them directly manually.
var result = db.YourTable; // get your Linq To Entities result.
DataSet ds = new DataSet();
DataTable tbl = new DataTable();
tbl.Columns.Add("col1", typeof(string));
tbl.Columns.Add("col2", typeof(int));
foreach (var r in result)
{
var row = tbl.NewRow();
row[0] = r.Col1;
row[1] = r.Col2;
tbl.Rows.Add(r);
}
ds.Tables.Add(tbl);
The Col1 and Col2 comes from your Linq To Entity objects, you can create all the table you need like this and return your DataSet.
This is a flexible code and should handle most of your needs:
public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}

Resources