I'm Using EF6 Code First (Generic Repository Concepts)
Example Code TO Create Record :
Generic.cs:
public T Create<T>(T t) where T : class
{
T newEntry = this.dbContext.Set<T>().Add(t);
this.SaveChanges();
return newEntry;
}
Actual Code :
var customer = new Customer{
Name = "Adams",
Amount = 1000
};
CustomerId is AutoIdentity Column It will Generate Id automatically.
Customer table having One To Many RelationShip with Products(CustomerID FK in Products Table)
Now I'm adding New CustomerID in the List of Products
var productsList = new List<Product>
{
new Product
{
ProdName = "crocin",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
},
new Product
{
ProdName = "crocin1",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
},
new Product
{
ProdName = "crocin2",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
}
};
Now I'm hitting database twice because First I'm getting CustomerId then I'm assigning New CustomerId to all the Selected Products.
Here is the code :
var newCustomer = Generic.Create<Customer>(customer);
Iterating the list of products and I'm assinging the newCustomerId into Products..
foreach(var product in productList)
{
product.CustomerId = newCustomerId;
Generic.Create<Product>(product);
}
Is it possible to change this code to more Generic way of implementing soemthing like this..
Create(T parentTable, List<T> childTableList)
{
// First Inserting ParentTable
db.SaveChanges();
//Fetching ID
// Inserting Into ChildTables..
db.SaveChanges();
}
Please suggest me to extend this code...
The problem is that you're missing the Customer -> Products association. Try to add a reference to Products from your Customer class:
public class Customer
{
public Customer()
{
this.Products = new List<Product>();
}
// your key
public int CustomerId { get; set; }
public string Name { get; set; }
public int Amount { get; set; }
// ... rest of your data
// the new association
public virtual ICollection<Product> Products { get; set; }
}
Now you can add them both in one call:
var productsList = new List<Product>
{
new Product
{
ProdName = "crocin",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
},
new Product
{
ProdName = "crocin1",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
},
new Product
{
ProdName = "crocin2",
Date = DateTime.Parse("2003-09-01"),
Customer.CustomerId = newCustomerId //
}
};
var newCustomer = new Customer();
foreach (var product in productsList)
newCustomer.Products.Add(product);
Then adding them to the context:
var newCustomer = Generic.Create<Customer>(newCustomer);
Related
I have a list of name. I need to find which name is present in FirstName or LastName Property of Document in Collection.
I had tried the Linq query to archive this but it through error ("Object reference not set to an instance of an object.").
public class UserDoc
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DocumentType { get { return "userdoc"; } private set { } }
}
List<string> Names = new List<string>() { "satya", "singh" };
IEnumerable<UserDoc> Users = await _dBRepository.GetItemsAsync<UserDoc>
(x => (Names.Contains(x.FirstName + " " + x.LastName))&& x.DocumentType == "userdoc");
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate) where T : class
{
IDocumentQuery<T> query = _client.CreateDocumentQuery<T>(documentCollectionUri:
UriFactory.CreateDocumentCollectionUri(databaseId: _databaseId, collectionId: _collectionId),
feedOptions: new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
According to your description. You code should be modified as below:
IEnumerable<UserDoc> Users = await _dBRepository.GetItemsAsync<UserDoc>
(x => (Names.Contains(x.FirstName)|| Names.Contains(x.LastName))&& x.DocumentType == "userdoc");
I have tested the code and it worked.
According to your error message, i think the the most likely reason is that the object _client point to Null, please check it and try again.
I can see you can do stuff like this in ORMLite:
var customer = new Customer {
Name = "Customer 1",
PrimaryAddress = new CustomerAddress {
AddressLine1 = "1 Australia Street",
Country = "Australia"
},
Orders = new[] {
new Order { LineItem = "Line 1", Qty = 1, Cost = 1.99m },
new Order { LineItem = "Line 2", Qty = 2, Cost = 2.99m },
}.ToList(),
};
db.Save(customer, references:true);
But what about updating child collections?
How is this done?
To expand on this.
I have a UserAccount class and an Image class internal to UserAccount:
public class UserAccount
{
[AutoIncrement]
public int Id {
get ;
set;
}
public UserAccount()
{
Images = new List<UserImage>();
}
public List<UserImage> Images { get; protected set; }
public UserImage Image { get; set; }
public class UserImage
{
public UserImage()
{
Created = DateTime.Now;
}
public UserImage(string name)
{
Value = name;
Created = DateTime.Now;
}
public string Value { get; set; }
}
}
Doing this:
var fullImage = new UserAccount.UserImage(newImageUrl);
fullImage.IsDefault = true;
user.Image = fullImage;
db.Update (fullImage);
Doesn't work.
Doing this:
var fullImage = new UserAccount.UserImage(newImageUrl);
fullImage.IsDefault = true;
user.Images.Add(fullImage);
db.Update (fullImage);
Doesn't work...
Also doing the same but having image as a seperate entity with it's own ID and setting reference on the parent class doesn't work?
I think this is the way:
var image = new UserImage(newImageUrl);
image.IsDefault = true;
db.Save (image);
var images = user.Images;
images.Add (image);
db.Update (new UserAccount{Id = user.Id, Image = image, Images = images});
I hope I am wrong because that is so ugly :/
[UPDATE]
I have it looking like this now which is much nicer:
db.UpdateOnly (user,
onlyFields: a=> new { a.Image, a.Images},
where: ua => ua.Id == user.Id);
I have created the following example to test Foreign Keys and up to this point, it works well. What I would like to be able to do, is use this framework that I built to set the property of the relationship and have it Save the child object when the Parent is saved and automatically set the PrimaryKey and Foreign Key.
The DataManager class exposes the Connection
public class DataManager
{
DataManager()
{
OrmLiteConfig.DialectProvider = SqliteDialect.Provider;
ConnectionString = SqliteFileDb;
updateTables();
}
private void updateTables()
{
using (var dbConn = OpenDbConnection())
{
dbConn.DropAndCreateTable<Person>();
dbConn.DropAndCreateTable<PhoneNumber>();
}
}
public static string SqliteFileDb = "~/App_Data/db.sqlite".MapAbsolutePath();
private static DataManager manager;
public static DataManager Manager {
get
{
if (manager == null)
manager = new DataManager();
return manager;
}
}
public IDbConnection InMemoryDbConnection { get; set; }
public IDbConnection OpenDbConnection(string connString = null)
{
connString = ConnectionString;
return connString.OpenDbConnection();
}
protected virtual string ConnectionString { get; set; }
protected virtual string GetFileConnectionString()
{
var connectionString = SqliteFileDb;
return connectionString;
}
}
These are my POCO's with the BaseClass used to Achieve my results:
public class Person : LiteBase
{
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
private List<PhoneNumber> numbers;
public List<PhoneNumber> PhoneNumbers {
get
{
if (numbers == null)
numbers = GetList<PhoneNumber>(p => p.Person == Id);
return numbers;
}
}
}
public class PhoneNumber
{
public string Number { get; set; }
public string Description { get; set; }
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[References(typeof (Person))]
public int Person { get; set; }
public void AddPerson(Person person)
{
Person = person.Id;
}
}
public class LiteBase:INotifyPropertyChanged
{
public List<T> GetList<T>(Expression< Func<T,bool>> thefunction) where T : new()
{
var objects = new List<T>();
using (var conn = Data.DataManager.Manager.OpenDbConnection())
{
objects = conn.Where<T>(thefunction);
}
return objects;
}
public T GetItem<T>(Expression<Func<T, bool>> thefunction) where T : new()
{
T obj = new T();
using (var conn = Data.DataManager.Manager.OpenDbConnection())
{
obj = conn.Where<T>(thefunction).FirstOrDefault<T>();
}
return obj;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Simple Class to Create Person and PhoneNumber objects
public class PersonManager
{
public void CreatePerson(string name, string surname, string number)
{
using (var conn = DataManager.Manager.OpenDbConnection())
{
var pnum = new PhoneNumber { Number = number };
var person = new Person
{
Name=name,
Surname = surname,
};
conn.Save<Person>(person);
var id = conn.GetLastInsertId();
person.Id = (int)id;
pnum.AddPerson(person);
conn.Save<PhoneNumber>(pnum);
}
}
public List<Person> GetPeople()
{
List<Person> people;
using (var conn = DataManager.Manager.OpenDbConnection())
{
people = conn.Select<Person>();
}
return people;
}
public List<PhoneNumber> GetNumbers()
{
List<PhoneNumber> numbers;
using (var conn = DataManager.Manager.OpenDbConnection())
{
numbers = conn.Select<PhoneNumber>();
}
return numbers;
}
}
And here is the usage:
var manager = new PersonManager();
manager.CreatePerson("John", "Doe", "12345679");
manager.CreatePerson("Jack", "Smith", "12345679");
manager.CreatePerson("Peter", "Jones", "12345679");
manager.CreatePerson("Dan", "Hardy", "12345679");
var people = manager.GetPeople();
var numbers = manager.GetNumbers();
for (int i = 0; i < people.Count; i++)
{
Console.WriteLine("{0} {1} {2}",
people[i].Name,people[i].Surname,people[i].Id);
}
for (int n = 0; n < numbers.Count; n++)
{
Console.WriteLine("PN: {0} {1}",
numbers[n].Number,numbers[n].Person);
}
for (int p = 0; p < people.Count; p++)
{
var person = people[p];
Console.WriteLine("{0}: {1} {2} {3}",
person.Id,person.Name,person.Surname,person.GetItem<PhoneNumber>(x=>x.Person==person.Id).Number);
}
The output is as I expected :
John Doe 1
Jack Smith 2
Peter Jones 3
Dan Hardy 4
PN: 12345679 1
PN: 12345679 2
PN: 12345679 3
PN: 12345679 4
1: John Doe 12345679
2: Jack Smith 12345679
3: Peter Jones 12345679
4: Dan Hardy 12345679
What I really would like to be able to do is the following:
var john = new Person
{
Name = "John",
Surname = "Smith",
PhoneNumber = new PhoneNumber { Number = "123456789" }
};
conn.Save<Person>(john);
var number = john.PhoneNumber.Number
Is this at all possible?
By default OrmLite v3 blobs all complex types properties in a string field and you need to explicitly set all references.
In the next major v4 release (ETA late Nov 2013), OrmLite adds some support for external references with the [Reference] attribute, which lets you tell OrmLite these properties should be stored in an external table and not blobbed, e.g:
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[Reference]
public CustomerAddress PrimaryAddress { get; set; }
[Reference]
public List<Order> Orders { get; set; }
}
This will allow you to call db.SaveReferences() to save the reference properties, e.g:
var customer = new Customer
{
Name = "Customer 1",
PrimaryAddress = new CustomerAddress {
AddressLine1 = "1 Humpty Street",
City = "Humpty Doo",
State = "Northern Territory",
Country = "Australia"
},
Orders = new[] {
new Order { LineItem = "Line 1", Qty = 1, Cost = 1.99m },
new Order { LineItem = "Line 2", Qty = 2, Cost = 2.99m },
}.ToList(),
};
Assert.That(customer.Id, Is.EqualTo(0)); //Id is not saved yet
//Inserts customer, populates auto-incrementing customer.Id
//Specify `references:true` to populate the ForeignKey ids and
//save the related rows as well, e.g:
db.Save(customer, references:true);
Assert.That(customer.Id, Is.GreaterThan(0));
Assert.That(customer.PrimaryAddress.CustomerId, Is.EqualTo(customer.Id));
Assert.That(customer.Orders.All(x => x.CustomerId == customer.Id));
Saving References manually
For more fine-grained control you can also choose which references you want to save, e.g:
db.Save(customer); //Doesn't save related rows
//1:1 PrimaryAddress Reference not saved yet
Assert.That(customer.PrimaryAddress.CustomerId, Is.EqualTo(0));
//1:1 PrimaryAddress Reference saved and ForeignKey id populated
db.SaveReferences(customer, customer.PrimaryAddress);
//1:Many Orders References saved and ForeignKey ids populated
db.SaveReferences(customer, customer.Orders);
Loading all related rows with the entity
You can then load the master row and all its references with db.LoadSingleById, e.g:
var dbCustomer = db.LoadSingleById<Customer>(customer.Id);
dbCustomer.PrintDump();
Assert.That(dbCustomer.PrimaryAddress, Is.Not.Null);
Assert.That(dbCustomer.Orders.Count, Is.EqualTo(2));
I see numerous examples on foreach collection reference type property update, but not quite what I am struggling with. What if you want to update a property of a property of the reference type item? like so:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public EmployeeType EmpType { get; set; }
}
public class EmployeeType
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Class1
{
private IList<Employee> existingEmp;
public void edit()
{
var dbEmployees = GetExistingEmployees();
IList<Employee> employees = new List<Employee> {
new Employee{ Id = 1, Name="me", EmpType = new EmployeeType { Id = 1}},
new Employee{ Id = 2, Name="me again", EmpType = new EmployeeType { Id = 2}}
};
foreach (var emp in employees)
{
foreach (var oldEmp in dbEmployees)
{
if (emp.Id == oldEmp.Id)
{
UpdateChanges(emp, oldEmp);
existingEmp.Add(oldEmp);
}
}
}
}
private void UpdateChanges(Employee emp, Employee oldEmp){
if (oldEmp.EmpType.Id != emp.EmpType.Id)
{
LogChange();
oldEmp.EmpType.Id = emp.EmpType.Id;
}
}
private void LogChange()
{
throw new NotImplementedException();
}
//data access layer
public IList<Employee> GetExistingEmployees()
{
throw new NotImplementedException();
}
}
The issue here is the last employee in the collection if his/her employee type property Id changed in a ddl, updating it will cascade to all other employees' emp type in the collection. That is nutts. Due to the logging requirement I can not use lambda or other fancy construct. I need hep with fixing this with in foreach or for loops.
EDIT:
As expected the same code structure works somewhere else in my application. I don't get the last item's property updating bleeding to other items' properties.
I solved this using a hacky approach:
private void UpdateChanges(Employee emp, Employee oldEmp){
var oldEmpTemp = GetEmployeeById(oldEmp.Id);
if (oldEmp.EmpType.Id != emp.EmpType.Id)
{
LogChange();
oldEmpTemp.EmpType.Id = emp.EmpType.Id;
}
//instead of updating the collection items
// and bulk updating in the db, update directly in the db
UpdateEmployee(oldEmpTemp);
}
But still can't explain why it's not working for this instance.
I am trying to use Neo4jClient (and am new to C#) to build and retrieve data from Neo4j. First, I build the items and relations to a search:
NodeReference<Search> searchNode = client.Create(searches[i]);
itmNode = client.Create(items[j], new IRelationshipAllowingParticipantNode<Item>[0],
new[]
{
new IndexEntry("Item")
{
{"Type", items[j].Type },
{"ItemDescription", items[j].ItemDescription },
{"ItemNumber", items[j].ItemNumber }
}
});
client.CreateRelationship(itmNode, new SearchedFor(searchNode, 1));
Then, I am testing the retrieval of the node back from Neo4j:
var results = client.Cypher.Start("n", itemDict[firstitem])
.Match("n-[r]->()<-[r2]-other")
.Return<Node<Item>>("other")
.Results;
var node6 = ((IRawGraphClient)client).ExecuteGetCypherResults<Node<Item>>(new Neo4jClient.Cypher.CypherQuery("start n=node(6) return n;", null,Neo4jClient.Cypher.CypherResultMode.Set)).Select(un => un.Data);
"results" returns the 2 nodes that are related to node(6). "node6" is other code I found that I thought would return node 6. Both of these return the nodes, but the properties returned are all blank. I can see the properties in the Neo4j Monitoring Tool, but not when they are returned using Neo4jClient. Am I missing something in how I am setting up the nodes, or on how I am retrieving the data?
My object return shows Data.ItemDescription="", Data.ItemNumber=0, Reference=Node 5
Adding the "Select(un => un.Data)" after .Results did not work like I saw in other examples like this
Please let me know if you need more information.
Neo4jClient version 1.0.0.579
Neo4j version 1.8.2
Here is the item class:
public class Item
{
private string _name;
private string _desc;
private long _id;
public Item(string name, string desc, long id)
{
_name = name;
_desc = desc;
_id = id;
}
public Item()
{
_name = "";
_desc = "";
_id = 0;
}
public long ItemNumber
{
get
{
return _id;
}
}
public string ItemDescription
{
get
{
return _desc;
}
}
public string Type
{
get
{
return "Item";
}
}
}
Making it work
The issue is that your Item class has no setters exposed. There's no way for us to set those properties , so we ignore them.
You can delete half your code, and it'll work. :)
public class Item
{
public long ItemNumber { get; set; }
public string ItemDescription { get; set; }
public string Type { get ; set; }
}
Making it better
Replace this:
NodeReference<Search> searchNode = client.Create(searches[i]);
itmNode = client.Create(items[j], new IRelationshipAllowingParticipantNode<Item>[0],
new[]
{
new IndexEntry("Item")
{
{"Type", items[j].Type },
{"ItemDescription", items[j].ItemDescription },
{"ItemNumber", items[j].ItemNumber }
}
});
client.CreateRelationship(itmNode, new SearchedFor(searchNode, 1));
with this:
var searchNode = client.Create(searches[i]);
var itemNode = client.Create(
items[j],
new[] { new SearchedFor(searchNode, 1) }
new[]
{
new IndexEntry("Item")
{
{"Type", items[j].Type },
{"ItemDescription", items[j].ItemDescription },
{"ItemNumber", items[j].ItemNumber }
}
});
That will create that second node, and the relationship, in a single call.