I'm using Castle ActiveRecord 3.0 and I have a problem with object references when fetching them from the database. When I'm fetching multiple entities within the same SessionScope I get the same object references for related entities.But often i can't use the same session scope to fetch all the objects I need. It's kinda hard to describe it so here's an example:
[ActiveRecord(Table = "car")]
public class Car : ActiveRecordLinqBase<Car>
{
[PrimaryKey("id")]
public virtual int Id { get; set; }
[OneToOne(PropertyRef = "Car", Cascade = CascadeEnum.SaveUpdate)]
public virtual Location Location { get; set; }
}
[ActiveRecord("location")]
public class Location : ActiveRecordLinqBase<Location>
{
[PrimaryKey(Column = "id")]
public virtual int Id { get; set; }
[BelongsTo("car_id", NotFoundBehaviour = NotFoundBehaviour.Ignore,
Cascade = CascadeEnum.SaveUpdate)]
public virtual Car Car { get; set; }
}
[ActiveRecord(Table = "move_order")]
public class MoveOrder : ActiveRecordLinqBase<MoveOrder>
{
[PrimaryKey(Column = "id")]
public virtual int Id { get; set; }
[BelongsTo(Column = "car_id")]
public virtual Car Car { get; set; }
[BelongsTo(Column = "destination_id")]
public virtual Location Destination { get; set; }
}
I'm creating some objects to start with:
using (new SessionScope())
{
var car = new Car() { };
var initialLocation = new Location() { Car = car };
initialLocation.Save();
var destinationLocation = new Location();
destinationLocation.Save();
}
Then if I create 2 move orders for the same Car/Location it works just fine:
MoveOrder moveOrder;
MoveOrder moveOrder2;
using (new SessionScope())
{
moveOrder = new MoveOrder()
{
Destination = Location.Queryable.First(x => x.Car == null),
Car = Car.Queryable.First();
};
moveOrder2 = new MoveOrder()
{
Destination = Location.Queryable.First(x => x.Car == null),
Car = Car.Queryable.First()
};
}
Object references are equal for:
moveOrder.Car, moveOrder2.Car
moveOrder.Location, moveOrder2.Location
But when the I use different session scopes to get both MoveOrders:
using (new SessionScope())
{
moveOrder = new MoveOrder()
{
Destination = Location.Queryable.First(x => x.Car == null),
Car = Car.Queryable.First()
};
}
and
using (new SessionScope())
{
moveOrder2 = new MoveOrder()
{
Destination = Location.Queryable.First(x => x.Car == null),
Car = Car.Queryable.First()
};
}
None of those properties are reference equal, but logically those are the same entities.
I also tried with getting Nhibernate session and associating all detached objects back into new session:
using (var sess = GetSession())
{
sess.Lock(moveOrder.Destination, LockMode.Read);
sess.Lock(moveOrder.Car, LockMode.Read);
moveOrder2 = new MoveOrder()
{
Destination = sess.Get<Location>(moveOrder.Destination.Id),
Car = sess.Get<Car>(moveOrder.Car.Id),
};
}
This does the trick in this case, but has some issues:
I have to pass EVERY database entity I currently use to the new session
I cannot use queries, session.Get works for Ids
The main question is:
How can I achieve getting the same object references from DB every time I fetch the same entities (same Ids)?
Also tried to use Nhibernate second level cache by configuring it in AR init and adding Cache = CacheEnum.ReadWrite to every db class ActiveRecord attribute - with no result.
I'd rather not keep the same session open all time.
Related
In the following sample console program, AutoMapper generates 3 DestinationAuthor objects, even though the source hierarchy only uses 2 distinct SourceAuthor objects (but one of them twice).
What I am looking for is to let AutoMapper generate only one distinct destination object for every distinct source object, and then reference this one destination object as many times as necessary during mapping, instead of creating duplicates.
The result would be an object hierarchy, where all destination objects and references mimic exactly the ones from the source hierarchy.
using System.Diagnostics;
using System.Linq;
using AutoMapper;
namespace AutomapperMapOnce
{
public class SourceBlog
{
public int BlogId { get; set; }
public string Title { get; set; }
public SourceAuthor Author { get; set; }
}
public class SourceAuthor
{
public int AuthorId { get; set; }
public string Name { get; set; }
}
public class DestinationBlog
{
public int BlogId { get; set; }
public string Title { get; set; }
public DestinationAuthor Author { get; set; }
}
public class DestinationAuthor
{
public int AuthorId { get; set; }
public string Name { get; set; }
}
internal static class Program
{
private static SourceBlog[] GetSourceBlogs()
{
var sourceAuthors = new[]
{
new SourceAuthor {AuthorId = 1, Name = "John"},
new SourceAuthor {AuthorId = 2, Name = "Sam"}
};
var sourceBlogs = new[]
{
new SourceBlog
{
BlogId = 1,
Title = "First Blog",
Author = sourceAuthors.First(a => a.Name == "John")
},
new SourceBlog
{
BlogId = 2,
Title = "Second Blog",
Author = sourceAuthors.First(a => a.Name == "John")
},
new SourceBlog
{
BlogId = 3,
Title = "Another Blog",
Author = sourceAuthors.First(a => a.Name == "Sam")
}
};
Trace.Assert(sourceAuthors.Distinct().Count() == 2);
Trace.Assert(sourceBlogs.Select(b => b.Author).Distinct().Count() == 2);
return sourceBlogs;
}
private static void Main()
{
var mapper = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<SourceBlog, DestinationBlog>();
cfg.CreateMap<SourceAuthor, DestinationAuthor>();
}).CreateMapper();
var sourceBlogs = GetSourceBlogs();
var destinationBlogs = mapper.Map<DestinationBlog[]>(sourceBlogs);
// Throws, because there are 3 distinct DestinationAuthor objects.
Trace.Assert(destinationBlogs.Select(b => b.Author).Distinct().Count() == 2);
}
}
}
(I could probably setup some kind of objects store in form of a dictionary, and then manually resolve instances through that, but this must be a very common AutoMapper scenario, so I assume this functionality already exists in the core or some extension package.)
Of course it is supported and pretty simple after all. Just use .PreserveReferences() (see 5.0 Upgrade Guide: Circular references):
var mapper = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<SourceBlog, DestinationBlog>()
.PreserveReferences();
cfg.CreateMap<SourceAuthor, DestinationAuthor>()
.PreserveReferences();
}).CreateMapper();
So I have the next method (which works) to return a list of claims plus its observations. One claim can have zero-or-many observations. Code works but I'm afraid its a mess, with the anonymous type and then parsing it into a new Claim type, setting the count.
public async Task<IEnumerable<Claim>> GetClaims(ClaimStatusCode status, int take = 10, int skip = 0)
{
using (var db = new DataContext())
{
var pendingclaims = await (from claim in db.Claims
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
select new
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.Observations.Count
}).OrderBy(c => c.OpenDate).Take(take).Skip(skip).ToListAsync();
var list = new List<Claim>();
foreach (var claim in pendingclaims)
{
Claim c = new Claim()
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.ObservationsCount
};
list.Add(c);
}
return list;
}
}
I think maybe I'm missing something to reduce the mess of the resulting SQL query, but don't figure what. Any idea?
UPDATE
As requested, here's the Claim and Observation class, I'm using a plain simple Entity Code First One to Many relationship:
Claim
public class Claim
{
public Claim()
{
Observations = new List<Observation>();
}
[Key]
public Guid ID { get; set; }
...
public virtual ICollection<Observation> Observations { get; set; }
[NotMapped]
public int ObservationsCount { get; set; }
}
Observation
public class Observation
{
public Observation()
{ }
[Key]
public Guid ID { get; set; }
...
public virtual Guid ClaimID { get; set; }
[ForeignKey("ClaimID")]
public virtual Claim Claim { get; set; }
}
There is no way in EF6 to get what you want without some intermediate projection (being it anonymous type or concrete type, as soon as it's not an entity type). But if you need all the object fields plus child count, you can simplify the implementation like this:
var pendingclaims = await (from claim in db.Claims.AsNoTracking()
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
orderby claim.OpenDate
select new
{
claim,
ObservationsCount = claim.Observations.Count
}).Take(take).Skip(skip).ToListAsync();
return pendingclaims.Select(item =>
{
item.claim.ObservationsCount = item.ObservationsCount;
return item.claim;
}).ToList();
I am trying to make a select from the database using the entity framework 5.0.
I have a table called Persons that is referenced by PersonsImages, so basically one record from Persons can have many PersonsImages.
I've made a select statement that gives the Persons, but I would also like to get the PersonsImages as a List<PersonsImages>, and put them in a custom object.
This is the code that I have so far:
var person = new Persons();
using (var context = new PersonEntities())
{
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages // there's an error here
})
.FirstOrDefault();
}
The Persons and the PersonsImages classes look like that (they are copies of the ones generated by the entity framework):
public partial class Persons
{
public Persons()
{
this.PersonsImages = new HashSet<PersonsImages>();
}
public string personName { get; set; }
public string personLastName { get; set; }
public virtual ICollection<PersonsImages> PersonsImages { get; set; }
}
public partial class PersonsImages
{
public string imageName { get; set; }
public byte[] image { get; set; }
public virtual Persons Persons { get; set; }
}
I know I can make a second select and "manually" find them, but isn't it possible to do it in one, just as what the entity framework normally does?
Assuming your error is "can't construct an object in a LINQ to Entities query" - project into an anonymous type, call the ToArray() method to enumerate the results, then project into new instances of Persons:
person = context.Persons.Where(x => x.personId == 555)
.Select(xxx => new
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.ToArray() // data now local
.Select(xxx => new Persons
{
personName = xxx.personName,
personLastName = xxx.personLastName,
PersonImages = xxx.PersonsImages
})
.FirstOrDefault();
I'm developing my first class library that uses Entity Framework Code First as Data access layer.
I have this class:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<User> Friends { get; set; }
public virtual ICollection<User> FromWhomIsFriend { get; set; }
}
And now I testing my code with a Console application:
static void Main(string[] args)
{
Database.SetInitializer(
new DropCreateDatabaseAlways<AdnLineContext>());
insertUsersAndFriends();
}
private static void insertUsersAndFriends()
{
using (var context = new AdnLineContext())
{
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
var user2 = context.Users.Create();
user2.Name = "User2";
user2.Age = 26;
user2.City = "City2";
user2.Country = "Country2";
user2.Email = "email_2#email.com";
user2.InterestIn = "User2's interests";
var user3 = context.Users.Create();
user3.Name = "User3";
user3.Age = 27;
user3.City = "City3";
user3.Country = "Country3";
user3.Email = "email_3#email.com";
user3.InterestIn = "User3's interests";
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends.Add(user2);
user3.Friends.Add(user1);
context.SaveChanges();
}
}
I'm testing, so the database is empty.
This is my UserConfiguration class:
public UserConfiguration()
{
Property(d => d.Name).IsRequired();
Property(d => d.Age).IsRequired();
Property(d => d.City).IsRequired();
Property(d => d.Country).IsRequired();
Property(d => d.Email).IsRequired();
Property(d => d.InterestIn).IsRequired();
HasMany(d => d.MessagesSent).WithRequired(l => l.SentByUser).WillCascadeOnDelete(false);
HasMany(d => d.MessagesReceived).WithRequired(l => l.SentToUser).WillCascadeOnDelete(false);
HasMany(d => d.Friends).
WithMany(d => d.FromWhomIsFriend).
Map(c =>
{
c.ToTable("UserFriends");
c.MapLeftKey("UserId");
c.MapRightKey("FriendId");
});
HasMany(d => d.WantsToDo).
WithMany(a => a.Users).
Map(t =>
{
t.ToTable("UserActivities");
t.MapLeftKey("UserId");
t.MapRightKey("ActivityId");
});
}
But I get a null pointer exception here user1.Friends.Add(user2); because Friends is null.
What am I doing wrong? How can I fix this problem?
Entity Framework seems to be smart in this case. You are adding a new User to the context:
var user1 = context.Users.Create();
//...
context.Users.Add(user1);
//...
user1.Friends.Add(user2);
The entity is in Added state after that. Why should EF run a query with lazy loading to initialize the Friends collection? user1 is the principal in the relationships and because the state is Added it cannot exist yet in the database, hence there can't be any dependent in the database refering to it that could be loaded. So, EF does not try to load the collection at all (which is good in order to avoid unnecessary database roundtrips).
You could apply tricks to make it work - by attaching the new users before you add them to the context:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.UserId = 1;
context.Users.Attach(user1);
user2.UserId = 2;
context.Users.Attach(user2);
user3.UserId = 3;
context.Users.Attach(user3);
// ...because we cannot attach three users with the same key
user1.Friends.Add(user2);
user3.Friends.Add(user1);
// Lazy loading will run twice here based on the `UserId` which is 1,2,3
// and returns nothing, but the Friends collection will be initialized
// as empty collection
// This MUST be AFTER accessing the Friends collection
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
// the three "dummy UserIds" are ignored because state is Added now
context.SaveChanges();
Now, just forget this solution again. It's nonsense to force lazy loading (= expensive database query) to create an empty collection. C# has the new operator for this:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.Friends = new List<User>();
user3.Friends.Add(user1);
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
context.SaveChanges();
You can also just use var user1 = new User() in this scenario where you only add new entities to the context. Creating dynamic proxies has no benefit here (unless you would set any foreign key properties to other existing entities and want to access their corresponding navigation properties after calling SaveChanges - which doesn't seem to be the case in your example).
You must declare your "List<>" Properties like this:
public virtual ICollection<User> Friends { get; set; }
If you don't use virtual keyword EF will not initialize collection for you.
In your case, you are creating new object, use private properties to initialize it for new objects:
private ICollection<User> _friends;
public ICollection<User> Friends {
get { return _friends ?? (_friends = new List<User>()); }
set { _friends = value; }
}
You have to initialize the member Friends like this:
using (var context = new AdnLineContext())
{
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.FromWhomIsFriend.Add(user1);
context.SaveChanges();
}
I think that you might want to consider how the database should model the relationship between users. It seems like you want to have a 1:N relationship between users and itself (IE, one user can have multiple other users associated with it). To be honest the only way I know how to achieve this is a lookup table that associates two UserIds together. You could do this in Entity Code First like this:
public class FriendDefinition
{
[ForeignKey("User")]
public int UserId { get; set; }
[ForeignKey("Friend")]
public int FriendUserId { get; set; }
public virtual User User { get; set; }
public virtual User Friend { get; set; }
}
Then you could update your User class like so:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<FriendDefinition> Friends { get; set; }
}
Finally, you would now use this as follows:
var user = db.context.Users.First();
var firstFriend = user.Friends.First().Friend;
It's a little clunky, but I think it would serve your purpose. Code first is an excellent tool but you still have to conceptualize how the data is actually being stored in the database to model what you need.
You should use the Create method of DbSet - it will give you a fully initialised proxy.
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
//....
context.Users.Add(user1);
Hard coding things like user1.Friends = new List<User>(); is hack not a solution.
I have the following poco class:
public class Category : IDisplayName
{
private ICollection<Category> children;
private Category parent;
public Category()
{
children = new List<Category>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual Category Parent
{
get { return parent; }
set
{
parent = value;
// if (value != null && parent.Children.Contains(this) == false)
// {
// parent.Children.Add(this);
// }
}
}
public virtual ICollection<Category> Children
{
get { return children; }
set { children = value; }
}
}
This is the Mapping file (I am not sure if this is correct.. but I am out of ideas and there is bugger all documentation out there...)
public class CategoryEntityConfiguration : EntityConfiguration<Category>
{
public CategoryEntityConfiguration()
{
Property(x => x.Name).IsRequired();
HasMany(x => x.Children).WithOptional(x => x.Parent);
HasOptional(x => x.Parent).WithMany(x => x.Children);
}
}
Notice the "Parent" property and how I am not adding them each using the "Children" collection.
var cat_0 = new Category { Name = "Root" };
var cat_1 = new Category { Name = "Property", Parent = cat_0 };
var cat_2 = new Category { Name = "Property Services", Parent = cat_1 };
var cat_3 = new Category { Name = "Housing Association", Parent = cat_2 };
var cat_4 = new Category { Name = "Mortgages & Conveyancing", Parent = cat_2 };
var cat_5 = new Category { Name = "Property Management", Parent = cat_2 };
var cat_6 = new Category { Name = "Property Auctions", Parent = cat_2 };
var cat_7 = new Category { Name = "Landlords Wanted", Parent = cat_2 };
context.Set<Category>().Add(cat_0);
When I save the cat_0 to the database only 1 row is inserted and Entity Framework does not pick up the fact the cat_0 is the parent of a whole bunch of other objects and does not realise that they need to be persisted. I have a workaround which is the commented out code in the "Parent" category property.. but I would rather not have to do this as is does not feel right.
Any help would be much appreciated
Jake
It is possible but you have to use tracking proxies. To do that modify your Category class so that all persisted properties are virtual.
public class Category
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
Create context and check that creation of dynamic proxy is allowed. On such context you can use CreateObject method to get your category instance. You will not get instance of type Category but dynamic type inherited from Category. This dynamic proxy is responsible for lazy loading (if enabled) and for change tracking to existing context. If you modify navigation property on the one side it will automatically modify navigation property on the other side.
using (var context = new ObjectContext(connectionString))
{
// This should be default value
context.ContextOptions.ProxyCreationEnabled = true;
var cat0 = context.CreateObject<Category>();
cat0.Name = "A";
var cat1 = context.CreateObject<Category>();
cat1.Name = "B";
cat1.Parent = cat0;
context.CreateObjectSet<Category>().AddObject(cat0);
context.SaveChanges();
}
Edit:
If you don't like approach with tracking proxies (which require existing context) you can reverse the way you create your entities. Instead of setting Parent property on childs you have to fill Childs on parent. In that case it will work.