After using http://json2csharp.com/ I got this stucture and I am trying to loop through all Books, however I don't know how and I guess there is a better way then to getting every Book list by hand like this BookStores[0].Prism.Books
In short I want to loop through every List<Book>
public class Book
{
public string name { get; set; }
public int pages { get; set; }
public double rating { get; set; }
public bool available { get; set; }
}
public class Prism
{
public List<Book> Books { get; set; }
}
public class Paragraphia
{
public List<Book> Books { get; set; }
}
public class BookStore
{
public Prism Prism { get; set; }
public Paragraphia Paragraphia { get; set; }
}
public class RootObject
{
public List<BookStore> BookStores { get; set; }
}
What about the following implementation.
public class Book
{
public string name { get; set; }
public int pages { get; set; }
public double rating { get; set; }
public bool available { get; set; }
}
public enum BookCategory
{
Prism,
Paragraphia
}
public class BookStore
{
public Dictionary<BookCategory, List<Book>> Books { get; set; }
}
And then use it as follows:
var bookStore = new BookStore();
bookStore.Books = new Dictionary<BookCategory, List<Book>>();
var paragrapiaBooks = new List<Book>();
paragrapiaBooks.Add(new Book{name = "Paragrapia book 1"});
paragrapiaBooks.Add(new Book{name = "Paragrapia book 2"});
bookStore.Books[BookCategory.Paragraphia] = paragrapiaBooks;
var prismBooks = new List<Book>();
prismBooks.Add(new Book{name = "Prism book 1"});
prismBooks.Add(new Book{name = "Prism book 2"});
bookStore.Books[BookCategory.Prism] = prismBooks;
foreach (var bookCategory in bookStore.Books)
{
foreach (var book in bookCategory.Value)
{
Console.WriteLine(book.name);
}
}
Console.ReadLine();
This way your implementation is also more flexible, you don't have to add a new property when you need a new book category, you just have to add a new Enum value. Instead of an Enum you can also use a String for example.
======================================================================
But I think the following solution is better, by adding property category to the Book class, because it is in fact a property of the book and not of the collection of books. A science fiction novel will still be science fiction even when it is placed on a bookshelf full of cookbooks.
public class Book
{
public string name { get; set; }
public int pages { get; set; }
public string category { get; set; }
public double rating { get; set; }
public bool available { get; set; }
}
public class BookStore
{
public List<Book> Books { get; set; }
}
And then use it as follows:
var bookStore = new BookStore();
bookStore.Books = new List<Book>();
bookStore.Books.Add(new Book { name = "Paragrapia book 1", category = "Paragrapia" });
bookStore.Books.Add(new Book { name = "Paragrapia book 2", category = "Paragrapia" });
bookStore.Books.Add(new Book { name = "Prism book 1", category = "Prism" });
bookStore.Books.Add(new Book { name = "Prism book 2", category = "Prism" });
Console.WriteLine("ONLY PRISM BOOKS:");
foreach (var book in bookStore.Books.Where(b => b.category == "Prism"))
{
Console.WriteLine(book.name);
}
Console.WriteLine("\r\nALL BOOKS:");
foreach (var book in bookStore.Books)
{
Console.WriteLine(book.name);
}
Console.ReadLine();
Related
it's been a while since my last post, this time i need some help to understand something that is goin on with Entity Framework (SQL Server) in c# using Code-First aproach.
Let me show you the code i have:
Blog.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Helper.Models
{
public class Blog
{
[Key]
public int BlogId { get; set; }
public string BlogTitle { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual Author Author { get; set; }
}
}
Author.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Helper.Models
{
public class Author
{
[Key,ForeignKey("Blog")]
public int AuthorId { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public virtual Blog Blog { get; set; }
}
}
RegularAuthor.cs
using System;
namespace Helper.Models
{
public class RegularAuthor : Author
{
public DateTime DateOfFirstBlogPost { get; set; }
}
}
GuestAuthor.cs
namespace Helper.Models
{
public class GuestAuthor : Author
{
public string OriginalBlogAccess { get; set; }
}
}
DefaultDB.cs
using Helper.Models;
using System.Data.Entity;
namespace EF_Basics
{
public class DefaultDB : DbContext
{
public DefaultDB(): base("EFDemo")
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Author> Authors { get; set; }
}
}
Program.cs
using Helper.Models;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace EF_Basics
{
class Testing
{
static void Main(string[] args)
{
TestInsert2();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
private static void TestInsert2()
{
using (DefaultDB ctx = new DefaultDB())
{
RegularAuthor author1 = new RegularAuthor()
{
Name = "First Author",
Address = GetLocalIpAddress(),
DateOfFirstBlogPost = DateTime.Now
};
GuestAuthor guest1 = new GuestAuthor()
{
Name = "Second Author",
Address = GetLocalIpAddress(),
OriginalBlogAccess = "Never"
};
List<Blog> BlogList = new List<Blog>()
{
new Blog
{
Author = author1,
BlogTitle = "Mid Century Modern DIY Dog House Build"
},
new Blog
{
Author = guest1,
BlogTitle = "Elf Doughnut Box Printable"
},
new Blog
{
Author = author1,
BlogTitle = "5 Ways to Make Giant Candy for a Candyland Theme"
}
};
foreach (var blog in BlogList)
{
Console.WriteLine($"Adding '{blog.BlogTitle}' by '{blog.Author.Name}'");
ctx.Blogs.Add(blog);
Thread.Sleep(1000);
Console.WriteLine();
}
ctx.SaveChanges();
}
}
private static string GetLocalIpAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("No network adapters with an IPv4 address in the system!");
}
}
}
So... now that we have all the pertinent code, when i run it all get "most" of the info into the database, but the last record just ignores all of the author data. I also included a snapshot of the SQL and the result after running the code.
I think the problem is that you are adding the blog to the database. Instead you should add the authors with their list of blogs.
The author class should have a list of blogs, so when you add an Author entity you can add as many Blogs as you require.
public List<Blog> Blogs { get; set; }
In the Blog class you can change the following:
public Author Author { get; set; }
This is an example of what should be done:
private static void TestInsert2()
{
using (DefaultDB ctx = new DefaultDB())
{
RegularAuthor author1 = new RegularAuthor()
{
Name = "First Author",
Address = GetLocalIpAddress(),
DateOfFirstBlogPost = DateTime.Now
};
GuestAuthor guest1 = new GuestAuthor()
{
Name = "Second Author",
Address = GetLocalIpAddress(),
OriginalBlogAccess = "Never"
};
author1.Blogs = new List<Blog>
{
new Blog
{
Author = author1,
BlogTitle = "Mid Century Modern DIY Dog House Build"
},
new Blog
{
Author = author1,
BlogTitle = "5 Ways to Make Giant Candy for a Candyland Theme"
}
}
guest1.Blogs = new List<Blog>
{
new Blog
{
Author = guest1,
BlogTitle = "Elf Doughnut Box Printable"
}
}
context.Add(author1);
context.Add(guest1);
ctx.SaveChanges();
}
}
As the blogs are referenced by the Blog property in the author object they will be added to the database
Edit:
This has taken some effort, but I think I have the solution. The table relationship is not correct.
What you are trying to do is to have an author with many blogs. Now that implies that the author object must have a collection of blogs.
The relationship you must have is the following:
In this scenario Blog has a foreign key to Author and as suggested in my main answer the Author class must have a collection of Blogs:
public partial class Author
{
public Author()
{
Blogs = new HashSet<Blog>();
}
public int authorId { get; set; }
public string name { get; set; }
public virtual ICollection<Blog> Blogs { get; set; }
}
Meanwhile the Blog class:
public partial class Blog
{
public int blogId { get; set; }
public string blogTitle { get; set; }
public int? authorId { get; set; }
public virtual Author Author { get; set; }
}
Your model will be:
public Model1()
{
}
public virtual DbSet<Author> Authors { get; set; }
public virtual DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>()
.Property(e => e.name)
.IsUnicode(false);
modelBuilder.Entity<Blog>()
.Property(e => e.blogTitle)
.IsUnicode(false);
}
Before running the code:
After running the code:
Let's say, in the BookDetails page (BookForDetailsDto) we also show the authors of that book (AuthorForListingDto). And moreover, I want to show this author list together with a little info (just the name and id) on the books (BookForAuthorListingDto) of each author.
I have a simple many-to-many relation consisting of Book, Author and BookAuthor objects.
public class Book {
public int Id { get; set; }
public string Name { get; set; }
public List<BookAuthor> Authors { get; set; }
}
public class Author {
public int Id { get; set; }
public string Name { get; set; }
public List<BookAuthor> Books { get; set; }
}
public class BookAuthor {
public int BookId { get; set; }
public Book Book { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
And I have also 3 DTOs (where I am stoping an infinite loop):
public class BookForDetailsDto {
public int Id { get; set; }
public string Name { get; set; }
public List<AuthorForListingDto> Authors { get; set; }
}
public class AuthorForListingDto {
public int Id { get; set; }
public string Name { get; set; }
public List<BookForAuthorListingDto> Books { get; set; }
}
public class BookForAuthorListingDto {
public int Id { get; set; }
public string Name { get; set; }
}
Having a configuration as the following:
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Book, BookForDetailsDto>();
cfg.CreateMap<BookAuthor, AuthorForListingDto>();
cfg.CreateMap<AuthorForListingDto, BookForAuthorListingDto>();
});
I'd like to perform a mapping from Book to BookForDetailsDto like this.
BookForDetailsDto BookDto = mapper.Map<BookForDetailsDto>(book);
But as a result, I get System.NullReferenceException.
It seems like, just in the first level of mapping, AutoMapper cannot get Author information from BookAuthor object.
I am searching for a configuration option but with no luck. I should say I am a newbie with automapper and if there is a simple solution I appreciate.
Note: I saw a comment which goes like "it is not a good practice to have reference in one DTO to second DTO". But I cannot figure out how to do otherwise, because ,for example, for a clickable/navigatable child_object we need at least "a key and a display_name", so a child object of type List seems inevitable.
A new day with a new head...
I changed the mappings like the following and it works as expected:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Book, BookForDetailsDto>()
.ForMember(dto => dto.Authorss, opt => opt.MapFrom(x => x.Authors.Select(a => a.Author)));
cfg.CreateMap<BookAuthor, BookForAuthorListingDto >()
.ForMember(res => res.Id, opt => opt.MapFrom(dto => dto.Book.Id))
.ForMember(res => res.Name, opt => opt.MapFrom(dto => dto.Book.Name));
});
I have two interfaces. I want to use them for list and for array.
public interface IBook<T>
{
string Name { get; set; }
T Authors { get; set; }
int PagesCount { get; set; }
}
public interface IAuthor<T>
{
string Name { get; set; }
T Books { get; set; }
}
class Author<T> : IAuthor<IBook<T>[]>
where T : IAuthor<IBook<T>[]>
{
public string Name { get; set; }
public IBook<T>[] Books { get; set; }
}
class Book<T> : IBook<IAuthor<T>[]>
where T : IBook<IAuthor<T>[]>
{
public string Name { get; set; }
public IAuthor<T>[] Authors { get; set; }
public int PagesCount { get; set; }
public static void Main(string[] args)
{
Author<IBook<IAuthor<...>[]>[]> a = new Author<>();
}
}
Is there any way to create object like this in main. The compiler says there are no errors in description of interfaces and classes. Please help me.
Personally, I think that you may be over-using generics here and making this a little bit more complex than it is.Evaluate your criteria:
Author (one -> many) Book
Book (one -> many) Author
You can do this by having the following classes:
public class Book
{
public string Name { get; set; }
public Author[] Authors { get; set; }
public int PageCount { get; set; }
}
public class Author
{
public string Name { get; set; }
public Book[] Books { get; set; }
}
If you make it like this, you can make your life somewhat easier by containing all books and authors within a parent class and using linq queries to identify which object a book / author is related to:
public class BookStore
{
public List<Book> Books { get; set; }
public List<Author> Authors { get; set; }
public Book GetBook(string name)
{
var query = Books.Where(b => b.Name.Equals(name));
if (query.Count() == 1)
return query.ElementAt(0);
else return null;
}
public Author GetAuthor(string name)
{
var query = Authors.Where(a => a.Name.Equals(name));
if (query.Count() == 1)
return query.ElementAt(0);
else return null;
}
}
It would definitely be better for you if you don't use generic classes like this, cause this will be a pain in the neck every time you try to initialise an object. At least you make a default implementation for the generic class where it doesn't need to be given a type of author. You should definitely implement some kind of strategy for your application which will depend on how scalable it should be and easy to maintain and #Nathangrad has given you a great example.
I think your objects should look something like this where your Book's authors are not open to be changed directly and Author's books as well:
public interface IBook
{
string Name { get; set; }
ICollection<IAuthor> GetAuthors();
void AddAuthor(IAuthor book);
int PagesCount { get; set; }
}
public interface IAuthor
{
string Name { get; set; }
ICollection<IBook> GetBooks();
void AddBook(IBook book);
}
public class Author : IAuthor
{
private ICollection<IBook> books;
public Author()
{
this.books = new HashSet<IBook>();
}
public Author(ICollection<IBook> books)
{
this.books = books;
}
public string Name { get; set; }
public void AddBook(IBook book)
{
this.books.Add(book);
}
public ICollection<IBook> GetBooks()
{
return this.books;
}
}
public class Book : IBook
{
private ICollection<IAuthor> authors;
public Book()
{
this.authors = new HashSet<IAuthor>();
}
public Book(ICollection<IAuthor> Authors)
{
this.authors = Authors;
}
public void AddAuthor(IAuthor author)
{
this.authors.Add(author);
}
public ICollection<IAuthor> GetAuthors()
{
return this.authors;
}
public string Name { get; set; }
public int PagesCount { get; set; }
}
Here is my list of classes:-
public interface IUniquelyIdentifiable
{
string AuthorName { get; set; }
}
public interface IUniquelyIdentifiable1
{
string CategoryName { get; set; }
}
public interface IUniquelyIdentifiable2
{
string PublisherName { get; set; }
}
[Serializable]
public class Book
{
//BookId, Category, Title, Author, Publisher, Description, Price, ISBN, PublicationDate.
public IUniquelyIdentifiable Author { get; set; }
public IUniquelyIdentifiable1 Category { get; set; }
public IUniquelyIdentifiable2 Publisher { get; set; }
public int BookId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int ISBN { get; set; }
public int Price { get; set; }
public string PublicationDate { get; set; }
}
[Serializable]
class Author : IUniquelyIdentifiable
{
//AuthorId, AuthorName, DateOfBirth, State, City, Phone
public int AuthorId { get; set; }
public string AuthorName { get; set; }
public string DateOfBirth { get; set; }
public string State { get; set; }
public string City { get; set; }
public int Phone { get; set; }
}
[Serializable]
class Category : IUniquelyIdentifiable1
{
//CategoryId, CategoryName, Description
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public string Description { get; set; }
}
[Serializable]
class Publisher : IUniquelyIdentifiable2
{
//PublisherId, PublisherName, DateOfBirth, State, City, Phone.
public int PublisherId { get; set; }
public string PublisherName { get; set; }
public string DateOfBirth { get; set; }
public string State { get; set; }
public string City { get; set; }
public int Phone { get; set; }
}
below is the method which is trying to serialize the objects created of the above classes:-
public static void XmlSerializeMyObject()
{
XmlSerializer writer = new XmlSerializer(typeof(Book));
//overview.title = "Serialization Overview";
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "//SerializationOverview.xml";
FileStream file = File.Create(path);
writer.Serialize(file,bookList);
file.Close();
}
As you can see I even used the attribute [Serializable] but still getting the error that I cant serialize interfaces.
Also I just want serialize the objects of the given classes and not interfaces.
See the comments at the end. My first solution answers the question directly but I don't recommend doing it unless you have no choice. Short version - I recommend solving the problem by using the concrete types Author, Category, and Publisher instead of interfaces in the Book class.
In order to serialize a type there must be some way of determining what the concrete types of the members are. It's possible that something could serialize an instance of Book using an implementation of IUniquelyIdentifiable that is unknown to the application deserializing it.
You can modify your Book class like this:
[Serializable][DataContract][KnownType(typeof(Author))]
[KnownType(typeof(Category))]
[KnownType(typeof(Publisher))]
public class Book
{
[DataMember]public IUniquelyIdentifiable Author { get; set; }
[DataMember]public IUniquelyIdentifiable1 Category { get; set; }
[DataMember]public IUniquelyIdentifiable2 Publisher { get; set; }
[DataMember]public int BookId { get; set; }
[DataMember]public string Title { get; set; }
[DataMember]public string Description { get; set; }
[DataMember]public int ISBN { get; set; }
[DataMember]public int Price { get; set; }
[DataMember]public string PublicationDate { get; set; }
}
Then use a DataContractSerializer to serialize. Here's an example:
using (var sw = new StringWriter())
{
using (var xw = new XmlTextWriter(sw))
{
var book = new Book();
book.Author = new Author { AuthorName = "Bob" };
book.Category = new Category { CategoryId = 5 };
book.Publisher = new Publisher { City = "Clearwater" };
var serializer = new DataContractSerializer(typeof(Book));
serializer.WriteObject(xw, book);
var output = sw.ToString();
Assert.IsNotNull(sw);
}
}
This answers the question, but it doesn't solve any problems. In fact, it creates a new problem.
If you just declare the Author, Category, and Publisher properties of Book as concrete types, then you're constrained to using those types. The compiler will show an error if you try to set that property using any class that isn't Author.
But if you add the KnownType attribute as above, the problem is even worse because it's hidden. Now you can set Author to anything implementing IUniquelyIdentifiable. But when you do that (perhaps in some other part of your application) you have no way of knowing that it will fail when serialized. The constraint is still there - you still have to use Author. The difference is that now you get a runtime exception instead of a compile error.
You could instead specify a list of known types to the DataContractSerializer. That gives you a way to specify more types, even using reflection to get a list of types that implement the interface.
But it's still problematic. It's a hidden constraint. You're saying that the type of the property is IUniquelyIdentifiable. According to proper OOP design and the Liskov substitution principle you should be able to use any implementation of that interface. But in reality you can't use any implementation. You have to use one that may or may not be labeled as a "known" type somewhere else (or in multiple places) in your code. Someone could break your application at any time without causing a compile error.
Based on that I'd say to only use the above methods if you have no choice, like if you have to serialize something that you didn't design. If you're writing your own classes then I would just declare Book using the concrete types Author, Category, and Publisher.
You can't serialize interfaces. It doesn't work.
your solution is to change Book's properties to the actual serializable classes:
[Serializable]
public class Book
{
//BookId, Category, Title, Author, Publisher, Description, Price, ISBN, PublicationDate.
public Author Author { get; set; }
public Category Category { get; set; }
public Publisher Publisher { get; set; }
public int BookId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int ISBN { get; set; }
public int Price { get; set; }
public string PublicationDate { get; set; }
}
The question #Richard_Everett linked contain the same answer. Sorry I can't provide any better solution.
I want to create a List that holds a couple of books with the book title, authors name and year of publication. example: ( AuthorLastName, AuthorFirstName, “The book title”, year.)
I know how to create List<int>, for example:
class Program
{
static void Main()
{
List<int> list = new List<int>();
list.Add(10);
list.Add(20);
list.Add(25);
list.Add(99);
}
}
But the problem is, if I want to create a list of books, I can't simply make a list<string> or list<int> because I want it to contain strings and int's (as the example above).
So, Can anyone explain how I can make a List of books?
You need to create a class called Book that contains the properties you want to have. Then you can instantiate a List<Book>.
Example:
public class Book
{
public string AuthorFirstName { get; set; }
public string AuthorLastName { get; set; }
public string Title { get; set; }
public int Year { get; set; }
}
And then, to use it:
var myBookList = new List<Book>();
myBookList.Add(new Book {
AuthorFirstName = "Some",
AuthorLastName = "Guy",
Title = "Read My Book",
Year = 2013
});
You need to define your class:
public class Book
{
public string Author { get; set; }
public string Title { get; set; }
public int Year { get; set; }
}
Then you can make a list of them:
var listOfBooks = new List<Book>();
Do something like this
public class Book
{
public string AuthorLastName { get; set; }
public string AuthorFirstName{ get; set; }
public string Title{ get; set; }
public int Year { get; set; }
}
List<Book> lstBooks = new List<Book>();
lstBooks.Add(new Book()
{
AuthorLastName = "What",
AuthorFirstName = "Ever",
Title = Whatever
Year = 2012;
});