Repository remove method not working - Object not found - c#

public class Hardware
{
public int id { get; set; }
public int Nodes { get; set; }
public int Repeaters { get; set; }
public int Hubs { get; set; }
}
public abstract class Repositories<T> where T:class
{
//where T:class fixes the issue with .SET
//Repository class constructor once initialized will instantiate datamodel
//Manufacturing data model instantiate because it models table
ManufacturingDataModel MDM;
public Repositories( ManufacturingDataModel mdm)
{
MDM = mdm;
}
public List<T> GetHardware()
{
//Creating new list and adding data from db in it
List<T> data = new List<T>();
foreach(var i in MDM.Set<T>())
{
data.Add(i);
}
return data;
}
public void AddHardware(T item)
{
MDM.Set<T>().Add(item);
MDM.SaveChanges();
}
public void RemoveHardware(T item)
{
MDM.Set<T>().Remove(item);
MDM.SaveChanges();
}
public void UpdateHardware(T item)
{
MDM.Set<T>().AddOrUpdate(item);
MDM.SaveChanges();
}
public class Test : Repositories<Hardware>
{
public Test(ManufacturingDataModel mdm) : base(mdm)
{
}
static void Main(string[] args)
{
ManufacturingDataModel MDM = new ManufacturingDataModel();
Test t = new Test(MDM);
Hardware hardware = new Hardware();
hardware.Nodes = 55;
hardware.Repeaters = 46;
hardware.Hubs = 82;
t.AddHardware(hardware); // WORKS
t.RemoveHardware(hardware); //DOES NOT WORK
I'm trying to make a working repository to add/remove items from my db table. The issue I'm facing is that the add method works perfectly but the remove does not. The issue with the remove method as shown in the picture is that the object does not exist.
I've tried different things such as change my remove method to DeleteObject and Attach but nothing seems to be working.
Any ideas?
ADO --> code first --> EF --> Repo class

If you want make it work, once the object has been inserted retrieve it from the database and pass it in the RemoveHardware method.
As you are passing an object without the primary key, you are getting that error.

You have first to fetch the item you want to remove and the remove it:
public void RemoveHardware(T item)
{
var dbItem = MDM.Set<T>().FirstOrDefault(x=>x.Id == item.Id);
if(dbItem!=null)
{
MDM.Set<T>().Remove(dbItem);
MDM.SaveChanges();
}
}
Update
As it is stated here, DbSet.Remove
Marks the given entity as Deleted such that it will be deleted from
the database when SaveChanges is called. Note that the entity must
exist in the context in some other state before this method is called.
In your case the item is not in your context and this is the reason you see the following error:
The object cannot be deleted because it was not found in the ObjectStateManager
If the item was in the context then as correctly Rainman has pointed out in his comment below, we woulnd't have to make the extra roundtrip to the database to fetch the item.

You need to set unique Id (mostly primary key) for Hardware entity.
It is something like that;
Hardware hardware = new Hardware();
hardware.Id = 1 //Set your unique Id here
hardware.Nodes = 55; //Not necessary
hardware.Repeaters = 46; //Not necessary
hardware.Hubs = 82; //Not necessary
RemoveHardware(hardware);
// So edit your RemoveHardware method too like this
public void RemoveHardware(T item)
{
MDM.Set<T>().Attach(item);
MDM.Set<T>().Remove(item);
MDM.SaveChanges();
}

Related

Create CSV with columns selected by the user

I have a little design problem. Let's say I have a project that contains a large number of people. I want to allow the user to export those people to a CSV file with the information he chooses.
For example, He could choose Id, Name, Phone number and according to his choice I would create the file.
Of course, there is a simple of way doing it like if(idCheckBox.Checked) getId(); etc.
I'm looking for something better. I don't want that for each new option I would like to add I would need to change the UI (e.g. New checkbox).
I thought of reading the possible options from a file, but that will only solved the UI problem. How would I know which values to get without using all those "if's" again?
You don't need a fancy design pattern for this task. However I understand you have identified a reason to change (added options in future). So you want to minimize amount of classes to be modified.
Your real problem is how to decouple CSV creation from the objects whose structure is going to change. You don't want your parsing logic to be affected whenever your Person class is changed.
In the following example the CSV object is truly decoupled from the objects it receives and parses. To achieve this, we are coding to an abstraction rather to an implementation. This way we are not even coupled to the Person object, but will welcome any objects that implement the AttributedObject interface. This dependency is being injected to our CSV parser.
I implemented this in PHP, but the idea is the same. C# is a static language, so fetching the attributes would be with a bit of change. You might use some kind of ArrayAccess interface.
interface AttributedObject {
public function getAttribute($attribute);
}
class Person implements AttributedObject {
protected $firstName;
protected $lastName;
protected $age;
protected $IQ;
public function __construct($firstName, $lastName, $age, $IQ)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->age = $age;
$this->IQ = $IQ;
}
public function getAttribute($attribute)
{
if(property_exists($this, $attribute)) {
return $this->$attribute;
}
throw new \Exception("Invalid attribute");
}
}
class CSV {
protected $attributedObject = null;
protected $attributesToDisplay = null;
protected $csvRepresentation = null;
protected $delimiter = null;
public function __construct(AttributedObject $attributedObject, array $attributesToDisplay, $delimiter = '|')
{
$this->attributedObject = $attributedObject;
$this->attributesToDisplay = $attributesToDisplay;
$this->delimiter = $delimiter;
$this->generateCSV();
}
protected function generateCSV()
{
$tempCSV = null;
foreach ($this->attributesToDisplay as $attribute) {
$tempCSV[] = $this->attributedObject->getAttribute($attribute);
}
$this->csvRepresentation = $tempCSV;
}
public function storeCSV()
{
$file = fopen("tmp.csv", "w");
fputcsv($file, $this->csvRepresentation, $this->delimiter);
}
}
$person1 = new Person('John', 'Doe', 30, 0);
$csv = new CSV($person1, array('firstName', 'age', 'IQ'));
$csv->storeCSV();
You can build a mapping set of fields based what fields the user is allowed to select, and which fields are required. This data can be read from a file or database. Your import/export can be as flexible as needed.
Here is a conceivable data structure that could hold info for your import/export sets.
public class FieldDefinition
{
public FieldDataTypeEnum DataType { get; set; }
public string FieldName{get;set;}
public int MaxSize { get; set; }
public bool Required { get; set; }
public bool AllowNull { get; set; }
public int FieldIndex { get; set; }
public bool CompositeKey { get; set; }
}
public class BaseImportSet
{
private List<FieldDefinition> FieldDefinitions { get; set; }
protected virtual void PerformImportRecord(Fields selectedfields)
{
throw new ConfigurationException("Import set is not properly configured to import record.");
}
protected virtual void PerformExportRecord(Fields selectedfields)
{
throw new ConfigurationException("Export set is not properly configured to import record.");
}
public LoadFieldDefinitionsFromFile(string filename)
{
//Implement reading from file
}
}
public class UserImportSet : BaseImportSet
{
public override void PerformImportRecord(Fields selectedfields)
{
//read in data one record at a time based on a loop in base class
}
public override string PerformExportRecord(Fields selectedfields)
{
//read out data one record at a time based on a loop in base class
}
}

How do I add a new table to a Telerik Open Access MVC project?

I've inherited a MVC project that seems to use Telerik Open Access to handle data instead of using something I'm more familiar with like entity framework. I'm trying to understand the whole concept of how to work with this data method, but right now I'm just needing to find out how to add a table. I've limited my code examples to one table, but in reality there are dozens of them.
So I see that the class OpenAccessContext.cs has a database connection string, but it also has a IQueryable item made up of the class tblMaterial. The tblMaterial class is defined in tblMaterial.cs. I don't understand how this class is connected to the SQL database version of tblMaterial (so feel free to educate me on that).
I have a table called tblContacts in the SQL database. What do I need to do to connect it to my project? There's no "update from database" option when I right click any object in the solution (because they're all just classes). Will I need to create a new class manually called tblContacts.cs? If so, how do I connect it to the database version of tblContacts? Am I going to need to manually change multiple classes to add the table (OpenAccessContext, MetadataSources, Repository, etc.)?
I tried to keep this as one simple question (how do I add a table) so I don't get dinged, but any light you can shine on the Telerik Open Access would be helpful. (Please don't ding me for asking that!) I checked out the Telerik documentation here: http://docs.telerik.com/data-access/developers-guide/code-only-mapping/getting-started/fluent-mapping-getting-started-fluent-mapping-api , but it's related to setting up a new open access solution. I need to know how to modify one (without ruining the already working code). Thank you in advance for your help!
Here's the solution as seen in Visual Studio:
Open Access
Properties
References
OpenAccessContext.cs
OpenAccessMetadataSources.cs
Repository.cs
tblMaterial.cs
Here's the code:
OpenAccessContext.cs
namespace OpenAccess
{
public partial class OpenAccessContext : OpenAccessContext
{
static MetadataContainer metadataContainer = new OpenAccessMetadataSource().GetModel();
static BackendConfiguration backendConfiguration = new BackendConfiguration()
{
Backend = "mssql"
};
private static string DbConnection = ConfigurationManager.ConnectionStrings["ConnString"].ConnectionString;
private static int entity = ConfigurationManager.AppSettings["Entity"] == "" ? 0 : int.Parse(ConfigurationManager.AppSettings["Entity"]);
public OpenAccessContext() : base(DbConnection, backendConfiguration, metadataContainer)
{
}
public IQueryable<tblMaterial> tblMaterials
{
get
{
return this.GetAll<tblMaterial>(); //.Where(a => a.EntityId == entity);
}
}
}
}
OpenAccessMetadataSources.cs
namespace OpenAccess
{
public class OpenAccessMetadataSource : FluentMetadataSource
{
protected override IList<MappingConfiguration> PrepareMapping()
{
var configurations = new List<MappingConfiguration>();
// tblMaterial
var materialConfiguration = new MappingConfiguration<tblMaterial>();
materialConfiguration.MapType(x => new
{
MaterialId = x.MaterialId,
MaterialName = x.MaterialName,
MaterialDescription = x.MaterialDescription,
MaterialActive = x.MaterialActive,
MaterialUsageType = x.MaterialUsageType,
AddDate = x.AddDate,
AddBy = x.AddBy,
ModDate = x.ModDate,
ModBy = x.ModBy
}).ToTable("tblMaterial");
materialConfiguration.HasProperty(x => x.MaterialId).IsIdentity(KeyGenerator.Autoinc);
}
}
}
Repository.cs
namespace OpenAccess
{
public class Repository : IRepository
{
#region private variables
private static OpenAccessContext dat = null;
#endregion private varibles
#region public constructor
/// <summary>
/// Constructor
/// </summary>
public Repository()
{
if (dat == null)
{
dat = new OpenAccessContext();
}
}
#endregion public constructor
#region Material (tblMaterials)
public int CreateMaterial(tblMaterial itm)
{
try
{
dat.Add(itm);
dat.SaveChanges();
return itm.MaterialId;
}
catch (Exception)
{
return 0;
}
}
}
}
tblMaterial.cs
namespace OpenAccess
{
public class tblMaterial
{
public int MaterialId { get; set; }
public string MaterialName { get; set; }
public string MaterialDescription { get; set; }
public bool MaterialActive { get; set; }
public int MaterialUsageType { get; set; }
public DateTime? AddDate { get; set; }
public string AddBy { get; set; }
public DateTime? ModDate { get; set; }
public string ModBy { get; set; }
}
}
In the case of tblContacts, I would suggest to you the following workflow for extending the model:
Add a new class file that will hold the definition of the tblContact POCO class. In this class add properties that will correspond to the columns of the table. The types of the properties should logically match the datatypes of the table columns.
In the OpenAccessMetadataSource class, add a new MappingConfiguration<tblContact> for the tblContact class and using explicit mapping provide the mapping details that logically connect the tblContact class with the tblContacts table. Make sure to add both the existing and the new mapping configurations to the configurations list.
Expose the newly added class through an IQueryable<tblContact> property in the context. This property will allow you to compose LINQ queries against the tblContacts table.
Regarding the Repository class, it seems like it is related to the custom logic of the application. It surely is not a file generated by Data Access. Therefore, you need to discuss it in your team.
I also strongly advise you against using OpenAccess in the namespaces of your application. This is known to interfere with the Data Access' namespaces during build time and at some point it causes runtime errors.
I hope this helps.

