linq for two tables on dotvvm framework - asp.net

I recently started to learn C# and DotVVM and i run to a problem. I dont know how to make linq query from two tables. I know how to make a query or linq for one table, but i got stuck with two tables.
My database of Authors
CREATE TABLE [dbo].[Autors] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[Jmeno] NVARCHAR (MAX) NULL,
[Prijmeni] NVARCHAR (MAX) NULL,
[ProstredniJmeno] NVARCHAR (MAX) NULL,
[Narozeni] DATE DEFAULT (NULL) NULL,
[Umrti] DATE DEFAULT (NULL) NULL,
[Bio] NVARCHAR (MAX) NULL,
[Narodnost] NVARCHAR (MAX) NULL,
[Obrazek] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_dbo.Autors] PRIMARY KEY CLUSTERED ([ID] ASC));
And my database of Books
CREATE TABLE [dbo].[Books] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Nazev] NVARCHAR (MAX) NOT NULL,
[IdAutor] INT NOT NULL,
[Popis] NVARCHAR (MAX) NULL,
[Isbn] NCHAR (15) NULL,
[IdZanr] INT NULL,
[RokVydani] INT NULL,
[PocetStran] INT NULL,
[Obrazek] TINYINT NULL,
CONSTRAINT [PK_dbo.Books] PRIMARY KEY CLUSTERED ([Id] ASC));
And i need to create linq query to get this
namespace AbsolvetnskaPrace.Models
{
public class AutorListModel
{
public int Id { get; set; }
public string Jmeno { get; set; } //First name
public string Prijmeni { get; set; } //Last name
}
}
namespace AbsolvetnskaPrace.Models
{
public class BookListModel
{
public int Id { get; set; }
public string Nazev { get; set; } //Title
public int IdAutor { get; set; }
public string JmenoAutor { get; set; } //Author's first name
public string PrijmeniAutor { get; set; } //Author's last name
}
}
i have class of Services where i have all linq queries i use. What I need is to make list of books but get a author's name and last name.
This is a view of the list:
<div class="page-center">
<div class="page-grid-top">
<div class="student-image"></div>
<h1>{{resource: Texts.Title_BookList}}</h1>
<dot:AuthenticatedView>
<dot:RouteLink Text="{resource: Texts.Label_NewBook}" RouteName="CRUD_BookCreate" class="page-button btn-add btn-long" />
</dot:AuthenticatedView>
</div>
<dot:GridView DataSource="{value: Books}" class="page-grid">
<Columns>
<dot:GridViewTextColumn ValueBinding="{value: Nazev}" HeaderText="{resource: Texts.Label_Title}" />
<dot:GridViewTextColumn ValueBinding="{value: IdAutor}" HeaderText="{resource: Texts.Label_BookAutor}" />
<dot:GridViewTemplateColumn>
<dot:RouteLink Text="{resource: Texts.Label_Detail}" RouteName="CRUD_BookDetail" Param-Id="{{value: Id}}" />
</dot:GridViewTemplateColumn>
<dot:GridViewTemplateColumn>
<dot:RouteLink Text="{resource: Texts.Label_Edit}" RouteName="CRUD_BookEdit" Param-Id="{{value: Id}}" />
</dot:GridViewTemplateColumn>
</Columns>
<EmptyDataTemplate>
{{resource: Texts.EmptyAutorTable}}
</EmptyDataTemplate>
</dot:GridView>
</div>
And finaly the result looks like this but the numbers in "Jméno a příjmení autora" shuld by Autor's first and last name. Right now the is Author's Id.
I know that this question is really long but I would be thankfull for any advice you can get me.

your classes (entity framework model) are missing navigation properties.
You have to add properties like this:
namespace AbsolvetnskaPrace.Models
{
public class AutorListModel
{
public int Id { get; set; }
public string Jmeno { get; set; } //First name
public string Prijmeni { get; set; } //Last name
--> pubilc ICollection<BookListModel> BookListModels { get; set; }
}
public class BookListModel
{
public int Id { get; set; }
public string Nazev { get; set; } //Titleenter code here
public int IdAutor { get; set; }
--> public AutorListModel Author { get; set; }
public string JmenoAutor { get; set; } //Author's first name
public string PrijmeniAutor { get; set; } //Author's last name
}
}
Then you linq query can look like:
context.Books.Include(a=> a.Authors).Where( -- lambda condition --).Select( -- lambda transformation --).ToList()
DotVVM serializes the objects in ViewModel, so it is highly recommended to transform you EF classes into DTO (data transfer objects) which has no logic and their main purpose is to transfer data and nothing else.

