Returning different versions of set of data - c#

So I have a class with a static method that uses EF to retrieve certain set of mailing lists and maps to the class.
public static List<MailingList> GetMailingListsForUser(IUsersAccess user, IProspectorDataSource db )
{
return db.MailingLists.Where(x => x.UserID == user.UserID).ToList()
.Select(y => new MailingList(y, db) ).ToList();
}
Now though I have a proc that will return the MailingList plus some extra stuff. I don't want to add these extra columns (which will be used in other sections and areas of functionality) to this class. What is the best way to address this?
I am thinking a Factory Pattern that will generate a different class that implements different contracts (interfaces) based on whats needed. Going to try implement it and will post code/working when completed.
Was wondering what other people have done in instances like this and if there are any better ways to address this.
Edit: (some extra information to help people understand what I mean).
public class MailingList
{
public int MailingListID { get; set; }
public string Name { get; set; }
public string Comments { get; set; }
public List<string> Tags { get; set; }
public int UserID { get; set; }
public System.DateTime DateCreated { get; set; }
public string CreatedBy { get; set; }
public System.DateTime LastModified { get; set; }
public string ModifiedBy { get; set; }
public List<MailingListAddress> MailingListAddresses { get; set; }
That is the definition of an object that we return. Now there is a new instance where I am going to return some extra columns from a proc and map to MailingList. So I could just add the properties to here but the issue is MailingListAddresses will be null as they will not be returned by the stored proc. So is there a way to map to specific properties and not have to return null for MailingListAddresses to the front end every time.

This was fixed by a senior developer who ended up going with the factory pattern. I will add the code when I get back to work :)

Related

Automapper - Nested Lists

