Say we have two domain classes.
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
Now let's create a Context.
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
public BloggingContext () : base("Blogging")
{
Database.SetInitializer<BloggingContext >(new CreateDatabaseIfNotExists<BloggingContext>());
}
Also in this class, we want to add some test data to verify it.
public void AddBlog(params)
{
using (BloggingContext db = new BloggingContext ())
{
var t = new Blog { Name =name };
db.Blogs.Add(t);
try
{
db.SaveChanges();
return true;
}
Then to test it, we create a unit test project.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
BloggingContext bloging= new BloggingContext ();
List<Post> post = new List<Post>();
Post objPost = new Post();
objPost.Post = "some";
objPost.OtherFields = "test";
// etc;
post.Add(objPost);
bloging.AddBlog("MyBlog",post);
To run the test, I found the code went to the constructor of BloggingContext class first, thus Database.SetInitializer... was executed.
Then when
using (BloggingContext db = new BloggingContext ())
It called the constructor again, I am not sure whether it is ok.
You can call the constructor as many times you like
Database.SetInitializer<BloggingContext >(new CreateDatabaseIfNotExists<BloggingContext>());
The CreateDatabaseIfNotExists just does a DB check that if it really exists or not, if not it does create it, else it skips the database creation part !
Name of the strategy does not really make sense.
Actually, what the CreateDatabaseIfNotExists strategy does is:
check if the database with the specified name exist or not, if not
it'll create a new database
if the database with the specified name already exists, and if
there's no tables/procs found in the db at all, it means the db is
in "fresh new" status, then the strategy will also generate the db
structure and insert seed data
If the database with the specified name already exists and there's
some table / procs in the db then it will do nothing
Related
I have basic object models with cross references
//Model in which I pass and gather data from view
public class ItemModel
{
public BasicItem BasicItem;
public FoodItem FoodItem;
public LocalItem LocalItem;
public ItemModel()
{
BasicItem = new BasicItem();
FoodItem = new FoodItem();
LocalItem = new LocalItem();
}
}
//And classes represents EF entities
public class BasicItem
{
...//Multiple basic fields: int, string
//EF references for PK-FK connection
public FoodItem FoodItem { get; set; }
public LocalItem LocalItem { get; set; }
}
public class LocalItem
{
...//Multiple basic fields: int, string
//EF reference for PK-FK connection
public BasicItem BasicItem { get; set; }
}
public class FoodItem
{
...//Multiple basic fields: int, string
//EF reference for PK-FK connection
public BasicItem BasicItem { get; set; }
}
And my view in basics seems like this
#model ItemModel
...
<input required asp-for="BasicItem.Price" type="number" name="Price">
...
<input asp-for="FoodItem.Weight" type="number" name="Weight">
...
As now I connect it (so different entities have relation each to other) like this:
public async Task<IActionResult> ProductAdd(ItemModel ItemModel)
{
if (ItemModel.BasicItem != null)
{
if (ItemModel.LocalItem != null)
{
ItemModel.BasicItem.LocalItem = ItemModel.LocalItem;
ItemModel.LocalItem.BasicItem = ItemModel.BasicItem;
await db.LocalItems.AddAsync(ItemModel.LocalItem);
}
//same for FoodItem
await db.BasicItems.AddAsync(ItemModel.BasicItem);
await db.SaveChangesAsync();
}
}
But data from form dosent bind to my ItemModel, so my code fails at point when it trying to Add new entity to db, but it has null fields(which null by default, but setuped in form).
Is there any way I can help bind this model to data Im entering?
As other way I can only see this: create plain model which will have all fields from Basic, Local and Food items and bind it in my action. But it will hurt a much, if I ever wanted to change one of this classes.
For you scenario , BasicItem has a one-to-one relationship with LocalItem and FootItem.When adding data into the database , you need to pay attention to that if the foreign key is nullable or exists and the order in which data is added to the primary table and child table .
Here is a working demo ,you could refer to :
Model definition
public class BasicItem
{
public int BasicItemID { get; set; }
public string Name { get; set; }
public int FoodItemID { get; set; }
[ForeignKey("FoodItemID")]
public FoodItem FoodItem { get; set; }
public int LocalItemID { get; set; }
[ForeignKey("LocalItemID")]
public LocalItem LocalItem { get; set; }
}
public class FoodItem
{
public int FoodItemID { get; set; }
public string Name { get; set; }
//public int BasicItemID { get; set; }
public BasicItem BasicItem { get; set; }
}
public class LocalItem
{
public int LocalItemID { get; set; }
public string Name { get; set; }
//public int BasicItemID { get; set; }
public BasicItem BasicItem { get; set; }
}
public class ItemModel
{
public BasicItem BasicItem;
public FoodItem FoodItem;
public LocalItem LocalItem;
public ItemModel()
{
BasicItem = new BasicItem();
FoodItem = new FoodItem();
LocalItem = new LocalItem();
}
}
Controller
public async Task<IActionResult> ProductAdd(ItemModel ItemModel)
{
if (ItemModel.BasicItem != null)
{
if (ItemModel.LocalItem != null)
{
await db.LocalItems.AddAsync(ItemModel.LocalItem);
await db.FoodItems.AddAsync(ItemModel.FoodItem);
}
//same for FoodItem
ItemModel.BasicItem.LocalItem = ItemModel.LocalItem;
ItemModel.BasicItem.FoodItem = ItemModel.FoodItem;
await db.BasicItems.AddAsync(ItemModel.BasicItem);
await db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(ItemModel);
}
Okay, we can divide my situation into 2 basic cases:
Creating new entities
Updating entities
In first case it's pretty simple and easy cause you can create new object, fill it up, setup relations (you can only setup relation in one object like basicItem.FoodItem = foodItem, you don't need to do foodItem.BasicItem = basicItem, cause EF will automatically connect them) and send it to db, and it will work.
In second case, it's a little more complicated, cause in case to update data in db, you must get a related entity to a context. It's brings it's own limitations. And again you can have two approaches:
Create new object and manually (or through auto-mapper, but I didn't dig into this) overwrite fields of db related object at the end.
Fetch object from db at the beginning, pass it it through actions and change data on fly (if you want/need, you can even update db record on fly).
They are quite the same in a way, that you need to choose what field to update and write some code dbFoodItem.Weight = userInput.Weight.
So in my case I took second approach, and cause I collected data in multiple actions, I used session to data storage object between them.
I have .net 4.5.2 test app playing about with Azure Mobile Services and I'm attempting to store data using the TableController. I have my data types as follows:
public class Run:EntityData
{
public int RunId { get; set; }
public DateTime? ActivityStarted { get; set; }
public DateTime? ActivityCompleted { get; set; }
public List<Lap> LapInformation { get; set; }
public Run()
{
LapInformation = new List<Lap>();
}
}
public class Lap
{
[Key]
public int LapNumber { get; set; }
public int CaloriesBurnt { get; set; }
public double Distance {get; set;}
//Some other basic fields in here
public DateTime? LapActivityStarted { get; set; }
public DateTime? LapActivityCompleted { get; set; }
public Lap()
{
}
In my Startup class I call:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
And in my MobileServiceContext class:
public class MobileServiceContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString2";
public MobileServiceContext() : base(connectionStringName)
{
}
public DbSet<Run> Runs { get; set; }
public DbSet<Lap> Laps { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
}
In my controller then, I have:
[MobileAppController]
public class RunController: TableController<Run>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileServiceContext context = new MobileServiceContext();
DomainManager = new EntityDomainManager<Run>(context, Request);
}
public IList<Run> GetAllRuns()
{
var runs = context.Runs.Include("LapInformation").ToList();
return runs;
}
public SingleResult<Run> GetRun(string id)
{
return Lookup(id);
}
public async Task<IHttpActionResult> PostRun(Run run)
{
Run current = await InsertAsync(run);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
public Task DeleteRun(string id)
{
return DeleteAsync(id);
}
}
I can then POST a record in fiddler which responds with a 201 and the Location of the newly created Item. An Example of the data I'm posting is:
{RunId: 1234, LapInformation:[{LapNumber:1,Distance:0.8, LapActivityStarted: "2017-06-19T00:00:00", LapActivityCompleted: "2017-06-19T00:00:00", CaloriesBurnt: 12}]}
However, when I GET that object, I'm only getting the fields from Run, without the list of Detail records (Lap). Is there anything I have to configure in Entity Framework so that when I GET a Run record from the DB, it also gets and deserializes all associated detail records?
Hopefully that makes sense.
EDIT
Turns out that it is pulling back all the lap information, but when I return it to the client, that information is getting lost.
You can use custom EF query with Include() method instead of Lookup call preferably overload that takes function from System.Data.Entity namespace.
var runs = context.Runs.Include(r => r.LapInformation)
Take a look at https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
AFAIK, you could also use the $expand parameter to expand your collections as follows:
GET /tables/Run$expand=LapInformation
Here is my sample, you could refer to it:
You could mark your action with a custom ActionFilterAttribute for automatically adding the $expand property to your query request as follows:
// GET tables/TodoItem
[ExpandProperty("Tags")]
public IQueryable<TodoItem> GetAllTodoItems()
{
return Query();
}
For more details, you could refer to adrian hall's book chapter3 relationships.
EDIT Turns out that it is pulling back all the lap information, but when I return it to the client, that information is getting lost.
I defined the following models in my mobile client:
public class TodoItem
{
public string Id { get; set; }
public string UserId { get; set; }
public string Text { get; set; }
public List<Tag> Tags { get; set; }
}
public class Tag
{
public string Id { get; set; }
public string TagName { get; set; }
}
After execute the following pull operation, I could retrieve the tags as follows:
await todoTable.PullAsync("todoItems", todoTable.CreateQuery());
Note: The Tags data is read-only, you could only update the information in the ToDoItem table.
Additionally, as adrian hall mentioned in Data Access and Offline Sync - The Domain Manager:
I prefer handling tables individually and handling relationship management on the mobile client manually. This causes more code on the mobile client but makes the server much simpler by avoiding most of the complexity of relationships.
I am creating a web application in C# using VS2012 to track contact attempts made to customers. I am saving the contact attempt, with 2 ref tables for contact attempt type, and contact attempt outcome, since these will always be fixed values. The problem I'm having is when I retrieve the App_ContactAttempt from the DB, it will bring back the App_ContactAttempt entity without the attached Ref_ContactOutcome and Ref_ContactType entities. I have lazy loading enabled and proxy creation enabled in the context file, and all ref table properties are set to virtual. But when I get an App_ContactAttempt from the db, there are not ref tables attached. Anyone got any ideas what I can do? If you need more information I can provide it.
UPDATE
Right, I have a service setup to get the App_ContactAttempt, which looks like this:
public App_ContactAttempt GetContactAttempt(int contactAttemptId)
{
using (var logger = new MethodLogger(contactAttemptId))
{
var contactAttempt = new App_ContactAttempt();
try
{
contactAttempt = _unitOfWork.ContactAttempts.Get(contactAttemptId);
}
catch (Exception e)
{
logger.LogException(e.InnerException);
}
return contactAttempt;
}
}
When I use this service, I get back App_ContactAttempt when I call the service, but Ref_ContactType and Ref_ContactOutcome are null. But when I call to the db from within the controller using the db context like so:
var db = new ParsDatabaseContext();
var contactAttemptTest1 = _clientService.GetContactAttempt(contactAttempt.ContactAttemptId);
var contactAttemptTest2 = db.App_ContactAttempt.Where(x => x.ContactAttemptId == contactAttempt.ContactAttemptId);
The contactAttemptTest1 returns the App_ContactAttempt with Ref_ContactType and Ref_ContactOutcome both being null. However, contactAttemptTest2 returns App_ContactAttempt with Ref_ContactType and Ref_ContactOutcome both being populated. Hope this helps narrow down my issue, because I haven't a clue..
UPDATE 2
Here are the context and classes if they help at all:
Context.cs
public partial class ParsDatabaseContext : DbContext
{
public ParsDatabaseContext()
: base("name=ParsDatabaseContext")
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<App_Client> App_Client { get; set; }
public DbSet<App_ContactAttempt> App_ContactAttempt { get; set; }
public DbSet<Ref_ContactOutcome> Ref_ContactOutcome { get; set; }
public DbSet<Ref_ContactType> Ref_ContactType { get; set; }
public virtual ObjectResult<GetClient_Result> GetClient(Nullable<int> clientID)
{
var clientIDParameter = clientID.HasValue ?
new ObjectParameter("ClientID", clientID) :
new ObjectParameter("ClientID", typeof(int));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<GetClient_Result>("GetClient", clientIDParameter);
}
}
App_ContactAttempt.cs
public partial class App_ContactAttempt
{
public int ContactAttemptId { get; set; }
public int ClientId { get; set; }
public Nullable<System.DateTime> ContactDate { get; set; }
public Nullable<int> ContactType { get; set; }
public Nullable<int> ContactOutcome { get; set; }
public string Notes { get; set; }
public virtual Ref_ContactOutcome Ref_ContactOutcome { get; set; }
public virtual Ref_ContactType Ref_ContactType { get; set; }
}
Ref_ContactOutcome.cs
public partial class Ref_ContactOutcome
{
public Ref_ContactOutcome()
{
this.App_ContactAttempt = new HashSet<App_ContactAttempt>();
}
public int ContactOutcomeId { get; set; }
public string Description { get; set; }
public virtual ICollection<App_ContactAttempt> App_ContactAttempt { get; set; }
}
Ref_ContactType.cs
public partial class Ref_ContactType
{
public Ref_ContactType()
{
this.App_ContactAttempt = new HashSet<App_ContactAttempt>();
}
public int ContactTypeId { get; set; }
public string Description { get; set; }
public virtual ICollection<App_ContactAttempt> App_ContactAttempt { get; set; }
}
The problem is that lazy loading works only if the DBContext used to create the proxy class is available. In your case is the proxy detached because the DBContext used to crate the proxy object contactAttempt of type App_ContactAttempt has already been disposed.
Also make sure that:
dbContext.Configuration.ProxyCreationEnabled = true;
And you can check if the object is proxy
public static bool IsProxy(object type)
{
return type != null && ObjectContext.GetObjectType(type.GetType()) != type.GetType();
}
https://msdn.microsoft.com/en-us/library/ee835846%28v=vs.100%29.aspx
See this answer to check if your proxy entity is attached to a DBContext.
You can attach existing detached entity to another existing context and make it lazy-loading again:
db.App_ContactAttempts.Attach(contactAttemptTest1);
If you have an entity that you know already exists in the database but
which is not currently being tracked by the context then you can tell
the context to track the entity using the Attach method on DbSet. The
entity will be in the Unchanged state in the context.
See here.
So in your example:
using (var db = new ParsDatabaseContext())
{
var contactAttemptTest1 = _clientService.GetContactAttempt(contactAttempt.ContactAttemptId);
db.App_ContactAttempts.Attach(contactAttemptTest1);
Debug.Print(contactAttemptTest1.Ref_ContactType.Description);
}
should work.
Use includes.
For example:
var contactAttemps = db.App_ContactAttempts
.Includes("Ref_ContactOutcome")
.Includes("Ref_ContactTypes")
.ToList();
Are you returning the entity itself or a DTO (data transfer object)?
if you are returning a DTO, ensure the mapping is properly done.
Post your entity object.
So I have a controller which updates just 2 fields in a db entry, however that entry is linked to two other tables, I know it's a bad explanation but sometimes it works and sometimes it doesn't and I cant identify what's different between submissions since no code changes.
Error
Controller
GroupFitnessSession session = unitOfWork.GroupFitnessSessionRepository.GetById(item.GroupFitnessSessionId);
session.IsConfirmed = true;
session.Attendees = item.Attendees;
unitOfWork.GroupFitnessSessionRepository.Update(session);
There are other fields to the Models that i've left out, but non of them are the same name or something to these oens
Models
public class GroupFitnessSession
{
public string GroupFitnessSessionId { get; set; }
[Required]
public virtual Trainer Trainer { get; set; }
[Required]
public virtual Location Location { get; set; }
}
public class Location
{
public string LocationId { get; set; }
public Location()
{
GroupFitnessSession = new HashSet<GroupFitnessSession>();
}
public ICollection<GroupFitnessSession> GroupFitnessSession { get; set; }
}
public class Trainer
{
public Trainer()
{
GroupFitness = new HashSet<GroupFitnessSession>();
}
public ICollection<GroupFitnessSession> GroupFitness { get; set; }
If you need any other information feel free to ask.
This is just confusing me too much, any advice would be appceiated
EDIT: showing that Location and Trainer are not empty objects
As you can see the auto generated Properties from EF aswell as the propertiy I am trying to update
This will work if you change the first line of the controller to:
GroupFitnessSession session = unitOfWork.GroupFitnessSessionRepository.GetById(item.GroupFitnessSessionId).Include(s => s.Trainer).Include(s => s.Location);
I just started using Entity Framework and it created a Context class which I can use to get all the data i need from it. But I am facing an issue on how I should organize my code, by watching the demos, the person just uses the framework and codes everything on a console application. What is the best way to use Entity Framework and that it looks clean?, what I mean by this is...right now using aspx pages, I could just use the aspx.cs to get the data or save the data. But I do not want this, I would like it to be more organized although the Entity Framework did almost everything by creating the objects etc.. but still, I need to use things like
using(var myobject = new MyContextData())
{
blah blah..
}
would you say that it would be nicer to write classes that would wrap these calls?. I would really appreciate any inputs as it would really make me a better programmer using the entity framework.
Regards
This question should everyone, who provides some tutorial about EF, ask. It is hard to say what is the best way, but put all code in the codebehind classes (aspx.cs) does not help extensibility and testability. Please, try to read this article:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Not only it is official tutorial on asp.net, but it mostly shows, that Framework EF could be used correctly in currently fancy Repository pattern
Edit:
I think that Generic Repository is Anti Pattern. But I do not understand #TomTom comment.
Original Answer:
As Radim Köhler mentioned you need to implement Repository and Unit of Work patterns
But the article he provided in my opinion is not fully correct.
At my current job I use following implementation of these patterns.
For example, we have three types of entities: Person, Good and Order. I created repository for Persons. In common case Repository must not be generic. It must contain methods which represent specific queries for this entity. So by looking at the interface of repository you can tell what kinds of queries executed for entity (Person, e.g.). As you will see I created DTO for Person called PersonWrap. For creating PersonWrap from Person and updating Person from PersonWrap you can use AutoMapper instead of PersonWrap() constructor and Update() method. Because EntityFramework DbContext implements Unit of Work pattern, you just need to provide created DbContext to repository methods. If repository method is a separate action and you do not need DbContext outside of this method you can create and dispose it inside this method.
public class Person {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public DateTime RegistrationDate { get; set; }
public List<Order> Orders { get; set; }
}
public class Good {
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Order {
public int Id { get; set; }
public Person Person { get; set; }
public Good Good { get; set; }
public int Count { get; set; }
}
public class MyDbContext: DbContext
{
public IDbSet<Person> Persons { get { return Set<Person>(); }}
public IDbSet<Good> Goods { get { return Set<Good>(); }}
public IDbSet<Order> Orders { get { return Set<Order>(); }}
}
public class PersonRepository {
public IEnumerable<Person> GetAll() {
using (var context = new MyDbContext()) {
return context.Persons.ToList();
}
}
public IEnumerable<Person> GetLastWeekPersons() {
using (var context = new MyDbContext()) {
return context.Persons.Where(p => p.RegistrationDate > new DateTime().AddDays(-7)).ToList();
}
}
public Person GetById(int id, MyDbContext context) {
return context.Persons.Include(p => p.Orders).FirstOrDefault(p => p.Id == id);
}
public Person GetById(int id) {
using (var context = new MyDbContext()) {
return GetById(id, context);
}
}
}
public class PersonWrap {
public int Id { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public int OrderCount { get; set; }
public PersonWrap(Person person) {
Id = person.Id;
FirstName = person.FirstName;
SecondName = person.SecondName;
OrderCount = person.Orders.Count;
}
public void Update(Person person) {
person.FirstName = FirstName;
person.SecondName = SecondName;
}
}
public class PersonDetailsViewController {
public PersonWrap Person { get; protected set; }
public PersonDetailsViewController(int personId) {
var person = new PersonRepository().GetById(personId);
if (person != null) {
Person = new PersonWrap(person);
}
}
public void Save() {
using (var context = new MyDbContext()) {
var person = new PersonRepository().GetById(Person.Id, context);
Person.Update(person);
context.SaveChanges();
}
}
}
You are on the right track for creating classes to handle your EF.
The biggest benefit for doing it this way is able to unit test easily.
Test early and test often is always a good idea.
I suggest putting your EF related classes in a separate project.