Related

MobileServiceClient IMobileServiceSyncTable PullAsync Resource Not Found

This error "
WindowsAzure.MobileServices The resource you are looking for has been
removed, had its name changed, or is temporarily unavailable"
Occurs when trying to retrieve records from an Azure SQL Db connected to a App Service. It occurs for below line of code
await SurveyResponseTable.PullAsync("SurveyResponse", SurveyResponseTable.CreateQuery()).ConfigureAwait(false);
where;
SurveyResponseTable = mClient.GetSyncTable<SurveyResponse>();
and
mClient = new MobileServiceClient(Constants.BackendUrl);
I have read every SO reference to this error but can find no solution. I am not sure whether the table on the Azure SQL Db has some difference to the class on the mobile client, whether a permissions problem or what.
So at this stage I am asking for suggestions as to how to debug, what to look for (can find nothing that helps me in the exception details). Or maybe how to create a table correctly on server side. I have seen when playing with the ToDoItem example that small things can stop successful connection such as misspellings, EF using plurals on server table name, lower case id etc. Tried all those things but still same error.
Azure SQL table structure;
CREATE TABLE [dbo].[SurveyResponses] (
[id] NVARCHAR (128) DEFAULT (newid()) NOT NULL,
[ThinkingStyle] NVARCHAR (128) NULL,
[ThinkingStyleA] NVARCHAR (128) NOT NULL,
[ThinkingStyleB] NVARCHAR (128) NULL,
[ResponseOrder] INT NOT NULL,
[ResponseGroup] NVARCHAR (128) NOT NULL,
[Question] NVARCHAR (128) NULL,
[ResponseA] NVARCHAR (512) NOT NULL,
[ResponseB] NVARCHAR (512) NULL,
[AzureVersion] ROWVERSION NOT NULL,
[CreatedAt] DATETIMEOFFSET (7) DEFAULT (sysutcdatetime()) NOT NULL,
[UpdatedAt] DATETIMEOFFSET (7) NULL,
[Deleted] BIT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_dbo.SurveyResponses] PRIMARY KEY NONCLUSTERED ([id] ASC));
GO CREATE CLUSTERED INDEX [IX_CreatedAt]
ON [dbo].[SurveyResponses]([CreatedAt] ASC);
GO CREATE TRIGGER [TR_dbo_SurveyResponses_InsertUpdateDelete] ON [dbo].[SurveyResponses] AFTER INSERT, UPDATE, DELETE AS BEGIN UPDATE [dbo].[SurveyResponses] SET [dbo].[SurveyResponses].[UpdatedAt] = CONVERT(DATETIMEOFFSET, SYSUTCDATETIME()) FROM INSERTED WHERE inserted.[id] = [dbo].[SurveyResponses].[id] END
And here is the mobile client class;
public class SurveyResponse
{
public string id { get; set; }
public string ThinkingStyle { get; set; }
public string ThinkingStyleA { get; set; }
public string ThinkingStyleB { get; set; }
public int ResponseOrder { get; set; }
public string ResponseGroup { get; set; }
public string Question { get; set; }
public string ResponseA { get; set; }
public string ResponseB { get; set; }
[Version]
public string AzureVersion { get; set; }
[CreatedAt]
public DateTime CreatedAt { get; set; }
[UpdatedAt]
public DateTime UpdatedAt { get; set; }
}

SQLiteNetExtensions - InsertWithChildrenAsync not working when Foreign Key constraint exists on child table