How do you get EntityFramework 6.1 to support Filtering and two way Binding like adapter.Fill(table) does?

I am trying to get EntityFramework 6.1.1 to return an ObserveableCollection or an IEnumerable that supports both Filtering and Sorting and Binding (Two-way) in a WinForm. I am not at all concerned what format the Entity is as the end as long as it is Bindable and supports Filtering and Sorting. I guess how adapter.Fill(table) worked but just with EntityFramwork as the backbone.
If have tried returning a _context.Orders.Local.ToBindingList();
But the associated OrderDetails entities are not Sortable nor can I Filter them.
If I attach a Where expression to Orders it Converts it to IEnumerable and I cannot Filter them nor can I edit them now.
Would a generic repository pattern help in this case? If so, would you please respond with code-examples?
Here is my simplified Order Class:
public class Orders
{
public Orders()
{
Details = new HashSet<OrderDetails>();
}
[Key]
public int OrderId {get; set}
public int CustomerId {get; set;}
public decimal OrderTotal { get; set;}
public DateTime OrderDate {get; set;}
public virtual ICollection<OrderDetails> Details {get; set;}
public virtual Customer Customer {get; set;}
}
I have a ClassLibrary with the following three methods I have tried. The InitDatabase() Method Loads the Entities if they are not already Loaded.
Method #1:
public static ObservableCollection<ZSA_TransactionDetail> GetAllDetailsAsObservableCollection()
{
InitDatabase();
return NContext.ZSA_TransactionDetail.Local;
}
Method #2:
public static ICollection<ZSA_TransactionDetail> GetAllDetailsAsICollection(int orderNumber = 1022)
{
InitDatabase();
ICollection<ZSA_TransactionDetail> iCollection = null;
ObservableCollection<ZSA_TransactionHeader> headers = NContext.ZSA_TransactionHeader.Local;
ZSA_TransactionHeader myHeader = headers.FirstOrDefault(i => i.intTransactionNumber == orderNumber);
if (myHeader != null)
{
iCollection = myHeader.ZSA_TransactionDetail;
}
if (iCollection != null)
Debug.WriteLine("iCollection Record Count was: " + iCollection.Count);
else
Debug.WriteLine("iCollection was null");
return iCollection;
}
Method #3:
public static BindingList<ZSA_TransactionDetail> GetAllDetailsAsBindingList(int orderNumber = -1)
{
InitDatabase();
if (orderNumber != -1 && IsValidTransaction(orderNumber))
{
BindingList<ZSA_TransactionDetail> details = NContext.ZSA_TransactionDetail.Local.ToBindingList();
var myDetails =
details.Where(i => i.intTransactionNumber == orderNumber) as BindingList<ZSA_TransactionDetail>;
if (myDetails != null)
{
Debug.WriteLine("Number of details: " + myDetails.Count);
return myDetails;
}
throw new Exception("Did not find any details");
}
Debug.WriteLine("Not a valid Transaction Number");
return NContext.ZSA_TransactionDetail.Local.ToBindingList();
}
My Form_Load Event looks like this when calling Method #2:
private void FormPick_Load(object sender, EventArgs e)
{
ICollection<ZSA_TransactionDetail> data = Library.GetAllDetailsAsICollection(_transactionNumber);
PickingBindingSource.RaiseListChangedEvents = true;
PickingBindingSource.DataSource = data;
Debug.WriteLine("Supports Filter: " + PickingBindingSource.SupportsFiltering);
PickingGridView.DataSource = PickingBindingSource;
ApplyFilter();
}
All three Methods report SupportsFiltering false.
EDIT: to better explain
The local property on the context set is an observable collection.
Would a generic repository pattern help in this case?
Yes , this is a common pattern in such case.
So using a generic Repository<T> pattern.
Use a Method like:
public ObservableCollection<TPoco> Local {
get { return Context.Set<TPoco>().Local; }
}
This gives you access to context loaded objects for the POCO in question.
Use your normal approach To populate prior to accessing the Local objects.
Also check out Context.Configuration.AutoDetectChangesEnabled = <bool>;
try a simple test case where you load a few objects.
Then access the objects via the local property, make a simple change or 2
and SaveChanges().
Then try with the collection bound.

