LINQ إلى SQL - استثناء التعيين عند استخدام الفئات الأساسية المجردة

StackOverflow https://stackoverflow.com/questions/1021274

  •  06-07-2019
  •  | 
  •  

سؤال

مشكلة:أرغب في مشاركة الكود بين تجميعات متعددة.سيحتاج هذا الرمز المشترك إلى العمل مع LINQ للفئات المعينة لـ SQL.

لقد واجهت نفس المشكلة وجدت هنا, ، لكنني وجدت أيضًا حلاً بديلًا أجده مثيرًا للقلق (لن أذهب إلى أبعد من ذلك لأقول "خطأ").

يمكن تنزيل كافة التعليمات البرمجية التالية في هذا الحل.

بالنظر إلى هذا الجدول:

create table Users
(
      Id int identity(1,1) not null constraint PK_Users primary key
    , Name nvarchar(40) not null
    , Email nvarchar(100) not null
)

وتعيين DBML هذا:

<Table Name="dbo.Users" Member="Users">
  <Type Name="User">
    <Column Name="Id" Modifier="Override" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
    <Column Name="Name" Modifier="Override" Type="System.String" DbType="NVarChar(40) NOT NULL" CanBeNull="false" />
    <Column Name="Email" Modifier="Override" Type="System.String" DbType="NVarChar(100) NOT NULL" CanBeNull="false" />
  </Type>
</Table>

لقد قمت بإنشاء الفئات الأساسية التالية في تجميع واحد "مشترك":

namespace TestLinq2Sql.Shared
{
    public abstract class UserBase
    {
        public abstract int Id { get; set; }
        public abstract string Name { get; set; }
        public abstract string Email { get; set; }
    }

    public abstract class UserBase<TUser> : UserBase where TUser : UserBase
    {
        public static TUser FindByName_Broken(DataContext db, string name)
        {
            return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name);
        }

        public static TUser FindByName_Works(DataContext db, string name)
        {
            return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name && 1 == 1);
        }

        public static TUser FindByNameEmail_Works(DataContext db, string name, string email)
        {
            return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name || u.Email == email);
        }
    }
}

تتم الإشارة إلى هذه الفئات في تجميع آخر "رئيسي"، كما يلي:

namespace TestLinq2Sql
{
    partial class User : TestLinq2Sql.Shared.UserBase<User>
    {

    }
}

يوجد ملف DBML في المجموعة "الرئيسية" أيضًا.

عند الاتصال User.FindByName_Broken(db, "test"), ، تم طرح استثناء:

System.InvalidOperationException:لم يتم تعيين UserBase.Name لعضو الفئة.

ومع ذلك، تعمل الطريقتان الأساسيتان الأخريان.

علاوة على ذلك، يتم إنشاء SQL عن طريق الاتصال User.FindByName_Works(db, "test") هذا ما كنا نأمله في المكالمة المقطوعة:

SELECT TOP (1) [t0].[Id], [t0].[Name], [t0].[Email]
FROM [dbo].[Users] AS [t0]
WHERE [t0].[Name] = @p0
-- @p0: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [test]

بينما أنا على استعداد لاستخدام هذا 1 == 1 "اختراق" للاستعلامات الأصلية الفردية، هل هناك طريقة أفضل لمشاركة LINQ مع التعليمات البرمجية المدركة لـ SQL في تجميع أساسي/مشترك/أساسي؟

هل كانت مفيدة؟

المحلول

لقد واجهت هذه المشكلة عدة مرات في الماضي لأن لدينا بنية مماثلة في إطار العمل الذي نستخدمه في شركتنا.ربما لاحظت أنه إذا كنت تستخدم استعلامات LINQ ذات النمط التعريفي فلن تواجه هذه المشكلة.على سبيل المثال سوف يعمل الكود التالي:

return (from i in db.GetTable<TUser>() where i.Name = "Something").FirstOrDefault();

ومع ذلك، نظرًا لأننا نستخدم تعبيرات التصفية الديناميكية، فلا يمكننا استخدام هذه الطريقة.الحل البديل هو استخدام شيء مثل هذا:

return db.GetTable<TUser>().Select(i => i).Where(i => i.Name == "Something").SingleOrDefault();

لقد حل هذا الحل مشكلتنا حيث يمكننا إدخال ".Select(i => i)" في بداية جميع التعبيرات تقريبًا.سيؤدي هذا إلى عدم قيام محرك Linq بالنظر إلى الفئة الأساسية للتعيينات وسيجبره على النظر إلى فئة الكيان الفعلية والعثور على التعيينات.

نأمل أن يساعد

نصائح أخرى

حاول تضمين OfType قبل جملة Where

return _dbContext.GetTable<T>().OfType<T>().Where(expression).ToList();

لقد حالفني الحظ في تحديد فئات البيانات في تجميع مشترك واستهلاكها في العديد من التجميعات مقابل تعيين فئات بيانات العديد من التجميعات إلى عقد مشترك.باستخدام أمثلة مساحات الأسماء، ضع DataContext مخصصًا وفئات البيانات المشتركة في TestLinq2Sql.Shared:

namespace TestLinq2Sql.Shared
{
    public class SharedContext : DataContext
    {
        public Table<User> Users;
        public SharedContext (string connectionString) : base(connectionString) { }
    }

    [Table(Name = "Users")]
    public class User
    {
        [Column(DbType = "Int NOT NULL IDENTITY", IsPrimaryKey=true, CanBeNull = false)]
        public int Id { get; set; }

        [Column(DbType = "nvarchar(40)", CanBeNull = false)]
        public string Name { get; set; }

        [Column(DbType = "nvarchar(100)", CanBeNull = false)]
        public string Email { get; set; }
    }
}

ثم استهلك DataContext من أي تجميع آخر:

using (TestLinq2Sql.Shared.SharedContext shared = 
    new TestLinq2Sql.Shared.SharedContext(
        ConfigurationManager.ConnectionStrings["myConnString"].ConnectionString))
{
    var user = shared.Users.FirstOrDefault(u => u.Name == "test");
}  

يبدو هذا وكأنه خطأ - قمنا بحالة خاصة على مفتاح أساسي لإجراء بحث محلي ولكن يبدو أن مسار التعليمات البرمجية هذا لا يلتقط البيانات التعريفية بشكل صحيح.

سيعني الاختراق 1=1 أن الأمر يتم عبر قاعدة بيانات عادية ذهابًا وإيابًا ولكن يجب الإبلاغ عن خطأ بالفعل...

أنت تطرح عدة أسئلة هنا يا جارود، هل يمكنك أن تكون أكثر تحديدًا؟أي هل تريد فقط أن تعرف سبب فشل طريقتك؟أو ربما تريد طريقة لاستخدام كائنات البيانات عبر مشاريع مختلفة؟أفترض أنك لا تحاول استخدام LINQ إلى SQL كطبقة تعيين لقاعدة البيانات وأنك تستخدمها كنموذج مجال؟في هذه الحالة، هل يقوم كلا التطبيقين بتنفيذ نفس المجال (العمليات التجارية، التحقق من الصحة، وما إلى ذلك)؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top