Xamarin.Forms project, using SQLiteNetExtensions 2.0.0 and sqlite-net-pcl 1.5.231
I am trying to insert new records into a sqlite database. My scenario works when I drop the NOT NULL and Foreign Key constraint on the child table. When I re-add the constraints I get the following exception:
SQLite.SQLiteException: 'Constraint'
Parent Model
[Table("RetailItem")]
public class RetailItemDTO
{
[PrimaryKey, AutoIncrement]
public int RetailItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool Taxable { get; set; }
public int DepartmentId { get; set; }
public int RetailTypeId { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<RetailItemRetailerPriceDTO> Prices { get; set; }
}
Child Model
[Table("RetailItemRetailerPrice")]
public class RetailItemRetailerPriceDTO
{
[PrimaryKey, AutoIncrement]
public int RetailItemRetailerPriceId { get; set; }
[ForeignKey(typeof(RetailItemDTO)), NotNull]
public int RetailItemId { get; set; }
public int RetailerId { get; set; }
public decimal Price { get; set; }
}
Insert/Update logic
public async Task Save(RetailItem entity)
{
var dto = GetDTOFromBusinessModel(entity);
if (dto.RetailItemId == 0)
{
await _sqliteRepository.Database.InsertWithChildrenAsync(dto, true);
entity.Id = dto.RetailItemId;
}
else
await _sqliteRepository.Database.InsertOrReplaceWithChildrenAsync(dto, true);
}
If I have an existing RetailItem, with a newly added price, the InsertOrReplaceWithChildrenAsync call works without issue fails with the following: SQLite.NotNullConstraintViolationException: 'NOT NULL constraint failed: RetailItemRetailerPrice.RetailItemId'
Anytime I add a new RetailItem with a new RetailItemRetailerPrice the Constraint exception occurs.
If I drop the NOT NULL and Foreign Key constraint for the RetailItemId on the RetailItemRetailerPrice table then both calls work.
I want to keep the proper constraints on my table.
Am I missing some attributes that will make my scenario work?
You have set the RetailItemIdnot null ,when you add a new item in RetailItemRetailerPrice table, you must set a RetailItemId for the new price.
[ForeignKey(typeof(RetailItemDTO)), NotNull]
public int RetailItemId { get; set; }
If you do not want to keep the constraints on your table. Before you add a new item in RetailItemRetailerPrice table, you can query the RetailItem table, get the RetailItemId, In the end , you can add this new item in RetailItemRetailerPrice table.

How does Entity Framework Core decide which properties are going to be NOT NULL in the database?

I am using Entity Framework to create a simple banking application for learning purposes. I don't understand how it decides which properties will become nullable and which become non nullable columns in the database.
For example this
model
public class Transaction
{
public int Id { get; set; }
public Account From { get; set; }
public Account To { get; set; }
[DataType(DataType.Currency)]
public decimal Amount { get; set; }
public String Currency { get; set; }
public DateTime Timestamp { get; set; }
}
generates the following
SQL
CREATE TABLE [dbo].[Transactions] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Amount] DECIMAL (18, 2) NOT NULL,
[Currency] NVARCHAR (MAX) NULL,
[FromId] INT NULL,
[Timestamp] DATETIME2 (7) NOT NULL,
[ToId] INT NULL,
CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Transactions_Accounts_FromId] FOREIGN KEY ([FromId]) REFERENCES [dbo].[Accounts] ([Id]),
CONSTRAINT [FK_Transactions_Accounts_ToId] FOREIGN KEY ([ToId]) REFERENCES [dbo].[Accounts] ([Id])
);
GO
CREATE NONCLUSTERED INDEX [IX_Transactions_FromId]
ON [dbo].[Transactions]([FromId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_Transactions_ToId]
ON [dbo].[Transactions]([ToId] ASC);
Question
Why is Currency nullable but not Amount? And what about the foreign keys FromId and ToId?
Appendix
In case it is relevant, here is the Account class referenced by the Transaction class.
public class Account
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
[InverseProperty("From")]
public ICollection<Transaction> TransactionsSend { get; set; }
[InverseProperty("To")]
public ICollection<Transaction> TransactionsReceived { get; set; }
public decimal Balance { get; set; }
}
There is a set of conventions at work here. You can fine-tune everything but the basic rules are clear and simple:
Why is Currency nullable but not Amount? And what about the foreign keys FromId and ToId?
Amount is a value-type. The C# property can't be null When you change it to decimal? the column will be nullable too.
Currency is a reference-type (string ). The From and To properties are references too. References will map to ALLOW-NULL unless you mark them as [Required]
And DateTime is also a value-type.

Update a table when the view has been modified by using a trigger