I am working with AutoMapper, which I am relatively new with, and I stumbled upon a small mapping problem I was hoping the community could assist with.
So I have two data transfer objects:
public class UserDto {
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<CharacterDto> Characters { get; set; }
}
public class CharaterDto {
public string CharacterName { get; set; }
public string ClassName { get; set; }
public int CharacterLevel { get; set; }
}
and two Domain Entities
public class Character {
public int ID { get; set; }
public int UserId { get; set; }
public string CharacterName { get; set; }
public string ClassName { get; set; }
public int CharacterLevel { get; set; }
}
public class User {
public int ID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
The end goal is to be able to save the data taken in by the DTOs into the database via the Domain Entities; however, when it comes to typing up the list of Characters for 'UserDto', I do not know how to map this properly with AutoMapper. I can map it manually with little to no problems... but I can't find anything that helps to explain this or any examples that would help me understand it better.
I have tried doing things like:
CreateMap<UserDto, Character>()
.ForMember(dest => dest.CharacterName, m => m.MapFrom(source => source.Characters[0].CharacterName));
However, this seems to only map the 1st entry and not the others. I have also considered mapping the individual mappings like so:
CreateMap<CharacterDto, Character>();
CreateMap<UserDto, Character>()
.ForMember(?/*this section I cannot figure out*/)
But can't figure out how to associate the the collection of characters to the mapped CharacterDto. I doubt that if I run the code without that association, the code is going to automatically understand that for each character in characters, map each character using the appropriate mapper... If I must manually do this, I can... but if there is an AutoMapper way, any help constructing it would be greatly appreciated.
Type converters are you friend here for mapping 1 to many like this.
Let me know if you need me to go further and get you a working example from your models.
https://stackoverflow.com/a/18096914/7911333

Recovery of class containing a list returns a null list

I have a couple of classes:
public class MyGoalsModel
{
[Key]
public string Name { get; set; }
/*Some local bools*/
public List<MyGoalString> myGoals { get; set; }
}
public class MyGoalString
{
public int MyGoalStringID { get; set; }
public string GoalString { get; set; }
public bool Selected { get; set; }
}
I can populate them correctly, and the code (EF?) generates the necessary hidden foreign keys to link them (all ok in SQL) and recover the information for MyGoalsModel, but the List is always null.
I use the following to get the entry I want:
MyGoalsModel goals = db.MyGoals.Find(Name);
but when I investigate the code goals.MyGoals is always null.
Am I missing something, is there a better way to recover the information with the lists present?
Add the keyword virtual so EF can create a proxy for your List and lazy load the data when needed.
Edit: Or as stated in the accepted answer in this question.

Entity Framework - Saving a snapshot of a document in a SQL database

Background I am using EF6 and is free to change the database.
I ran into this problem today. Say I have:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Address
{
public int Id { get; set; }
public int CompanyId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
}
The users are allowed to update Company and Address. Users are also allowed to update Invoice. But since it is a financial document, it must somehow save a snapshot of the address if the user marks IsCompleted to true.
Currently it is done in the following way:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Auditing fields
public string CompanyName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
I think this is hard to follow. I was thinking:
Option 1: Save an audit to an audit table of its own:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Null if IsCompleted = false.
public DateTime? CompletedTimeStamp { get; set; }
}
public class CompanyAudit
{
public int CompanyId { get; set; }
public string Name { get; set; }
public DateTime TimeStamp { get; set; }
}
public class AddressAudit
{
public int AddressId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public DateTime TimeStamp { get; set; }
}
But then that seems like a lot of tables to create and a lot of work if we do change the schema for Company and Address. Also, it's not very robust. I can't reuse this for other documents without a bunch of wiring. However, this is what I found mostly on the internet. One audit table for each table.
Option 2: Save all the audit to the same table:
public class Audit
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string JsonData { get; set; }
public DateTime TimeStamp { get; set; }
}
But then, this seems like it's not very standard. I have never saved Json data to a SQL database before. Is this bad? If so What could go wrong?
Should I go with Option 1 or Option 2?
Short answer
Assuming you are using SQL Server, I would recommend creating some XML-serializable DTOs for that job and storing XML in a dedicated column, using the XML datatype.
Slightly longer answer
I have already gone that exact same path. We needed to save a snapshot of data at the point that it has been printed out, and there were many tables involved that would have been duplicated in the process.
Requirements and Evaluation
We didn't want to incorporate an additional technology (e.g. File System as proposed by Andreas or some NoSQL/document database), but store everything in SQL Server, as otherwise this would have complicated backup scenarios, deployment, maintenance and so on.
We wanted something easy to understand. New developers should be familiar with the technology used. Architecture shouldn't be influenced too much.
For serialization, there are several options: XML, JSON, BinaryFormatter, DataContractSerializer, Protocol Buffers... Our requirements: Easy versioning (for added properties or relationships), readability, conformance with SQL Server.
Easy versioning should be possible using all mentioned formats. Readability: XML and JSON win here. Conformance with SQL Server: XML is supported in SQL Server natively and was our choice.
Implementation
We did several things:
Create additional DTOs in our database project, side by side with the existing Entities, not for usage with EF but for XML serialization. They are annotated with XmlAttributes and resemble a complex, self-contained structure with everything that is needed to hold the document's data. The root InvoiceSnapshot class has Parse and ToXml methods to support serialization.
Update our Entities to include the snapshots, where required:
public string InvoiceXml { get; set; }
public InvoiceSnapshot Invoice
{
get
{
return this.InvoiceXml != null
? InvoiceSnapshot.Parse(this.InvoiceXml)
: null;
}
set { this.InvoiceXml = value != null ? value.ToXml() : null; }
}
Update the entity configuration to create an XML column and ignore the InvoiceSnapshot property:
public class InvoiceEntityConfig : EntityTypeConfiguration<InvoiceEntity>
{
public InvoiceEntityConfig()
{
this.Property(c => c.InvoiceXml).HasColumnType("xml");
this.Ignore(c => c.Invoice);
}
}
Modify our business objects so that they load themselves either from Entities (editable state) or from XML-DTOs (snapshot, readonly state). We use interfaces on both where they help streamlining the process.
Further steps
You should add metadata for common queries in separate scalar columns and index them. Retrieve the xml data only when you really want to show the invoice.
You can look into what SQL Server can do for you regarding XML, especially when you need to query based on attributes. It can index them, and you can use XPath in queries.
Sign or hash your xml to make sure that snapshot data won't be tampered with.
Option2
BUT
A better way is to store the genrated invoice (pdf)
and all reversions of it in normal files.
You can still use your Invoice table
but then u dont need to wory if some customer-data change
and an user reprints an older document.
To store the generated documents it's near the same
as to store the model in JsonData but u dont Need to
safe the Version of your template and template generator.
Option1 is just brut force and is maybe better released
with the "Event Store", "Event Sourcing" and "Query and Command" pattern.
public class Document //or DocumentFile
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string FilePath { get; set; }
[Index]
public String Owner { get; set;} //exp. "Customer:Id", "Contact:Id" maybe just int
[Index]
public String Reference { get; set; } //exp. "Invoice:Id", "Contract:Id" maybe just int
public DateTime TimeStamp { get; set; } //maybe int Reversion
}

