I am learning about the Entity Framework and POCOs and while I like a lot of the concepts, I think I am not quite getting it. Here's an example:
I have a schema like the following:
create table Customer
{
Id int,
Name varchar(32),
Value1 varchar(32),
Value2 varchar(32),
Value3 varchar(32)
...
Value50 varchar(32)
}
-- ColumnName will map to "Value1", "Value2", etc
create table ColumnMapping
{
ColumnName varchar(32),
DisplayName varchar(32)
}
The object which represents this data looks like:
class Customer
{
public Id { get; set; }
public Name { get; set;}
public Dictionary<string, string> CustomData { get; set; }
}
That is, I'd like to map the Value1 to Value50 to a Dictionary (where the Key of the dictionary is determined by the ColumnMapping table).
I am wondering what the best approach to this.
I'd like the Customer to be a POCO, but in order to do that, it would need to know about Value1..Value50 so that it would be able to convert those columns into a dictionary. But given that a POCO should be persistent ignorant, I am questioning if that is the right approach.
I guess, in general, I am struggling with what the POCO really is - is it the object which is used by the business layer, or does there need to be a mapping between the POCO and a "business object" and the "business object" is what should be used by the business layer.
Any advice on how to deal with this type of scenario will be appreciated.
Edit
As I didn't receive an answer to the question I was trying to ask, I'll go ahead and indicate what I decided (in case anyone has this similar issue). While the POCO is persistent ignorant in that it doesn't need to know about how it gets persisted, it's not entirely persistent ignorant. That is, it has to be tied to the persistence layer in some manner.
In my example, while I don't want the business layer to know about Value1, Value2, Value3, etc, someone needs to know about it in order to convert those values to a dictionary. I believe that the right place to put that logic is the POCO and hence, I believe the POCO should have properties for the Value1, Value2, Value3, etc, columns.
Thanks,
Eric
In ORM world, this is typical approach
class Customer
{
public int Id { get; set; }
public string Name {get; set; }
public virtual ICollection<CustomDatum> CustomData { get; set; }
}
class CustomDatum
{
public int Id { get; set; } // PK
public string ColomnName { get; set; }
public string DisplayName { get; set; }
}
Related
I have a need to store an unknown data structure in a SQL Server database table field via ORMLite. This is to support a timeline feature on a website where each step on the timeline contains different information, and I want to store them as generic "Steps", with the variable data in a "StepData" property. I have the POCO set up like this:
public class ItemStep
{
public ItemStep()
{
this.Complete = false;
}
[Alias("ItemStepId")]
public Guid Id { get; set; }
[References(typeof(Item))]
public Guid ItemId { get; set; }
[References(typeof(Step))]
public int StepId { get; set; }
public object StepData { get; set; }
[Reference]
public Step Step { get; set; }
public bool Complete { get; set; }
public DateTime? CompletedOn { get; set; }
}
My front-end send a JSON object for StepData, and it's saved to the database appropriately.
{itemAmount:1000,isRed:False,isBlue:True,conversion:True}
Now, when I go to retrieve that data using...
List<ItemStep> itemSteps = Db.Select<ItemStep>(q => q.ItemId == request.ItemId).OrderByDescending(q => q.StepId).ToList<ItemStep>();
...the "StepData" node of the JSON response on the client is not a Javascript Array object as I'm expecting. So, on the client (AngularJS app using Coffeescript),
ItemStep.getItemSteps(ItemId).then((response) ->
$scope.StepData = response.data.itemSteps[0].stepData
is a double-quoted string of the JSON array.
"{itemAmount:1000,isRed:False,isBlue:True,conversion:True}"
Can anybody help me with this? I've tried parsing that string as JSON and I can't seem to get it to work:
JSON.parse($scope.StepData)
I'm using the exact same methodology in other areas of the app to store and retrieve things like addresses, with the only difference I can see being that there is a specified Address class.
Thanks!
Found this link that solved my problem: https://github.com/ServiceStackV3/mythz_blog/blob/master/pages/314.md
Essentially I added a "Type" field to the ItemStep class, and set that when I create a new row (create the next step in the timeline). Then, when I retrieve that record, I call a method like "GetBody" in the referenced link (GetStepData for me), that deserializes the object using the stored Type. I then stuff that back into a generic "object" type in the return POCO so that I can include many steps of varying types in the same call. Works great!
Thanks Mythz for the blog post!
I am using Entity Framework and have a connection to a MySQL database. The id column is set to use StoreGeneratedPattern Identity and the column in the database has been set to auto-increment. When I create a new object and save it to the database, the item posts correctly in the database. However, after saving, the id of the object in C# remains 0 rather than reflecting the value than was assigned by the database.
The section of code is given below:
Group newGroup = new Group("MyGroupName", "Active");
dbContext.Groups.Add(newGroup);
dbContext.SaveChanges();
int testId = newGroup.id;
Even though "newGroup" saves in the database with a database-assigned id, when I read the id (such as I do when reading testId) the id is still 0.
Based on this, I have tried adding
dbContext.Entry(newGroup).Reload();
after SaveChanges() and I have also tried (based on this and this) adding
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
objectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, newGroup);
after SaveChanges() in an attempt to refresh the object (and thus the id) from the database, yet the problem remains. How can I get the id that was assigned by the database?
EDIT: Adding class definition for Group:
[Table("groups")]
public partial class Group
{
public Group()
{
this.user_groups = new HashSet<UserGroup>();
}
public long id { get; set; }
public string name { get; set; }
public string status { get; set; }
public System.DateTime created_at { get; set; }
public System.DateTime updated_at { get; set; }
public virtual ICollection<UserGroup> user_groups { get; set; }
}
Try decorating your id with the [Key] attribute.
It SHOULD be this attribute
[DatabaseGenerated(DatabaseGenerationOption.Identity)]
However, this SHOULD be the default.
The [Key] attribute, should be unnecessary since the column name Id is magical... Although this might only be the case when using the accepted naming convention for C#.
I wonder if it might be the long that your id property is typed, or possibly the naming convention... you could try naming it Id.
I'm having the same problem with my project. What I did for a work around was to order the table (group) by the ID descending and select the first or default record, then select the ID column.
var newID = dbcontext.Groups.OrderByDescending(x => x.id).FirstOrDefault().ID
You can then assign that to whatever you need and save changes again. I know it's an old thread but hopefully this helps. Seems like there should be a better way to do it...
Popular example: In the issue tracker JIRA, issues can be linked to other issues. The link itself has some data attached, in particular a type.
Example:
Issue A -> depends on -> Issue B
Issue B <- is depended on by <- Issue A
We are introducing the same kind of relationship for an entity in our C# ASP.NET MVC application using EF 4.1 CodeFirst, and I'm wondering how to best model this relationship?
Details:
There are some particularities about this situation:
A link has some data attached, so we can't simply model a many-to-many relationship between issues and issues. We rather have to introduce a new entity Link, which represents a relationship between two issues.
A link, by definition, links two instances of the same entity, it is a "two-to-many" relationship (a link has two issues, an issue can have many links).
The link is directed, which means, if Issue A depends on Issue B, then Issue B is depended on by Issue A.
We will certainly have a Link entity that looks like this:
public class Link
{
public int ID { get; set; }
public Issue IssueA { get; set; }
public Issue IssueB { get; set; }
public LinkType Type { get; set; }
}
The Issue class might look like this:
public class Issue
{
public int ID { get; set; }
public virtual ICollection<Link> Links { get; set; }
}
Currently there would be only one link type: dependency. So, the link type would look like this:
public class LinkType
{
public int ID { get; set; }
public string ForwardName { get; set; } // depends on
public string BackwardName { get; set; } // is depended on by
}
Now for the big question:
If I want EF to automatically manage Issue.Links, I have to tell it what Foreign key on the Link table to use. Either I use IssueA, or I use IssueB. I can't use both, can I?
Either I define:
modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueA);
or I define:
modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueB);
Possible approaches - I am curious about your feedback on whether some of them will lead to troubles, cannot be implemented, or whether any of these approaches can be regarded as "best practice":
Add two Collections to the Issue, ICollection<Link> OutgoingLinks, ICollection<Link> IncomingLinks. This way the collections can be maintained by EF, but from a business logic point of view they don't make much sense.
Only add one collection and configure EF 4.1 to add incoming and outgoing links to it, if that is possible.
Only add one collection and implement it on my own:
ICollection<Link> AllLinks { return _context.Links.Where(l => l.IssueA == this || l.IssueB == this).ToList(); }
The problem with this approach is that the domain entity executes data access tasks which is bad in terms of seperation of concerns.
Any other?
Option (1) is the way to go in my opinion, together with a readonly helper perhaps which combines the two collections:
public class Issue
{
public int ID { get; set; }
public virtual ICollection<Link> OutgoingLinks { get; set; }
public virtual ICollection<Link> InComingLinks { get; set; }
public IEnumerable<Link> Links // not mapped because readonly
{
get { return OutgoingLinks.Concat(InComingLinks); }
}
}
Option (2) isn't possible because you cannot map one navigation property to two different ends/navigation properties.
I am moving from an EDMX mapping to the EF 4.1 DbContext and Fluent mapping and I am wanting to map both a string foreign key and the foreign object using the fluent API. I have an Employee with an Optional Office. I would like both the OfficeId and the Office Object in the Employee class (This is all read only, and I do not need to be able to save these objects). Objects with int keys work fine, but I have tried several with string keys and get the same result - the OfficeId field populates, but the Office object comes back as null. Chekcking in SQL profiler the data is being queried, but the office object is not being populated.
public partial class Employee
{
public int Id { get; set; }
// snip irrelevant properties
public Office Office { get; set; } // this is (incorrectly) always null
public string OfficeId { get; set; }
public WorkGroup WorkGroup { get; set; } // this one with the int key is fine
public int? WorkGroupId { get; set; }
// snip more properties
}
public partial class Office
{
public string Id { get; set; }
public string Description { get; set; }
}
public partial class WorkGroup
{
public int Id { get; set; }
public string Code { get; set; }
}
After feedback from Ladislav below, I map it like this in the OnModelCreating
modelBuilder.Entity<Employee>().HasKey(d => d.Id).ToTable("Employee", "ExpertQuery");
modelBuilder.Entity<Office>().HasKey(d => d.Id).ToTable("Office", "ExpertQuery");
modelBuilder.Entity<WorkGroup>().HasKey(d => d.Id).ToTable("WorkGroup", "ExpertQuery");
modelBuilder.Entity<Employee>()
.HasOptional(a => a.Office)
.WithMany()
.Map(x => x.MapKey("OfficeId")); // this one does not work
modelBuilder.Entity<Employee>()
.HasOptional(e => e.WorkGroup)
.WithMany()
.HasForeignKey(e => e.WorkGroupId); // this one works fine
I assume there is some subtlety with string keys that I am missing ? I am querying it as follows :
var employees = expertEntities.Employees.Include("Office").Include("WorkGroup").Take(10).ToList();
If I omit the OfficeId field from Employee, and set up the mapping like this :
modelBuilder.Entity<Employee>()
.HasOptional(e => e.BusinessEntity)
.WithMany()
.Map(x => x.MapKey("OfficeId"));
Then the office object is populated, but I need the OfficeId field in the Employee object.
Well, I found the issue - it's a data issue - the primary key string values were space padded and the foreign key values were not (!). Although SQL joins the tables correctly (ignoring the padding) and fetches the correct data, it appears that EF will not correlate it back into the the correct objects as .NET is fussier than SQL about trailing blanks.
Your customized mapping just conflicts because of the fact that you have already introduced a OfficeId property of string type. See what happens if you remove the OfficeId property from your Employee definition, or change it to int type.
That is not correct mapping. If you have FK property you cannot use Map and MapKey. That is for scenarios where you don't have that property. Try this:
modelBuilder.Entity<Employee>()
.HasOptional(a => a.Office)
.WithMany()
.HasForeignKey(a => a.OfficeId);
Also first part of your mapping with mapping entities to table is most probably incorrect. Map is used for inheritance and entity splitting scenarios. You are looking for ToTable:
modelBuilder.Entity<Employee>().HasKey(d => d.Id).ToTable("ExpertQuery.Employee");
Also if your ExpertQuery is database schema and not part of table name it should look like:
modelBuilder.Entity<Employee>().HasKey(d => d.Id).ToTable("Employee", "ExpertQuery");
I have a class hierarchy that I want to map across several tables using Entity Framework 4.1 Code First. It's like table-per-type (TPT) but I also want a discrimator column.
The hierarchy looks something like:
public class Event
{
public Guid Id { get; set; }
public string Code { get; set; } // discriminator
public DateTime Date { get; set; }
}
public class Party : Event
{
public int AttendeeCount { get; set; }
}
public class BirthdayParty : Party
{
public int Age { get; set; }
}
public class WeddingParty : Party
{
public string Surname { get; set; }
}
This is a pretty weak example but I hope it makes sense. There'll be an "Events" table, a "Parties" table and a table for each kind of party. However, the discriminator column ("Code") will have a known value for each kind of event, like "BIRTH" for birthday parties or "WEDDING" for wedding parties.
The idea is that if I query for just birthday parties on a given date, EF would know to add Code = 'BIRTH' to my query instead of doing a bunch of UNIONs and JOINs to work out which rows it needs.
I map my lowest-level classes like this:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.ToTable("BirthdayParties");
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
I now need to specify the discriminator value in there somehow. I've tried this:
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
});
... but that complains that I haven't specified the table name inside the call to Map. So I tried moving the ToTable call into there:
var bd = modelBuilder.Entity<BirthdayParty>();
bd.Property(p => p.Age).HasColumnName("BirthdayAge");
modelBuilder.Entity<Event>().Map<BirthdayParty>(cfg =>
{
cfg.Requires("Code").HasValue("BIRTH");
cfg.ToTable("BirthdayParties");
});
... and now it thinks I want a "Code" column in the "BirthdayParties" table, which is not correct. I've already told it that the "Code" column is in the "Events" table.
Is this even possible? Can I combine the use of a discriminator column with a table-per-type mapping?
Unfortunately this is not supported. Discriminator column can be used only in TPH. TPT differs entity types by mapped tables and it always produces those terrible queries. It could be nice feature so perhaps suggestion on Data UserVoice would make it implemented one day.
Update
There is already a suggestion on user voice for this titled "Discriminator column support in TPT inheritance".
I did an override on SaveChanges to accomplish something similar. I simply added an attribute onto the abstract class called Descriminator and set it based on the Concrete Class Name anytime something new is added.
public class MyContext : DbContext
{
public override int SaveChanges()
{
foreach (var item in ChangeTracker.Entries().Where(x=>x.Entity is MyAbstractClass && x.State == EntityState.Added))
{
((MyAbstractClass)item.Entity).Descriminator = item.Entity.GetType().Name;
}
return base.SaveChanges();
}
}