I've built a nice repository layer around a db4o database to store Product objects, which relate to Manufacturer objects in a one-to-many relationship, i.e.:
public class Manufacturer
{
public string Name { get; set; }
}
public class Product // simplified
{
public string Name { get; set; }
Manufacturer Manufacturer { get; set; }
}
So far I really like db4o. The problem I have is preventing data duplication without resorting to IDs.
When implementing references like manufacturers using SQL Server, my data models would contain a unique ID field, and in turn my Product class would be dirtied up with a ManufacturerID. I imagined that using an object database like db4o would reduce the impedance mismatch between a relational DB and objects, but without IDs, there is no way to tell one object from another when editing it.
Is there an elegant way to share a manufacturer between products without duplicating data? Or should I just use a relational DB?
You can add an unique index to db4o in your config.
configuration.Common.ObjectClass(typeof (Manufacturer)).ObjectField("<Name>k__BackingField").Indexed(true);
configuration.Add(new UniqueFieldValueConstraint(typeof(Manufacturer), "<Name>k__BackingField"));
This way it is not possible to have two different Manufacturer object with the same name in your db. The field name have to be "k__BackingField"because you are using auto properties. Of course you could add an integer ID, and index it the same way.
The basic things first. db4o manages objects by their object-identity. When you store the same object-instance again, db4o will update that object in the database. The same applies to references. When two different objects refer to the same object, they will actually refer to the same object in the database. In your example: When two different product refer to the same Manufacturer-instance, they will also refer to the same Manufacturer in the database. This is achieved by having a table in the back-ground which keeps track of the objects.
Now this approach has issues. As soon as you serialize objects (web-application, web-services etc), or close the object-container, db4o forgets which object in memory belongs to which object. Then it doesn't recognizes a existing object anymore and just stores is as a new object. This means that you should never load and store a object with different object container instance. And that you probably need IDs to recognizes objects. For example to recognize a object across web-requests. A easy solution is to use Guids to give an unique id to an object.
Back to your question. To share the a manufacturer between products you simply point it to the same manufacturer. Like this:
Product newShinyProduct = new Product(); // the new thing
// Get a existing product or manufacturerer, doesn't matter
Product oldProduct = (from p in container.AsQueryable<Product>()
where p.Name == "Get a product"
select p).First();
// now just assigne the existing manufacturer to the new product
// this product will now refer to the same manufacturer
// and db4o will store this that way. The manufacturer isn't doublicated.
newShinyProduct.Manufacturer = oldProduct.Manufacturer;
// store the new product in the database.
container.Store(newShinyProduct);
db4o does maintain a unique internal ID for each object stored.
check these links:
http://community.versant.com/documentation/reference/db4o-8.1/java/reference/Content/basics/identity_concept.htm
http://community.versant.com/documentation/reference/db4o-8.1/java/reference/Content/platform_specific_issues/disconnected_objects/comparison_of_ids.htm
Related
I'm building a simple note app and I currently have a note entity in the database.
Now I want to add a note category in the database and I'm wondering what will be the most appropriate way to create this. It needs to hold a few static records to start with and user should be able to add more later if needed.
I was thinking to store the static default values in enum and make the note category an attribute of the note entity as an integer.
enum NoteCategory: Int32 {
case General, Grocery ....
}
But then, enum is not designed to scale dynamically so it may be not a feasible option for me.
Alternatively, I thought of creating the note category as an entity and make relationship with the note entity with category id associated but in this case I'm not sure how I can store static records to begin with.
What would be the best option that would fit my needs?
I am using entity framework and developing an architecture for application with remote data access. Coming back to point, i query the database for one record (say on the basis of itemcode). Now the resultset i will get whether i should return it as List or collection or simple as an object of entity. I am using entity object but my boss is saying i should use List. He thought , returning result as an entity with return whole table structure also. Quick suggestion would be appreciated.
List<Employee> lstemployee = GetRecordByCode(itemCode)
or
Employee emp = GetRecordByCode(itemCode)
What's the difference? If itemCode is a unique key you will either get one Employee object or a list containing the same one Employee object. You will never return the whole table. That will only happen if within GetRecordByCode you do something like context.Employees.ToList() without any Where filter before the ToList().
If itemCode is not unique you even have to use a list.
Say I have a generic repository interface as follows:
public interface IRepository<T>
{
Add(T item);
Delete(int itemId);
Update(T item);
}
Typically the new ID of an item added through IRepository.Add() would be determined by some back-end database, but only once the overall transaction/unit of work has been submitted. So I'm fairly certain that it would be wrong for IRepository.Add() to return the new ID of the added item. The repository really shouldn't know anything about how ID are created. Is this correct?
If this is a case how else can one determine the new ID of an item added to a repository, or should I even be doing this? I know an ORM like NHibernate is able to automagically replace objects in memory with new objects with the correct ID, but I'm trying to design my repository with out any specific ORM implementation in mind.
For example say I have a website where customers can make orders. A new customer chooses to check out and is sent to a form to fill out their details. This information is used to create a Customer object which is stored in a CustomerRepository. Now their order information needs to be created but an Order needs to reference a Customer by their ID?
Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);
customerRepository.Add(newCustomer);
//How would I determine customerId??
Order newOrder = new Order(customerId, shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");
In the example you give, I would create the Customer and Order in one step, and pass domain objects to domain objects, instead of passing Ids:
Customer newCustomer = new Customer(first, last, address, phone dateOfBirth);
// Pass the customer rather than the CustomerId:
Order newOrder = new Order(newCustomer , shippingAddress, billingAddress);
newOrder.AddOrderItem("widget");
newOrder.AddOrderItem("doohicky");
newOrder.AddOrderItem("stuff");
customerRepository.Add(newCustomer);
orderRepository.Add(newOrder);
// SaveChanges()
...when the changes are saved, the framework automatically populates the Ids of both Customer and Order, and fills in Customer.Id, Order.customerId, (etc.) by virtue of the Customer object having been assigned to the Order.
Eric,
In the scenario you mention, I don't see any CommitChanges() going on. I would wrap everything in a transactionscope and then hit customerRepository.CommitChanges() before you add the orderlines. you should then be able to grab the id from the newly created customer object and use it as follows:
Order newOrder = new Order(newCustomer.Id, shippingAddress, billingAddress);
then, if the order(s) fails, you can roll everything back and keep it atomic by not hitting scope.Complete().
hope this helps..
I generate id on a client (a la CombGuid.NewGuid()) and then pass it to constructor. Approach when you are using database identity has serious disadvantages
Whether or not you use NHibernate, I feel that the approach it takes is the right one. Your goal with any domain objects is to only ever have one instance of that object in memory at any one time, i.e. you should never have two objects that represent the same database record. It follows that if the database has updated the record with a new id, the domain object in memory should also be updated with that ID since that is the "one" true representation of that record.
After calling Add, the object's ID is set and you could then make further changes to that object and call Update without having to know too much about your implementation.
Your interface is more DAO than Repository according to DDD:
http://codebetter.com/iancooper/2011/04/12/repository-saveupdate-is-a-smell/
Like Steve Wilkes mentioned, you should keep reference of Customer in Order and not Customer Id so when Unit of Work is processed, it will create correct link those Entities in Persistence Storage (SQL DB, Web Service etc)
For more on DAO here: http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Domain entities should have their own ID strategy regardless database IDs, so preferably generate your id in the domain layer, or if you really need to generate id in database, then add another domain identifier generated at domain layer beside the database auto generated id.
In domain driven design where you apply repository pattern you should not tie your domain with database so relying on database for id creation is not a good idea.
Another point is that you may want to make customer associated in order not just putting the customer id, this makes the domain layer rich and solid.
My current setup:
I have an entity object with some properties, among them an int Id. To communicate with the database, I've created a repository with, among others, the following method:
public int Add(TEntity entity)
{
ObjectSet.AddObject(entity);
return entity.Id;
}
(It's a generic repository, that requires that TEntity is a class, and that it implements IEntity, which is just an interface with an int Id property.)
My problem:
Now, when I want to create a new entity for adding to the repository, I need to give it an id. However, that doesn't allow EF to automatically generate the id for me, since it will already have a value.
Possible solutions:
I have only been able to think of these two possibilities.
Make the Id property nullable
Pass an EntryInputModel to the repository instead, and then do the mapping there.
Currently I'm binding to EntryInputModel in my Controller, and mapping it to an Entry using AutoMapper. If I need to change this, I also need to re-think the dependencies in my project, since the ...InputModel and ...ViewModel classes are currently only available to my Web application.
Which is preferable? Are there more ways to counter this?
If you're using SQL Server as the backend, my recommended solution would be to make the ID column an INT IDENTITY on the database, and a not-nullable column in your entity object.
When adding a new entity, assign the ID some arbitrary value, like -1 or something, or no value at all, even. The actual ID will be generated automatically by SQL Server when you insert the new entity (
EntityContext.AddToEntities(newEntity);
EntityContext.SaveChanges();
and it will be automagically be signalled back to EF so you'll be able to use it right away once it's inserted:
int newEntityID = newEntity.ID;
Works like a charm - at least in my test apps :-)
you can have generated unique id's that are not generated by the datastore/EF, allowing you to define them before passing the object into EF... (think of Guid's)
Similar situation to : How to add several dependent records with LINQ2SQL
(this seems logical, but it doesn't work for me)
ASP.NET MVC + Linq2SQL
I have 2 tables called Challenges and Participants.
Challenge { challengeId,
ChallengeDesc, applicantId,
respondantId }
Participants { participantId, FirstName, LastName }
There are 1-many relationships between participants and Challenges - 1 for each key (applicantId, RespondantId).
I have an input form that collects all fields to create a new applicant and respondant and the challenge. The Databinder binds all the fields to Challenge and it's child participants correctly, however, the participant objects' names don't match the key names in the challenge object when browsing the structure created by Linq2Sql (applicantId matches with Participant object and respondantId matches with Participant1 object).
So when i try to SubmitChanges() after InsertOnSubmit(aChallenge) i get a foreign_key constraint validation message back from Linq2Sql. In SQL Server Profiler, I can see that the participants are being created properly, but when the challenge is being saved, the IDs of these newly inserted participants are not being set to the challenge object so the system is throwing a foreign key violation message.
How do i get past this?
You have to write it this way I think (assuming you have classes Participant and Challenge):
Participant applicant = new Participant();
Participant respondant = new Participant();
//update your participants here
Challenge insertedChallenge = new Challenge();
//update your challenge values here
applicant.Challenges.add(insertedChallenge);
respondant.Challenges1.add(insertedChallenge);
submitChanges();
Linq-to-SQL should automatically assign these properties (Challenges and Challenges) so it can set the key values for you.
Hope it helps.
You might want to edit you data objects (normally by using the DBML designer) and rename the Participant-typed properties to Applicant and Respondent respectively. It'll be easier to work with than having Participant and Participant1. You can do this in the association properties (the lines that connect the tables).
When you want to assign the foreign keys in Challenge, you have two choices. If you have the Participant objects themselves, you can assign them to the (newly renamed) Applicant and Respondent properties (and LINQ to SQL will update ApplicantID or RespondentID accordingly). Or if you have the ParticipantIDs, you can assign them to ApplicantID or RespondentID directly.