Accessing custom objects in DomainService from client

I am using Domain Service to fetch data from database from Silverlight Client.
In DomainService1.cs, I have added the following:
[EnableClientAccess()]
public class Product
{
public int productID;
public string productName;
public List<Part> Parts = new List<Part>(); //Part is already present in Model designer
}
In DomainService1 class I added a new method to retrive a collection of the custom class object:
[EnableClientAccess()]
public class DomainService1 : LinqToEntitiesDomainService<HELPERDBNEWEntities1>
{
...
public List<Product> GetProductsList(...)
{
List<Product> resultProducts = new List<Product>();
...
return resultProducts;
}
}
From the silverlight client I am trying to access that method:
DomainService1 ds1 = new DomainService1();
var allproductList = ds1.GetProductsList(...);
ds1.Load<SLProduct>(allproductList).Completed += new EventHandler(Load_Completed); //Not correct usage
However it is not the correct way to call the new method. The reason I added a new class Product in DomainServices.cs is to have an efficient grouping. I cannot achieve the same using the model classes auto-generated by the entity framework.
How call I call the new method from the client?
I believe there is a similar question with an answer here:
Can a DomainService return a single custom type?
Also, here is some discussion about the overall problem of adding custom methods in a Domain Service:
http://forums.silverlight.net/t/159292.aspx/1
While I don't know what you mean by "it is not the correct way to call the new method", or if you're getting any errors, I thought maybe posting some working code might help.
My POCO
public class GraphPointWithMeta
{
[Key]
public Guid PK { get; set; }
public string SeriesName { get; set; }
public string EntityName { get; set; }
public double Amount { get; set; }
public GraphPointWithMeta(string seriesName, string entityName, double amount)
{
PK = Guid.NewGuid();
SeriesName = seriesName;
EntityName = entityName;
Amount = amount;
}
// Default ctor required.
public GraphPointWithMeta()
{
PK = Guid.NewGuid();
}
}
A method in the domain service (EnableClientAccess decorates the class)
public IEnumerable<GraphPointWithMeta> CallingActivityByCommercial()
{
List<GraphPointWithMeta> gps = new List<GraphPointWithMeta>();
// ...
return gps;
}
Called from the Silverlight client like
ctx1.Load(ctx1.CallingActivityByCommercialQuery(), CallingActivityCompleted, null);
client call back method
private void CallingActivityCompleted(LoadOperation<GraphPointWithMeta> lo)
{
// lo.Entities is an IEnumerable<GraphPointWithMeta>
}
I am not sure if your Product class is an actual entity or not. From the way it is defined, it does not appear to be an entity. My answer is assuming it is not an entity. You will need to apply the DataMemberAttribute for your Product properties, and you wouldn't load the product list - load is for Entity Queries (IQueryable on the service side). You would just invoke it like this (client side):
void GetProductList( Action<InvokeOperation<List<Product>>> callback)
{
DomainService ds1 = new DomainService();
ds1.GetProductsList(callback, null);//invoke operation call
}
And the domain service's (server side) method needs the InvokeAttribute and would look like this:
[EnableClientAccess]
public class MyDomainService
{
[Invoke]
public List<Product> GetProductList()
{
var list = new List<Product>();
...
return list;
}
}
And here is how your Product class might be defined (if it is not an entity):
public class Product
{
[DataMember]
public int productID;
[DataMember]
public string productName;
[DataMember]
public List<Part> Parts = new List<Part>(); // you might have some trouble here.
//not sure if any other attributes are needed for Parts,
//since you said this is an entity; also not sure if you
//can even have a list of entities or it needs to be an
//entity collection or what it needs to be. You might
//have to make two separate calls - one to get the products
//and then one to get the parts.
}
Like I said, i am not sure what Product inherits from... Hope this helps.