How do I use EF6 with Database First and existing views?

I'm an EF noob (any version) and my Google-foo has failed me on finding out how to do this. Which makes me think I must be doing this wrong, but here is the situation:
I'm definitely in an environment that is database first and the schema won't be updated by us coders. I'm also not a fan of 'automatic' code generation, so I've stayed away from the designer or the EF powertools (though I did run through them just to see them work).
To learn I imported the Northwind DB into my LocalDB to have something to play with while creating some simple Web API 2 endpoints. This all went well as I created slimmed down models of the Employees, Shippers, & Region tables in Northwind. Region was particularly interesting as it wasn't plural and EF had issues with that. Anyway, I got by that.
My trouble now is; I want to use a view instead of a table as my source and whatever I'm doing just doesn't seem to work. What I tried was setting it up just like I did the tables. But that produces a ModelValidationException error. I tried looking at the auto-generated code from the designer, but got no insight.
My models:
//-- employee, shipper, & region work as expected
public class employee {
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
public class shipper {
public int ShipperID { get; set; }
public string CompanyName { get; set; }
public string Phone { get; set; }
}
public class region {
public int RegionID { get; set; }
public string RegionDescription { get; set; }
}
//-- invoice is a view (actual viewname is 'Invoices')
//-- so i followed the same rules as i did for employee & shipper
//-- i have tried uppercase 'I' as well as a plural version of the model
public class invoice {
public string CustomerID { get; set; }
public string CustomerName { get; set; }
public string Salesperson { get; set; }
public int OrderID { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
}
My Context looks like this:
public class NorthwindDBContext : DbContext {
public DbSet<Employee> Employees { get; set; }
public DbSet<shipper> Shippers { get; set; }
public DbSet<region> Regions { get; set; }
public DbSet<Invoice> Invoices { get; set; } //-- offending line of code
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//--- fix for Region being singular instead of plural
modelBuilder.Entity<region>().ToTable("Region");
}
}
If I comment out the public DbSet<Invoice> Invoices { get; set; } line in the context everything works. Just by having the line present (even if i don't reference the Invoices property) I receive the ModelValidationException error when using the context in anyway.
Can anybody tell me what I'm doing wrong here?
Thanks.
Update: I tried this in one of my controllers, but I am too noob'ish to know if this is the right path either, though it worked as far as getting records.
using (var dbContext = new NorthwindDBContext()) {
return dbContext.Database.SqlQuery<Invoice>("select * from invoices").ToList();
}
Code-first conventions will look for an ID or InvoiceID property to use as a key. Your Invoice model has neither, while the others do. This is the specific reason your code is failing.
The less-specific one is that you can't have entities in EF which lack a unique key. If you can, have the view define a key. Otherwise, you may still be able to work around the issue.

Where did the overload of DbQuery.Include() go that takes a lambda?

I just declared some code-first models for a new project that uses EntityFramework.
public class BlogEntry
{
public long Id { get; set; }
public long AuthorId { get; set; }
public DateTime PublishedStamp { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public virtual User Author { get; set; }
}
public class User
{
public long Id { get; set; }
public string Email { get; set; }
// ...
}
class BlogDb : DbContext
{
public DbSet<BlogEntry> Entries { get; set; }
public DbSet<User> Users { get; set; }
}
Now suppose I want to retrieve the 10 most recent blog entries:
var entries = new BlogDb().Entries.OrderByDescending(...).Take(10).ToList();
The problem now is that accessing entry.Author will cause another database query. You wouldn’t want a separate such query for every blog entry. Now, it is my understanding that the purpose of Include is exactly this case, so I can say:
var entries = new BlogDb().Entries.Include(e => e.Author).(...).ToList();
However, that method doesn’t seem to exist. There is only an Include(string), like this:
var entries = new BlogDb().Entries.Include("Author").(...).ToList();
but this is annoying because it’s not compile-time checked and will be missed by the rename refactoring. Surely the version with the lambda is the “correct” approach.
Where did that method go? Is it no longer included in EntityFramework?
(I know that I can write an extension method for myself to achieve this, so you don’t have to. I’d just like to know whether I’m missing something.)
using System.Data.Entity;
It's in EF v4.1 and above, but you need a reference as it is an extension method.
Edit (thanks to #EastonJamesHarvey)
If using EF Core the import should be:
using Microsoft.EntityFrameworkCore;

Categories