I have a database in a server as [test]. This [test] database has about fifty tables and I have created a view in that [test] database by using most essential table columns. Then again there's another database in the same server as [list] and I have created a view in the [list] database by referring to the [test] database view. Then I inserted the data in the view of the [list] database to a table in the same [list] database. I need to update my both view and table in the [list] database once the [test] database has been updated.
To do that first I have written a trigger to update the view in the [list] database and it worked. This is the trigger.
Use database list
Create trigger UpdateView on listView Instead of Update
AS
Begin
Update [test].[dbo].[testView]
set testPersonId = testPersonId + 1
From testView
End
Go
Now I need to update my [list] database table once the listView has updated. To do that I have written the following trigger. When I executed the trigger, I got the result of commands completed successfully, but didn't update my table.
Use database list
Create trigger UpdateTable on listTable Instead of Update
AS
Begin
Update [list].[dbo].[listView]
set listPersonId = listPersonId + 1
From listView
End
Go
I just need to know the wrong of this trigger and how to make it work.
Here are the object in my testView.
> testPersonId int
> testPersonName nvarchar
> testDepId int
> testDepName nvarchar
> testLogRecordNowId int
> testLogRecordNowTime datetime
> testNowLogEvent smallint
> testLastLogRecordTime datetime
> testLastLogEvent smallint
> testInTimeHour nvarchar
> testInTimeMinute nvarchar
Here are the objects in my listView.
> listPersonId int
> listPersonName nvarchar
> listDepId int
> listDepName nvarchar
> listLogRecordNowId int
> listLogRecordNowTime datetime
> listNowLogEvent smallint
> listLastLogRecordTime datetime
> listLastLogEvent smallint
> listInTimeHour nvarchar
> listInTimeMinute nvarchar
Then my table in the [list] database has created by the codefirst manner. Here's the code.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonLogId { get; set; }
[Required]
public int PersonId{ get; set; }
[Required, StringLength(32)]
public string PersonName { get; set; }
[Required]
public int DepId { get; set; }
[Required, StringLength(64)]
public string DepName { get; set; }
public int LogRecordNowId { get; set; }
public DateTime LogRecordNowTime { get; set; }
[Required]
public Int16 LogEventNow { get; set; }
public DateTime LogRecordLastTime { get; set; }
[Required]
public Int16 LogEventLast { get; set; }
[Required, StringLength(33)]
public string InTimeHour { get; set; }
[Required]
public int InTimeMinuteMinute { get; set; }
I was able to insert the listView data to this table(listTable). Now I just need to update this table once the listView is updating. Is it possible to do that with triggers? If it is not, is there another way to do that without using triggers?

EF7 generating incorrect sql with optional relationships

Consider the following models:
public class News
{
public int Id { get; set; }
public string Title { get; set; }
public Branch Branch { get; set; }
public int? BranchId { get; set; }
}
public class NewsDto
{
public int Id { get; set; }
public string Title { get; set; }
public BranchDto Branch { get; set; }
}
public class Branch
{
public int Id { get; set; }
public string Name { get; set; }
public List<News> News { get; set; } = new List<News>();
}
public class BranchDto
{
public int Id { get; set; }
public string Name { get; set; }
}
Here we have an optional relationship between News and Branch (1-to-many).
We're issuing the following query:
context.News
.Select(dto => new NewsDto()
{
Id = dto.Id,
Title = dto.Title,
Branch = dto.Branch == null ? null : new BranchDto()
{
Id = dto.Branch.Id,
Name = dto.Branch.Name
}
}).ToList();
EF6
The generated query is:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Extent1].[BranchId] AS [BranchId],
[Extent2].[Name] AS [Name]
FROM [dbo].[News] AS [Extent1]
LEFT OUTER JOIN [dbo].[Branches] AS [Extent2] ON [Extent1].[BranchId] = [Extent2].[Id]
This is how it should be: a left outer join after a CASE WHEN to handle null references to the Branches table.
EF7 (RC1)
The same query is generated as:
SELECT
[dto].[Id],
[dto].[Title],
[dto].[BranchId] IS NULL,
[dto].[BranchId],
[dto.Branch].[Name]
FROM [News] AS [dto]
INNER JOIN [Branch] AS [dto.Branch] ON [dto].[BranchId] = [dto.Branch].[Id]
I don't really understand why this is happening. The relationship is clearly optional so inner joins are just plain wrong. Other than that, the line [dto].[BranchId] IS NULL is giving a System.Data.SqlClient.SqlException: Incorrect syntax near the keyword 'IS' so I guess this isn't even valid sql.
This is only happening when the relationship is optional. What's going on?
So I believe this is related to issue #3186. The EF team recognizes this as a high priority bug and it should be fixed in RC2.

Resources