Adding new Item to CSLA EF4 pattern - c#

I'm adding a new item to my CSLA BusinessList. But I can only add it with a 0 primary key because my items which I add is always null. And if I try: "Item temp= new item{...}" is this item not child of the list.
I'll post my Add function first and then some dataportals.
public void ExecuteNew(object obj)
{
if (Model != null)
{
Temp = Model.AddNew();
//Temp.FarbauswahlNr = 123;
//Temp.Kurztext = this.Kurztext;
//Temp.Ressource = this.Ressource;
//Temp.Vari1 = this.Vari1;
Model = Model.Save();
}
}
Now some DataPortals from my Business class and BusinessList class
protected override void DataPortal_Update()
{
using (var ctx = Csla.Data.ObjectContextManager<Datenbank.TestDBEntities>.GetManager(Business.EntitiesDatabase.Name))
{
Child_Update();
}
}
protected override void Child_Create()
{
base.DataPortal_Create();
BusinessRules.CheckRules();
}
private void Child_Insert()
{
using (var ctx = Csla.Data.ObjectContextManager<TestDBEntities>.GetManager(EntitiesDatabase.Name))
{
try
{
var data = new Datenbank.Farbe();
data.Kurztext = ReadProperty<string>(KurztextProperty);
data.Ressource = ReadProperty<string>(RessourceProperty);
data.Var1 = ReadProperty<bool>(Vari1Property);
data.Vari2 = ReadProperty<string>(Vari2Property);
ctx.ObjectContext.Farben.AddObject(data);
ctx.ObjectContext.SaveChanges();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
Possible Errors :
NullReferebceException was unhadeld ( because temp is always null )

The Problem was: CSLA 4.5 has got a bug wich let your UI Freeze after the Save Command. Now im using CSLA 4.1 and it works just fine.

Related

Deep cloning an object when items could be null

I tried this code to copy values from another object a deep clone but it doesnt seem to like nullable properties and I cannot figure out why
public static TConvert ConvertTo<TConvert>(this object entity) where TConvert : new()
{
var convertProperties = TypeDescriptor.GetProperties(typeof(TConvert)).Cast<PropertyDescriptor>();
var entityProperties = TypeDescriptor.GetProperties(entity).Cast<PropertyDescriptor>();
var convert = new TConvert();
foreach (var entityProperty in entityProperties)
{
var property = entityProperty;
var convertProperty = convertProperties.FirstOrDefault(prop => prop.Name == property.Name);
if (convertProperty != null)
{
convertProperty.SetValue(convert,
Convert.ChangeType(entityProperty.GetValue(entity),
convertProperty.PropertyType)); }
}
return convert;
}
I please a try catch around it and it brought me to this property which does exist in both models.
public int? PullUpHolds { get; set; }
How would I modify the above to take that into account I tried removing the if statment but that still caused a exception on the clone.
My Usuage is
private async void btnEndSession_Clicked(object sender, EventArgs e)
{
var item = dgWeightLifting.SelectedItem as WeightLifting;
if(item != null)
{
var removePlayer= await DisplayAlert(Constants.AppName,
$"This will remove the player {item.Players.FullName}
from the weight lifting screen. We will make a final
record for this session in work out history proceed",
"OK", "Cancel");
if (removePlayer)
{
var test =item.ConvertTo<Workout>();
}
}
}

Linq to SQL - Can changes persist over different DataContext instances

If I retrieve an object from the database using Linq to SQL in one method using an instance of DataContext, which closes on exit of that method, can I edit the object in a different method and with a different DataContext and have the changes take effect in the database?
i.e. Would something like the below work?
public void Foo()
{
using (var db = new DataContext())
{
Bar a = this.GetBar();
if (a != null)
{
a.Property1 = true;
db.SubmitChanges();
}
}
}
private Bar GetBar(string val)
{
using (var db = new DataContext())
{
return db.FirstOrDefault(x => x.Property2 == val);
}
}
There should be some kind of Attach method
Something like:
public void Foo()
{
using (var db = new DataContext())
{
Bar a = this.GetBar();
if (a != null)
{
db.Bars.Attach(a);
a.Property1 = true;
db.SubmitChanges();
}
}
}

How to insert/update master-detail in Entity Framework?

I'm trying to make a master-detail Web Form working with Entity Framework and performing insert and update on the same page. I'm new at EF, so I must be making a lot of mistakes here. Can you help me pointing me what's the best practices to perform insert/update on EF? What am I doing wrong here?
In this code, the "New" mode works well, but the "Edit" mode gets this error: "An entity object cannot be referenced by multiple instances of IEntityChangeTracker".
OrdersEntities ordersEntities = new OrdersEntities();
private Order myOrder
{
get { return (Order)Session["myOrder"]; }
set { Session["myOrder"] = value; }
}
public DataTable dtOrderDetails
{
get { return (DataTable)ViewState["dtOrderDetails"]; }
set { ViewState["dtOrderDetails"] = value; }
}
private string Mode
{
get { return (string)ViewState["mode"]; }
set { ViewState["_modo"] = value; }
}
private void btnSaveOrder_Click(object sender, EventArgs e)
{
if (dtOrderDetails.Rows.Count > 0)
{
using (ordersEntities)
{
using (var contextTransaction = ordersEntities.Database.BeginTransaction())
{
try
{
if (Mode == "New")
{
Order newOrder = new Order();
OrderDetails newOrderDetails;
int maxOrderNumber = ordersEntities.Order.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
ordersEntities.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(newOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = myOrder;
OrderDetails editedOrderDetails;
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
ordersEntities.Order.Attach(editedOrder);
ordersEntities.Entry(editedOrder).State = System.Data.Entity.EntityState.Modified;
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(editedOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
}
}
catch (Exception ex)
{
contextTransaction.Rollback();
}
}
}
}
}
Here is how you should approach it.
It would be best if you abstract the DbContext away, with this simple interface:
public interface IDataRepository : IDisposable
{
IDbSet<Order> Orders { get; set; }
void Save();
}
Of course, your implementation of IDataRepository is based on EntityFramework. Note that you will need to have a dataRepositoryConnection connection string in your web.config file:
public class EfDataRepository : DbContext, IDataRepository
{
public EfDataRepository() : base("dataRepositoryConnection")
{
}
public IDbSet<Order> Orders { get; set; }
public void Save()
{
this.SaveChanges();
}
}
In my experience, you also need a 'factory', which gives you a new instance of the data repository. This allows you to be the 'owner' of the instance, and you can safely dispose it. Note that the interaction with the DataContext should be minimal - you do your Unity of Work and get rid of it. Don't reuse! You will see it as an example below.
public class DataRepositoryFactory<T> where T : IDataRepository
{
private Type dataRepositoryImplementationType;
public DataRepositoryFactory(T dataRepositoryImplementation)
{
if (dataRepositoryImplementation == null)
{
throw new ArgumentException("dataRepositoryImplementation");
}
this.dataRepositoryImplementationType = dataRepositoryImplementation.GetType();
}
public T Create()
{
return (T)Activator.CreateInstance(this.dataRepositoryImplementationType);
}
}
In your controller (if it were MVC app), or Page backend (forms), it would be best if you use Microsoft Unity to get an instance of DataRepositoryFactory. For now, a manual construction would suffice too.
IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);
Also, you don't need all this Transaction/Commit stuff you have put. It should be transparent for you. EF supports it implicitly, you don't have to be explicit about it.
// See, now you are the 'owner' of the dataRepository
using (var dataRepository = this.dataRepositoryFactory.Create())
{
if (Mode == "New")
{
Order newOrder = new Order();
// This doesn't make sense. Either generate a random order number (e.g. a Guid), or just use the Order.Id as an order number, although I don't recommend it.
int maxOrderNumber = dataRepository.Orders.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
dataRepository.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
newOrder.OrderDetails.Add(newOrderDetails);
}
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = dataRepository.Orders.FirstOrDefault(o => o.Id == myOrder.Id);
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
editedOrder.OrderDetails.Add(editedOrderDetails);
}
}
dataRepository.Save();
}
Also, I am pretty sure you have setup the relation between Order and OrderDetails classes incorrectly, in your EF code-first approach.
This is just wrong:
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
If you post them here, I can fix them for you.

Is there a way to iterate through an EF6 Entity?

I have a web form with around 50 fields that is used for crud operations on an Oracle DB, I am using EF6.
Currently, I accomplish this like so:
private GENERIC_FTP_SEND GetFields()
{
GENERIC_FTP_SEND ftpPartner = new GENERIC_FTP_SEND();
//Contact Info
ftpPartner.FTP_LOOKUP_ID = FTP_LOOKUP_IDTB.Text;
ftpPartner.PARTNER_NAME = PARTNER_NAMETB.Text;
ftpPartner.REMEDY_QUEUE = REMEDY_QUEUETB.Text;
ftpPartner.PRIORITY = PRIORITYBtns.SelectedValue;
ftpPartner.CONTACT_EMAIL = CONTACT_EMAILTB.Text;
ftpPartner.CONTACT_NAME = CONTACT_NAMETB.Text;
ftpPartner.CONTACT_PHONE = CONTACT_PHONETB.Text;
...
}
where GENERIC_FTP_SEND is the name of the virtual DbSet in my Model.context.cs.
This works fine but is not reusable in the least. What I would like to accomplish is to have some code that allows me to iterate through the attributes of ftpPartner and compare them to the field id for a match. Something like this:
var n =0;
foreach (Control cntrl in ControlList){
if(cntrl.ID == ftpPartner[n]){
ftpPartner[n] = cntrl.Text;
}
n++;
}
In case you need/want to see it here is my Model.context.cs
public partial class Entities : DbContext{
public Entities(): base("name=Entities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<GENERIC_FTP_SEND> GENERIC_FTP_SEND { get; set; }
}
I saw the question here but I am not sure how to implement that in my case.
Entity Framework 6: is there a way to iterate through a table without holding each row in memory
You can achieve that with reflection:
var type = typeof(GENERIC_FTP_SEND);
foreach (Control cntrl in ControlList){
Object value = null;
if (cntrl is TextBox){
value = (cntrl as TextBox).Text;
} else if (cntrl is GroupBox){
value = (cntrl as GroupBox).SelectedValue;
} //etc ...
PropertyInfo pInfo = type.GetProperty(cntrl.ID);
if (pInfo != null && value != null){
pInfo.SetValue(ftpPartner, value, null);
}
}
You can also use the Entity Framework context object as well to accomplish the same if you know you are going to insert.
var x = new GENERIC_FTP_SEND();
// Add it to your context immediately
ctx.GENERIC_FTP_SEND.Add(x);
// Then something along these lines
foreach (Control cntrl in ControlList)
{
ctx.Entry(x).Property(cntrl.Name).CurrentValue = ctrl.Text;
}

Why does this not work?

I have a class which will act as variables to store data from textboxes:
public class Business
{
Int64 _businessID = new Int64();
int _businessNo = new int();
string _businessName;
string _businessDescription;
public Int64 BusinessID
{
get { return Convert.ToInt64(_businessID.ToString()); }
}
public int BusinessNo
{
get { return _businessNo; }
set { _businessNo = value; }
}
public string BusinessName
{
get { return _businessName; }
set { _businessName = value; }
}
public string BusinessDescription
{
get { return _businessDescription; }
set { _businessDescription = value; }
}
I then have the code to store the data from the textbox into a session and into a list (there can be many businesses uploaded to the database at one time) - database irrelevent for now. I then want to display the list of businesses stored into the session onto the gridview: (b = class business)
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
businessCollection.Add(b);
GridView1.DataSource = GetBusiness();
GridView1.DataBind();
}
It doesn't seem to add the list to the gridview, can someone help?
Debug your code and ensure that if (Session["Business"] != null) actually evaluates to true.
If it is false then you are adding to a list that is never returned from GetBusinesss
Without any more information you can rewrite it like this:
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
return businessCollection;
else
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
I personally wouldn't do it like this, as it seems like you need an assignment to Session["Business"] but I don't want to change the logic of your code.
Update
I wanted to update this with what I think you wanted to accomplish.
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
Session["Business"] = new List<Business>();
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
It seems you are not assigning anything to Session["Business"]
There's a very strong chance that you're problem is caused by the fact that you are referencing the Business List object inconsistently. You've created an accessor for this object, so use it everywhere.
This:
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Should be:
var businessCollection = GetBusiness();
Note the use of var: I suspect defining businessCollection as a member variable is part of the problem. In any case it is bad design if your intent is to store the list in the session. So I would also remove the member declaration for businessCollection and stick with a locally scoped variable.

Categories