The issue is that I can't understand how to make a OneToOne relation between two objects the way for the first object to have a link to the second and for the second to have a link to the first. Here's the code:
[MapTo("Model")]
public class Model : CSObject<Model, int>
{
[OneToOne(LocalKey = "ModelID", ForeignKey = "ModelID")]
public Product Product { get { return (Product)GetField ("Product"); } set { SetField ("Product", value); } }
}
[MapTo("Product")]
public class Product : CSObject<Product, int>
{
[OneToOne(LocalKey = "ProductID", ForeignKey = "ProductID")]
public Model Model { get { return (Model)GetField ("Model"); } set { SetField ("Model", value); } }
}
The thing is that when I create a product and a model and set the model's property "Product" to the created one and save it, the product's "Model" property doesn't get set and remains NULL. I've already tried making all the local and foreign keys for both Product's and Model's properties the same (e.g. "ModelID") but it didn't solve the problem. What is the right way of doing this?
I guess making one of them [OneToMany] will do the trick but will return a collection while I need a single object to be returned by a property.
Update
Here comes a simple solution one would call a crutch:
[OneToMany]
public CSList<Product> _ProductList { get { return (CSList<Product>)GetField ("_ProductList"); } set { SetField ("_ProductList", value); } }
[NotMapped]
public Product Product {
get {
CSList<Product> list = this._ProductList;
if (list.Count > 0)
return list [0];
return null;
}
set {
if (value != null) {
CSList<Product> list = this._ProductList;
list.RemoveAll ();
list.Add (value);
}
}
}
You can make both relations [ManyToOne]. That will work in your scenario.
Related
I want to prevent documents from being deleted in my project and I decided to use metadata to mark document as Archived. I used below code to do that:
public class DeleteDocumentListener : IDocumentDeleteListener
{
public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
{
metadata.Add("Archived", true);
throw new NotSupportedException();
}
}
After that I wanted to alter query to return only documents which have Archived metadata value set to false:
using (var session = _store.OpenSession())
{
var query = session.Advanced.DocumentQuery<Cutter>()
.WhereEquals("#metadata.Archived", false);
}
Unfortunately this query return empty result set. It occurs that if Document doesn't have this metadata property then above condition is treated as false. It wasn't what I expected.
How can I compose query to return Documents which don't have metadata property or this property has some value ?
You can solve it by creating an index for you Cutter documents and then query against that:
public class ArchivedIndex : AbstractIndexCreationTask<Cutter>
{
public class QueryModel
{
public bool Archived { get; set; }
}
public ArchivedIndex()
{
Map = documents => from doc in documents
select new QueryModel
{
Archived = MetadataFor(doc)["Archived"] != null && MetadataFor(doc).Value<bool>("Archived")
};
}
}
Then query it like this:
using (var session = documentStore.OpenSession())
{
var cutters = session.Query<ArchivedIndex.QueryModel, ArchivedIndex>()
.Where(x => x.Archived == false)
.OfType<Cutter>()
.ToList();
}
Hope this helps!
Quick side note. To create the index, the following code may need to be run:
new ArchivedIndex().Execute(session.Advanced.DocumentStore);
All of my entities (that are mapped to a database table) inherit from an entity class with a dynamic component on it called Attributes e.g.:
public abstract class Entity<T> {
public virtual T Id { get; set; }
private IDictionary _attributes;
public virtual IDictionary Attributes {
get { return _attributes ?? (_attributes = new Hashtable()); }
set { _attributes = value; }
}
}
The Attributes collection allows me to add extra fields to each entity without directly changing the entity itself. This allows me to make my application more modular.
For example say I have the following entity:
public class User : Entity<int> {
public virtual string Name { get; set; }
}
Now say I have a Forum module which needs a NumPosts property against the User. I would add the field against the Users table in the database. This field is non nullable and has a default value of 0. I then map the field using the dynamic component against the User entity.
However when I try inserting the user by saying:
session.Save(new User() { Name = "Test" });
It throws an error as it's expecting me to set a value for NumPosts and the generated SQL would be something like:
INSERT INTO Users (Name, NumPosts) VALUES ('Test', NULL)
However NumPosts does not allow nulls and hence the error. Ideally I'd like it to say the following if the Attributes collection does not contain an entry for NumPosts:
INSERT INTO Users (Name) VALUES ('Test')
An alternative is to say the following which would work fine:
session.Save(new User() { Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } } });
The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this.
For reference here's a bare bones version of session factory method which maps the NumPosts field:
return Fluently.Configure()
...
.ExposeConfiguration(c => {
// Get the persistent class
var persistentClass = c.GetClassMapping("User");
// Create the attributes component
var component = new Component(persistentClass);
// Create a simple value
var simpleValue = new SimpleValue(persistentClass.Table);
// Set the type name
simpleValue.TypeName = "Int32";
// Create a new db column specification
var column = new Column("NumPosts");
column.Value = simpleValue;
column.Length = 10;
column.IsNullable = false;
column.DefaultValue = "0";
// Add the column to the value
simpleValue.AddColumn(column);
// Ad the value to the component
component.AddProperty(new Property() { Name = column.Name, Value = simpleValue });
// Add the component property
persistentClass.AddProperty(new Property() { Name = "Attributes", Value = component });
})
.BuildConfiguration();
I'd appreciate if someone could let me know if this is possible. Thanks
You know how to make it working as described above:
... An alternative is to say the following which would work fine:
session.Save(new User()
{
Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } }
});
... The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this...
In case, that the biggest issue is the explicit Attributes initialization ("...I don't want the modules to have a dependency...") we can use:
12.2. Event system
So, with Listener like this:
[Serializable]
public class MyPersistListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(SaveOrUpdateEvent #event)
{
var entity = #event.Entity as Entity<int>; // some interface IHaveAttributes
if (entity == null) // would be more appropriate
{
return;
}
var numPosts = entity.Attributes["NumPosts"] as int?;
if (numPosts.HasValue)
{
return;
}
entity.Attributes["NumPosts"] = 0;
}
}
Based on this doc snippet:
Configuration cfg = new Configuration();
ILoadEventListener[] stack = new ILoadEventListener[] { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners.LoadEventListeners = stack;
This should be the init in our case:
.ExposeConfiguration(c => {
var stack = new ISaveOrUpdateEventListener [] { new MyPersistListener() };
c.EventListeners.SaveEventListeners= stack;
I'm a new user in LINQ to SQL and I have some problems using it.
I've used LINQ to SQL Designer and I have created my classes, mapped on the DB tables.
In particular, I have one class, named voice:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.voce")]
public partial class voce : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _id_voce;
... other private fields;
private int _category;
private EntityRef<category> _category1;
public voce()
{
this._riepilogo = new EntitySet<riepilogo>(new Action<riepilogo>(this.attach_riepilogo), new Action<riepilogo>(this.detach_riepilogo));
this._hera = default(EntityRef<hera>);
this._category1 = default(EntityRef<category>);
OnCreated();
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id_voce", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
public int id_voce
{
get
{
return this._id_voce;
}
set
{
if ((this._id_voce != value))
{
this.Onid_voceChanging(value);
this.SendPropertyChanging();
this._id_voce = value;
this.SendPropertyChanged("id_voce");
this.Onid_voceChanged();
}
}
}
......
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_category", DbType="Int NOT NULL")]
public int category
{
get
{
return this._category;
}
set
{
if ((this._category != value))
{
if (this._category1.HasLoadedOrAssignedValue)
{
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
this.OncategoryChanging(value);
this.SendPropertyChanging();
this._category = value;
this.SendPropertyChanged("category");
this.OncategoryChanged();
}
}
}
As you can see, voce class has a field named category that refers to a table named category.
When I add a new voce to my database, I create a new voce istance and, using the DataContext, i simply add it, using:
voce v = new voce(){...field, category1 = //create or retrieve category};
In particular, the category field is retrieved from the DB if already exists or, if not, it is inserted, before I insert the voice.
The problem is that when I add the voice in the database:
datacontext.InsertOnSubmit(v);
datacontext.SubmitChanges();
it inserts the category again, failing with the unique contraint.
How can I add a voice without adding every nested object?
Thank you and sorry for my bad English.
internal category GetCategoryFromDescription (string desc, Utility.VOICE_MODALITY mode)
{
bool type = mode == Utility.VOICE_MODALITY.ENTRATA ? true : false;
var query = from cat in dc.category
where cat.description == desc && cat.type == type
select cat;
if (query.Count() == 0)
{
category newC = new category() { description = desc };
dc.category.InsertOnSubmit(newC);
dc.SubmitChanges();
return newC;
}
else
return query.Single();
}
I've been Following this post To get my head around Lazy field of T, Which I think I understand, But I'm having trouble getting associated Field Data for a Part loaded this way
Aim - To show photo of blog post author on a blog post.
I want to add a content part "Content Author"
The part Editor should appear as a drop down list of orchard users.
(regardless of the content owner cms users should be able to pick the author)
I have added an image upload field to the User Content Type
I want to show the image of the user on the front end in the view for the Content Author Part
For the first part I have created the content type and used the lazy Filed of UserPart to get the username. However when I try and get the associated fields for the UserPart. There dosent seem to be any.
public class ContentAuthorRecord : ContentPartRecord
{
public virtual string AuthorEmail { get; set; }
}
public class ContentAuthorPart : ContentPart<ContentAuthorRecord>
{
internal readonly LazyField<UserPart> Owner = new LazyField<UserPart>();
public string AuthorEmail
{
get { return Record.AuthorEmail; }
set { Record.AuthorEmail = value; }
}
public UserPart Author
{
get { return Owner.Value; }
set { Owner.Value = value; }
}
public string AuthorName
{
get
{
if (Author == null)
return "Riders for health";
else
{
return Author.UserName;
}
}
}
}
public class ContentAuthorHandler :ContentHandler
{
private readonly IContentManager _contentManager;
public ContentAuthorHandler(IRepository<ContentAuthorRecord> repository, IContentManager contentManager)
{
_contentManager = contentManager;
OnActivated<ContentAuthorPart>(SetUpCustomPart);
Filters.Add(StorageFilter.For(repository));
}
private void SetUpCustomPart(ActivatedContentContext content, ContentAuthorPart part)
{
// Setup the getter of the lazy field
part.Owner.Loader(() => _contentManager.Query<UserPart, UserPartRecord>().List().FirstOrDefault(x => x.Email == part.AuthorEmail));
}
}
I would expect to be able to access the field with something like
(ImageUploadField.Fields.ImageUploadField)Author.Fields.FirstOrDefault(x
=> x.Name == "Photo");
form the within the part class
( although this makes every thing a bit brittle, hard coding a field name, but I'm not sure how eles to go about it)
Further Info
I have a HeaderPart with a Image field added via the cms (not in code) in the display handler I fetch the field like this
protected override DriverResult Display(HeaderPart part, string displayType, dynamic shapeHelper)
{
if (part.HeaderType == HeaderType.Full_width_hero_image)
{
var field = (ImageUploadField) part.Fields.FirstOrDefault(f => f.Name == "HeaderImage");
if (field != null)
{
return ContentShape("Parts_Header_ImageHero",
() => shapeHelper.Parts_Header_ImageHero(ImagePath: field.ImagePath, ImageTitle: field.FileName));
}
return null;
}
if (part.HeaderType == HeaderType.Full_width_hero_video)
{
return ContentShape("Parts_Header_VideoHero", () => shapeHelper.Parts_Header_VideoHero(VideoUrl: part.VideoUrl));
}
if (part.HeaderType == HeaderType.Body_width_video)
{
return ContentShape("Parts_Header_VideoBody", () => shapeHelper.Parts_Header_VideoBody(VideoUrl: part.VideoUrl));
}
return null;
}
This works, But I can do the same for a part loaded into a lazy field.
Cast to dynamic first, then the syntax becomes much simpler: ((dynamic)part.ContentItem).NameOfTheType.NameOfTheField.NameOfTheProperty
If you have added the fields to the User content type via the CMS interface, it may have added the fields to a different part to the one you expect. If you are adding fields to the User content type, by default it will have added the fields to a new part called 'User', not 'UserPart'. Try to following to search all parts in the content item:
(ImageUploadField.Fields.ImageUploadField)Author.ContentItem.Parts
.SelectMany(p => p.Fields).FirstOrDefault(f => f.Name == "Photo");
or directly from the 'User' part:
(ImageUploadField.Fields.ImageUploadField)Author.ContentItem.Parts
.First(p => p.PartDefinition.Name == p.ContentItem.ContentType).Fields
.FirstOrDefault(f => f.Name == "Photo");
I am having the difficulty to understand NHibernate an petapoco loading mechanism. Actually I did a test to compare how both behave upon a query.
My class is as follows:
UserTest.cs with the following properties:
private string name;
private int id;
private int customerId;
public int ID
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public int? CustomerID
{
get { return customerId; }
set
{
if (value != customerId)
{
customerId = value;
if (this.ID > 0)
{
DoSomeOtherWork();
}
}
}
}
When I do a User.Load in NHibernate, I have observed that DoSomeOtherWork is never called whereas in PetaPoco, when I do a query from loading User such as Connection.db.Fetch<UserTest>(...) or Connection.db.Query<UserTest>(...), I can see that DoSomeOtherWork is called.
Why is that so?
Is there a way to avoid calling DoSomeOherWork when using PetaPoco such that it has the same behaviour as NHibernate? I dont want to usePetaPoco.Ignoreas I need to get and set theCustomerID`.
PetaPoco it a micro-ORM (much lighter than Nhibernate) and materializes your POCO object when you fetch the record. There is no other magic than that, so the answer is:
No, you can't avoid calling the setter of the property.