SubmitChanges not "submitting" in LINQ to SQL

I'm creating an application using WPF, MVVM and LINQ to SQL. I have a collection of notes on an object of type Calculation. I have therefore created a ViewModel-class for this called vmCalculation. My problem is, that when I try to add a note to this object and submit the changes the "note" isn't submittet to the database.
Content of vmCalculation
public class vmCalculation : vmBase
{
Calculation calc;
public ObservableCollection<Note> Notes { get; private set; }
public vmCalculation(Calculation calc)
{
this.calc = calc;
Notes = new ObservableCollection<Note>();
foreach (var n in calc.Notes) Notes.Add(n);
}
public void AddNote()
{
Notes.Add(new Note
{
NoteText = "New note",
NoteType = 1
});
}
internal void Save()
{
foreach (var n in Notes.Where(n => n.NoteId == 0))
calc.Notes.Add(n);
}
}
Method in vmNotes (ViewModel for the "NoteWindow")
public void SaveChanges()
{
CurrentCalc.Save();
DC.SubmitChanges();
}
CurrentCalc is a property that gets/sets a vmCalculation that I use in the databinding (binding a DataGrid to CurrentCalc.Notes).
When I run AddNote() on CurrentCalc the view is updated just fine with a "New note"-note. But, when I run SaveChanges() the note isn't written to the database.
Any thoughts on this problem?
A possible cause for the the problem could be, that I don't initialize the DataContext (DC) in vmNotes. I get the DataContext from another ViewModel so that I don't destroy the MVVM-structure.
You must add your new entities to the datacontext before you submit it.
Example:
DC.Notes.InsertOnSubmit(NewNote);
DC.SubmitChanges();
Thought of a possible solution for my problem.
I updated the SaveChanges()-method on the vmNotes class a bit.
public void SaveChanges()
{
var newNotes = currentCalc.Notes.Where(n => n.NoteId == 0);
DC.Notes.InsertAllOnSubmit(newNotes);
DC.SubmitChanges();
}
}
UPDATE 03/09/2011:
Above code is not needed anyway.
I discovered that I had multiple (and static) instances of my DataModel-class.
I cut away some of these and now my original code works just fine!

Categories

Resources