Map List object using SQL Server and Dapper - sql-server

I'm using SQL Server and Dapper and I want to properly store my models object into database and retrieve them.
That's my model, the guid list is list of other model 'Generator' IDs.
public class GeneratorSet
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Guid> GeneratorsList { get; set; }
}
My goal is to correctly map this object to a SQL Server table and then using Dapper correctly retrieve my objects from database. The relationship is many to many (set can 'posses' many generators, and generator may be possesed by many sets).

You can do this using the SplitOn parameter... Here is a guide;
https://taylorhutchison.github.io/2016/03/23/dapper-orm-complex-queries.html
Or just by having unique names and mapping using a lambda - using the example from the documentation (https://github.com/StackExchange/Dapper);
var sql =
#"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post;});
var post = data.First();
Assert.Equal("Sams Post1", post.Content);
Assert.Equal(1, post.Id);
Assert.Equal("Sam", post.Owner.Name);
Assert.Equal(99, post.Owner.Id);
I can't write the actual code/query as I don't know your database schema... but hopefully you get the idea?

Related

SQL Server table refreshing through ASP.NET Core MVC and Entity Framework

I am creating a website for a warehouse using ASP.NET Core MVC and Entity Framework. There are over 5000 tools and equipment in this warehouse.
I have a model class like this:
public class Tool
{
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
I have another table which keeps the log of all of the inputs and outputs of tools which is like:
public class Transaction
{
public long Id { get; set; }
public string FormId { get; set; }
public DateTime Date { get; set; }
public bool IsInput { get; set; } // if input 1 if output 0
public float Quantity { get; set; }
public Tool Item { get; set; } //equipment
public string Description { get; set; }
}
Each day there will at least be 300 rows added to the Transaction table and in 3 years it will be over 300,000 rows. To get the quantity of an individual tool I did something like:
database.getTools()
.where(x => x.Id == ID)
.where(x => x.IsInput == true)
.select(x => x.quantity).sum() - //all inputs for this tool
database.getTools()
.where(x => x.Id == ID)
.where(x => x.IsInput == false)
.select(x => x.quantity).sum(); //all outputs for this tool
I am concerned that after sometime (few years) this function will be really time consuming especially if it has to iterate through all tools of the warehouse. One of the best ways is to make a fresh new table at the end of the warehouse counting period and initialize all of the tools with their closing stock quantity. This will make sure that the number of rows of the transaction table do not grow indefinitely.
But how to do this? What should I search for?
If my approach is not correct please correct me.
One more thing is that this is not the only purpose of the website which means that there are other tables doing some other things that I don't want to be affected by this process, what I mean is I cannot make a new database.
I am new to all of this so please keep it as simple as possible.
Thanks
well, First you are using a Database Management System which handles the numbers of rows you mentioned easily.
second, you don't need to use the "where" method two times, you don't even need it.
Also, you don't need "sum" as you filtering using the ID
when you use where to filter by ID it returns an IEnumerable(list) of type your class that contains one object.
I would use "SingleOrDefault" instead of "Where" method to achieve this, it returns an object for you
Here is an example
Transaction trans = _context.Transactions.SingleOrDefault(x=> x.Id == ID && x.IsInput == true);
where _context is your context object and Transactions is your DbSet property
Then you can use the object to access the quantity.
var quantity = trans.Quantity

How to Map Sql Query to EF Models when using FromSql

I am trying to use .FromSql but I keep getting an exception
"Required Column Id is Missing"
Now all my Models have Id so I have no clue which one I am missing, or if I am writing my "As" statements wrong and it is not mapping properly.
var inventoryItems = dbContext.InventoryItems.AsNoTracking().FromSql(#"SELECT Brands.Id AS BrandsId, InventoryItems.Id AS InventoryItemsId,
Companies.Id AS CompaniesIds, Countries.Id AS CountriesId, States.Id AS StatesId, Branches.Id AS BranchesId,
Branches.CountryId, Branches.StateId, States.CountryId AS StatesCountryId, InventoryItems.InventoryCategoryId, InventoryItems.BrandId, InventoryItems.BranchId, Branches.CompanyId
FROM Branches INNER JOIN
Brands ON Branches.Id = Brands.Id INNER JOIN
Companies ON Branches.CompanyId = Companies.Id INNER JOIN
Countries ON Branches.CountryId = Countries.Id INNER JOIN
InventoryItems ON Branches.Id = InventoryItems.BranchId AND Brands.Id = InventoryItems.BrandId INNER JOIN
States ON Branches.StateId = States.Id AND Countries.Id = States.CountryId ).ToList();
Model example and all my models follow the same pattern
public class InventoryItem
{
public int Id { get; set; } //PK name
public int InventoryCategoryId { get; set; } // FK name pattern
public int BranchId { get; set; }
public virtual Branch Branch { get; set; }
public int BrandId { get; set; }
public virtual Brand Brand { get; set; }
}
I am using FromSql because I need to do a where clause(not shown) that filters on a json column with EF core does not support.
a .FromSql query returns a single Entity Type. That query should return Id, InventoryCategoryId, BranchId, BrandId, one column for each property of Inventory Item (except for Navigation Properties).
If you want to load multiple different entity types, you can't use a single .FromSql() call. Instead use one of the methods detailed here. Especially note:
Entity Framework Core will automatically fix-up navigation properties
to any other entities that were previously loaded into the context
instance. So even if you don't explicitly include the data for a
navigation property, the property may still be populated if some or
all of the related entities were previously loaded.
So you can fetch the Entities you need across multiple seperate queries if you want. But this "fix-up" doesn't happen if you suppress change tracking on your Entities, as it's the change tracker that does this.

How do I use a composite key for a one to many relationship in Code First EF

I am using EF Code First.
I need two tables, LedgerCategories and LedgerSubCategories with a one-to-many relationship (Categories -> SubCategories), with the keys in each being codes (strings) - i.e. LedgerCategoryCode and LedgerSubCategoryCode respectively. However, I need to allow the SubCategoryCode values to be the same for different Categories.
E.g. CategoryCode = REHAB, SubCategoryCodes = MATL, CONTR, and FEES; and CategoryCode = MAINT, SubCategoryCodes = MATL, CONTR, and FEES.
I'm thinking I need to use a composite key and include both the CategoryCode and SubCategoryCode fields in the LedgerSubCategories table. Currently I have:
public class LedgerCategory
{
[Key]
public string LedgerCategoryCode { get; set; }
public string Description { get; set; }
public List<LedgerSubCategory> LedgerSubCategories { get; set; }
}
public class LedgerSubCategory
{
[Key, Column(Order = 0)]
public string LedgerCategoryCode { get; set; }
[Key, Column(Order = 1)]
public string LedgerSubCategoryCode { get; set; }
public string Description { get; set; }
}
I am seeding these tables using only instances of the LedgerCategory class, having each contain a List of appropriately instantiated LedgerSubCategory classes. This appears to both set up the DB schema correctly (in my perception), and populate both tables appropriately.
But, when I reinstantiate a simple List of LedgerCategory, i.e.
using (var db = new BusinessLedgerDBContext())
{
var LedgerCategories = db.LedgerCategories.ToList();
}
The LedgerCategory instances don't contain their respective List of associated LedgerSubCategory instances.
I am trying to avoid, what seems like a kludge, to introduce a unique number or Guid ID field in LedgerSubCategories as a PK and just index off the other Code fields. I haven't tried this, but I'm not sure it would cause any different results for reinstantiating the LedgerCategories and getting associated LedgerSubCategories.
Any advice on how to do this appropriately and get proper results is appreciated.
To, I suppose, answer my own question, I have found that overriding OnModelCreating() in the respective DbContext with Fluent API to establish the one to many relationship and foreign key when the Code First framework establishes the desired DB Schema. There appears no other way to do this, such as with Attributes. By many accounts of others, including MSDN, Fluent API appears to be what is needed. However, that has led me to a new issue, or set of issues, which I've posed as a question here.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures the one-many relationship between Categories and
// SubCategories, and established the Foreign Key in SubCategories
modelBuilder.Entity<Category>()
.HasMany<SubCategory>(c => c.SubCategories)
.WithRequired(s => s.Category)
.HasForeignKey<string>(s => s.CategoryCode);
}

How can Entity Framework connect to a SQL Server view rather than table? [duplicate]

I am developing a contact log in a website using VS 2010, MVC3 and EF 5 - the entities are created using code first. The data is stored in an SQL Server 2008 R2 set of databases. I want to display a summary of the contact log and have created a view.
CREATE VIEW dbo.ContactLogSummaries
AS
SELECT
CLE.ContactLogEntryID,
CLE.CaseID,
'Test' AS ContactName,
EU.UserName As OfficeUser,
CLE.DateAndTimeOfContact,
CLC.Category,
CLE.ContactDetails
FROM
ContactLogEntries AS CLE
JOIN
ContactLogCategories AS CLC
ON CLE.ContactLogCategoryID = CLC.ContactLogCategoryID
JOIN
Control.dbo.EndUsers AS EU
ON CLE.UserID = EU.EnduserID
There are two entities in the Contact Log database (ContactLogEntries and ContactLogCategories) and a database first entity Control.dbo.EndUsers in another database. The contact log could contain a large number of records. I want to be able to display just the records for a specific case.
My question is in two parts:
Can I use the SQL view directly to display a summary on a web page (perhaps by reading it into a class)
Can I create a code first object equivalent to the SQL view.
You can just map the Entity directly to the view using TableAttribute (data annoations), or ToTable in your Fluent Mappings...
For example using data annotions:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public namespace whatever.mynamespace
[Table("dbo.ContactLogSummaries")] //<-- this is your view
public class ContactLogSummary
{
...
}
}
Found a simple solution to question 1:
public class ContactLogSummary
{
public int ContactLogEntryID { get; set; }
public int MaternalCaseID { get; set; }
public String ContactName { get; set; }
public String OfficeUser { get; set; }
public DateTime DateAndTimeOfContact { get; set; }
public String Category { get; set; }
public String ContactDetails { get; set; }
public static List<ContactLogSummary> LoadContactListSummary
(int caseID, String connectionString);
{
MyDataContext dbContext = new MyDataContext(connectionString);
return dbContext.Database.SqlQuery<ContactLogSummary>
("SELECT * FROM dbo.ContactLogSummaries WHERE MaternalCaseID = #CaseID ORDER BY ContactLogEntryID DESC",
new SqlParameter("CaseID", caseID)).ToList();
}
It does all that's required so, although I'm interest in an answer to question 2 I have a working solution.

MVC Model Confusion

I have confusion regarding models when we have relations between multiple tables. Formerly, I was using views or stored procedures of SQL to manipulate data but now I am using Entity Framework so confused how my model should look like ?
I have a table for Users, UserImages, UserRoles.
UserImages and UserRoles is referring to UserID from Users table.I am not sure how my model should be. While displaying data I generally use a view created in SQL server by joining these 2 tables. And when an user is edited or created I update these 3 tables.
Not sure what should my model look like in this case ? Do I create a SQL server view and add it to edmx ? Or add 3 tables separately in EDMX and create custom properties in User model for those other 2 tables?
[HttpPost]
public ActionResult Create(UserFormViewModel UserView)
{
User user = UserView.User;
try {
if (ModelState.IsValid) {
repository.AddUser(user);
repository.Save();
return View(new UserFormViewModel(user));
} else {
return View(new UserFormViewModel(user));
}
} catch (Exception ex) {
ModelState.AddModelError("Error", ex.Message);
return View(new UserFormViewModel(user));
}
}
public class UserFormViewModel {
UucsrRepository repository = new UucsrRepository();
public User User { get; private set; }
public SelectList States { get; private set; }
public SelectList Genders { get; private set; }
public SelectList RolesLists { get; private set; }
public SelectList SelectedRolesLists{ get; private set; }
public UserFormViewModel(User contact) {
User = contact;
States = new SelectList(repository.GetAllStates() ,"ShortName","Name");
Genders = new SelectList(repository.GetAllGenders(), "Gender", "Description");
RolesLists = new SelectList(repository.GetAllRolesLists(), "Id", "Name");
}
}
I am not sure how should I exactly handle the Adding role list and images here .
The User class should have a list of Roles and Images. The tables should have foreign keys to each other by UserId. When generating the models from your tables select all the tables you want Entity Framework to use. The User model should in that case automatically have a List of UserRoles and UserImages. Change the names appropriately.
When adding or changing roles or images you should fetch the user and update or add them to the correct list.
That's how I would do it. In this case your User is an aggregate root. Check out DDD you're interested.

Resources