ক্যোয়ারী কমান্ড এবং / অথবা নির্দিষ্টকরণগুলি ভালভাবে ডিজাইন করা হয়েছে


92

টিপিক্যাল রিপোজিটরি প্যাটার্ন দ্বারা উপস্থাপিত সমস্যাগুলির জন্য ভাল সমাধানের জন্য আমি বেশ কিছুদিন অনুসন্ধান করে যাচ্ছিলাম (বিশেষ প্রশ্নের জন্য পদ্ধতিগুলির তালিকা বৃদ্ধি ইত্যাদি) দেখুন: http://ayende.com/blog/3955/repository- is-the-new-singleton )।

বিশেষ করে স্পেসিফিকেশন প্যাটার্ন ব্যবহারের মাধ্যমে কমান্ড ক্যোয়ারীগুলি ব্যবহার করার ধারণাটি আমি সত্যিই পছন্দ করি। তবে, স্পেসিফিকেশনের সাথে আমার সমস্যাটি হ'ল এটি কেবলমাত্র সাধারণ নির্বাচনের মানদণ্ডের সাথে সম্পর্কিত (মূলত যেখানে ক্লজ), এবং যোগদানের, গ্রুপিং, সাবসেট নির্বাচন বা প্রজেকশন ইত্যাদির মতো অন্যান্য প্রশ্নের সাথে কাজ করে না .. মূলত, সমস্ত অতিরিক্ত হুপস অনেক প্রশ্নের অবশ্যই তথ্যের সঠিক সেট পেতে অবশ্যই যেতে হবে।

(দ্রষ্টব্য: আমি কমান্ড "শব্দটি কমান্ড প্যাটার্ন হিসাবে ব্যবহার করি, যা ক্যোয়ারী অবজেক্টস হিসাবেও পরিচিত I'm আমি কমান্ড / ক্যোয়ারী বিভাজন হিসাবে কমান্ডের বিষয়ে বলছি না যেখানে প্রশ্ন এবং কমান্ডের মধ্যে পার্থক্য রয়েছে (আপডেট, মুছুন, sertোকান))

সুতরাং আমি এমন বিকল্পগুলির সন্ধান করছি যা পুরো ক্যোয়ারিকে encapsulate করে, তবে এখনও যথেষ্ট নমনীয় যে আপনি কেবল কমান্ড ক্লাসগুলির বিস্ফোরণের জন্য স্প্যাগেটি রিপোজিটরিগুলি অদলবদল করছেন না।

উদাহরণস্বরূপ, লিনকস্পেকস আমি ব্যবহার করেছি এবং যখন আমি নির্বাচনের মানদণ্ডে অর্থবোধক নামগুলি নির্ধারণ করতে সক্ষম হতে কিছু মূল্য খুঁজে পাই, এটি যথেষ্ট নয়। সম্ভবত আমি এক মিশ্রিত সমাধান চাই যা একাধিক পদ্ধতির সমন্বয় করে।

আমি এমন সমাধানগুলির সন্ধান করছি যা অন্যরা এই সমস্যার সমাধান করতে বা অন্য কোনও সমস্যার সমাধানের জন্য বিকাশ করেছে তবে এখনও এই প্রয়োজনীয়তাগুলি পূরণ করে ies সংযুক্ত নিবন্ধে, আয়েন্দি হাইবারনেট প্রসঙ্গটি সরাসরি ব্যবহার করার পরামর্শ দিয়েছিল তবে আমি মনে করি যে আপনার ব্যবসায়ের স্তরটি বেশিরভাগ ক্ষেত্রে জটিল করে তোলে কারণ এটিতে এখন কোয়েরি তথ্যও থাকতে হবে।

অপেক্ষার সময়টি অতিবাহিত হওয়ার সাথে সাথে আমি এটিতে একটি অনুদান প্রদান করব। সুতরাং দয়া করে ভাল সমাধানের সাথে আপনার সমাধানগুলিকে অনুগ্রহযোগ্য করে তুলুন এবং আমি সেরা সমাধানটি নির্বাচন করব এবং রানার্স আপকে আপগেট করব।

দ্রষ্টব্য: আমি এমন কিছু সন্ধান করছি যা ORM ভিত্তিক। স্পষ্টভাবে EF বা nHibernate হতে হবে না, তবে সেগুলি সবচেয়ে সাধারণ এবং সেরাটি ফিট করে fit এটি যদি অন্য ওআরএম-এর সাথে সহজেই মানিয়ে নেওয়া যায় তবে তা বোনাস হবে। লিনক সামঞ্জস্যপূর্ণও ভাল লাগবে।

আপডেট: আমি সত্যিই অবাক হয়েছি যে এখানে অনেক ভাল পরামর্শ নেই। দেখে মনে হচ্ছে লোকেরা হয় পুরোপুরি সিকিউআরএস, অথবা তারা সম্পূর্ণরূপে সংগ্রহস্থল শিবিরে। আমার বেশিরভাগ অ্যাপ্লিকেশনগুলি সিকিউআরএসের ওয়ারেন্ট দেওয়ার পক্ষে যথেষ্ট জটিল নয় (বেশিরভাগ সিকিউআরএসের পক্ষে অ্যাডভোকেটসের সাথে সহজেই বলা হয় যে এটির জন্য আপনার ব্যবহার করা উচিত নয়)।

আপডেট: এখানে কিছুটা বিভ্রান্তি রয়েছে বলে মনে হচ্ছে। আমি একটি নতুন ডেটা অ্যাক্সেস প্রযুক্তি খুঁজছি না, বরং ব্যবসায় এবং ডেটাগুলির মধ্যে যুক্তিসঙ্গতভাবে সু-নকশিত ইন্টারফেস।

আদর্শভাবে, আমি যা খুঁজছি তা হল কোয়েরি অবজেক্টস, স্পেসিফিকেশন প্যাটার্ন এবং সংগ্রহস্থলের মধ্যে এক ধরণের ক্রস। যেমন আমি আগেই বলেছি, স্পেসিফিকেশন প্যাটার্নটি কেবল যেখানে ক্লোজের দিকের সাথে সম্পর্কিত, এবং কোয়েরির অন্যান্য দিকগুলি যেমন যোগ দেয়, উপ-নির্বাচনগুলি ইত্যাদির সাথে সম্পর্কিত হয় না os । ক্যোয়ারী অবজেক্টগুলি পুরো ক্যোয়ারির সাথেও ডিল করে, তবে আমি কোয়েরি অবজেক্টগুলির বিস্ফোরণগুলির সাথে সহজেই সংগ্রহস্থলগুলি প্রতিস্থাপন করতে চাই না।


4
চমত্কার প্রশ্ন। আমিও আমার পরামর্শের চেয়ে বেশি অভিজ্ঞতার সাথে লোকেরা কী দেখতে চাই। আমি এই মুহুর্তে একটি কোড বেসে কাজ করছি যেখানে জেনেরিক সংগ্রহশালায় কমান্ড অবজেক্টস বা ক্যোয়ারী অবজেক্টগুলির জন্য ওভারলোডগুলি রয়েছে, যার কাঠামো আয়েন্দে তার ব্লগে বর্ণিত মত। পিএস: এটি প্রোগ্রামারস.এসইতেও কিছুটা দৃষ্টি আকর্ষণ করতে পারে।
সাইমন হোয়াইটহেড

আপনি যদি লিনকিউয়ের উপর নির্ভরশীলতা মনে না করেন তবে কেবল আইকোয়ারিযোগ্যকে উদ্ভাসিত করে এমন একটি সংগ্রহস্থল কেন ব্যবহার করবেন না? একটি সাধারণ পন্থা হল জেনেরিক সংগ্রহশালা এবং তারপরে আপনার যখন উপরে পুনরায় ব্যবহারযোগ্য যুক্তি প্রয়োজন হয় তখন আপনি আপনার অতিরিক্ত পদ্ধতিগুলির সাহায্যে একটি উদ্ভূত সংগ্রহস্থল তৈরি করেন।
ডিডিজিটাল

@ দেবডিজিটাল - লিনকের উপর নির্ভরতা ডেটা প্রয়োগের উপর নির্ভরশীলতার মতো নয়। আমি বস্তুগুলিতে লিনক ব্যবহার করতে চাই, তাই আমি অন্য ব্যবসায় স্তর ফাংশনগুলি বাছাই করতে পারি। তবে এর অর্থ এই নয় যে আমি ডেটা মডেল বাস্তবায়নের উপর নির্ভরতা চাই। আমি এখানে যা বলছি তা হ'ল স্তর / স্তরের ইন্টারফেস। উদাহরণস্বরূপ, আমি একটি ক্যোয়ারী পরিবর্তন করতে সক্ষম হতে চাই এবং 200 টি জায়গায় এটি পরিবর্তন করতে হবে না, যা আপনি আইকুয়ারেবলকে সরাসরি ব্যবসায়ের মডেলটিতে ঠেলে দিলে যা হয়।
এরিক ফানকেনবাশ

4
@ দেবডিজিটাল - যা মূলত কেবল আপনার ব্যবসায়িক স্তরের মধ্যে একটি সংগ্রহস্থল সহ সমস্যাগুলি সরিয়ে দেয়। আপনি কেবল সমস্যাটি প্রায় চারদিকে বদলাচ্ছেন।
এরিক ফানকেনবাশ

4
@ মিস্ত্রিমন এই 2 টি নিবন্ধটি একবার দেখুন: ব্লগ.gauffin.org/2012/10/gigffin-decoupled-the-queries এবং cuttingedge.it/blogs/steven/pivot/entry.php?id=92
ডেভিড.এস

উত্তর:


95

দাবি অস্বীকার: যেহেতু এখনও পর্যন্ত কোনও দুর্দান্ত উত্তর নেই, তাই আমি বেশ কিছু কাল পূর্বে পড়া একটি দুর্দান্ত ব্লগ পোস্ট থেকে একটি অংশ পোস্ট করার সিদ্ধান্ত নিয়েছিলাম, প্রায় ভার্বামটি অনুলিপি করেছি। আপনি এখানে সম্পূর্ণ ব্লগ পোস্ট পেতে পারেন । সুতরাং এটি এখানে:


আমরা নিম্নলিখিত দুটি ইন্টারফেস সংজ্ঞায়িত করতে পারি:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

IQuery<TResult>নির্দিষ্ট করে একটি বার্তা যে ডেটা এটি ব্যবহার ফেরৎ একটি নির্দিষ্ট প্রশ্নের সাথে সংজ্ঞায়িত TResultজেনেরিক প্রকার। পূর্বনির্ধারিত ইন্টারফেসের সাহায্যে আমরা একটি কোয়েরি বার্তাটিকে এভাবে সংজ্ঞায়িত করতে পারি:

public class FindUsersBySearchTextQuery : IQuery<User[]>
{
    public string SearchText { get; set; }
    public bool IncludeInactiveUsers { get; set; }
}

এই শ্রেণিটি দুটি পরামিতি সহ একটি ক্যোয়ারী অপারেশন সংজ্ঞায়িত করে, যার ফলে Userঅবজেক্টগুলির অ্যারে হবে in এই বার্তাটি পরিচালনা করে এমন শ্রেণিটি নীচে সংজ্ঞায়িত করা যেতে পারে:

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, User[]>
{
    private readonly NorthwindUnitOfWork db;

    public FindUsersBySearchTextQueryHandler(NorthwindUnitOfWork db)
    {
        this.db = db;
    }

    public User[] Handle(FindUsersBySearchTextQuery query)
    {
        return db.Users.Where(x => x.Name.Contains(query.SearchText)).ToArray();
    }
}

আমরা এখন গ্রাহকদের জেনেরিক IQueryHandlerইন্টারফেসের উপর নির্ভর করতে পারি :

public class UserController : Controller
{
    IQueryHandler<FindUsersBySearchTextQuery, User[]> findUsersBySearchTextHandler;

    public UserController(
        IQueryHandler<FindUsersBySearchTextQuery, User[]> findUsersBySearchTextHandler)
    {
        this.findUsersBySearchTextHandler = findUsersBySearchTextHandler;
    }

    public View SearchUsers(string searchString)
    {
        var query = new FindUsersBySearchTextQuery
        {
            SearchText = searchString,
            IncludeInactiveUsers = false
        };

        User[] users = this.findUsersBySearchTextHandler.Handle(query);    
        return View(users);
    }
}

অবিলম্বে এই মডেল আমাদের, নমনীয়তা অনেক দেয়, কারণ আমরা এখন সিদ্ধান্ত নিতে পারেন কি মধ্যে উদ্বুদ্ধ করতে UserController। আমরা UserController(এবং সেই ইন্টারফেসের অন্যান্য সমস্ত গ্রাহক) পরিবর্তন না করেই সম্পূর্ণ ভিন্ন বাস্তবায়ন বা বাস্তব বাস্তবায়নকে গুটিয়ে ফেলা একটি ইনজেক্ট করতে পারি ।

IQuery<TResult>ইন্টারফেস আমাদের কম্পাইল-টাইম সমর্থন যখন নির্দিষ্ট বা ইনজেকশনের দেয় IQueryHandlersআমাদের কোডে। আমরা যখন পরিবর্তন FindUsersBySearchTextQueryফিরতে UserInfo[]পরিবর্তে (প্রয়োগ করে IQuery<UserInfo[]>), UserController, কম্পাইল করতে ব্যর্থ থেকে জেনেরিক টাইপ বাধ্যতা হবে IQueryHandler<TQuery, TResult>ম্যাপ পারবে না FindUsersBySearchTextQueryকরতে User[]

IQueryHandlerইন্টারফেসটি গ্রাহক হিসাবে ইনজেক্ট করার ক্ষেত্রে কিছু কম স্পষ্ট সমস্যা রয়েছে যা এখনও সমাধান করা দরকার। আমাদের ভোক্তাদের নির্ভরতার সংখ্যা খুব বেশি বেড়ে যেতে পারে এবং কনস্ট্রাক্টরকে ওভার-ইনজেকশন দিতে পারে - যখন কোনও কনস্ট্রাক্টর অনেক বেশি যুক্তি নেন। কোনও শ্রেণি নির্বাহ করে এমন প্রশ্নের সংখ্যা প্রায়শই বদলে যেতে পারে, যার জন্য কনস্ট্রাক্টর আর্গুমেন্টের সংখ্যায় ধ্রুবক পরিবর্তন প্রয়োজন।

IQueryHandlersবিমূর্ততার অতিরিক্ত স্তর দিয়ে আমরা অনেকগুলি ইনজেকশনের সমস্যাটি সমাধান করতে পারি । আমরা একটি মধ্যস্থতাকারী তৈরি করি যা গ্রাহক এবং ক্যোয়ারি হ্যান্ডলারের মধ্যে বসে থাকে:

public interface IQueryProcessor
{
    TResult Process<TResult>(IQuery<TResult> query);
}

IQueryProcessorএক generic পদ্ধতি সম্পর্কে একটি অ-জেনেরিক ইন্টারফেস। আপনি ইন্টারফেস সংজ্ঞা দেখতে পারেন, ইন্টারফেস IQueryProcessorউপর নির্ভর করে IQuery<TResult>। এটি আমাদের উপর নির্ভর করে আমাদের গ্রাহকদের মধ্যে সংকলন সময় সমর্থন করতে দেয় IQueryProcessor। নতুনটি UserControllerব্যবহার করতে পুনরায় লিখুন IQueryProcessor:

public class UserController : Controller
{
    private IQueryProcessor queryProcessor;

    public UserController(IQueryProcessor queryProcessor)
    {
        this.queryProcessor = queryProcessor;
    }

    public View SearchUsers(string searchString)
    {
        var query = new FindUsersBySearchTextQuery
        {
            SearchText = searchString,
            IncludeInactiveUsers = false
        };

        // Note how we omit the generic type argument,
        // but still have type safety.
        User[] users = this.queryProcessor.Process(query);

        return this.View(users);
    }
}

UserControllerএখন একটি উপর নির্ভর করে IQueryProcessorআমাদের প্রশ্নের সব ব্যবস্থা করতে সক্ষম। UserControllerএর SearchUsersপদ্ধতি কল IQueryProcessor.Processপদ্ধতি একটি সক্রিয়া ক্যোয়ারী বস্তু ক্ষণস্থায়ী। যেহেতু ইন্টারফেসটি FindUsersBySearchTextQueryপ্রয়োগ করে IQuery<User[]>, তাই আমরা এটিকে জেনেরিক Execute<TResult>(IQuery<TResult> query)পদ্ধতিতে প্রেরণ করতে পারি । সি # টাইপ অনুমানের জন্য ধন্যবাদ, সংকলক জেনেরিক টাইপ নির্ধারণ করতে সক্ষম হয় এবং এটি আমাদের স্পষ্টভাবে টাইপটি জানাতে বাঁচায়। Processপদ্ধতির রিটার্ন টাইপও জানা যায়।

এটা এখন বাস্তবায়ন দায়িত্ব IQueryProcessorডান খুঁজে পেতে IQueryHandler। এর জন্য কিছু গতিশীল টাইপিং প্রয়োজন হয় এবং allyচ্ছিকভাবে নির্ভরতা ইনজেকশন ফ্রেমওয়ার্ক ব্যবহার করা হয় এবং কোডের কয়েকটি লাইন দিয়ে সমস্ত করা যেতে পারে:

sealed class QueryProcessor : IQueryProcessor
{
    private readonly Container container;

    public QueryProcessor(Container container)
    {
        this.container = container;
    }

    [DebuggerStepThrough]
    public TResult Process<TResult>(IQuery<TResult> query)
    {
        var handlerType = typeof(IQueryHandler<,>)
            .MakeGenericType(query.GetType(), typeof(TResult));

        dynamic handler = container.GetInstance(handlerType);

        return handler.Handle((dynamic)query);
    }
}

QueryProcessorবর্গ একটি নির্দিষ্ট নির্মান IQueryHandler<TQuery, TResult>সরবরাহকৃত ক্যোয়ারী উদাহরণস্বরূপ ধরনের উপর ভিত্তি করে প্রকার। এই ধরণের সরবরাহকৃত ধারক শ্রেণীর কাছে এই ধরণের উদাহরণ পেতে জিজ্ঞাসা করা হয়। দুর্ভাগ্যক্রমে Handleপ্রতিবিম্বটি ব্যবহার করে আমাদের পদ্ধতিটি কল করতে হবে (এই ক্ষেত্রে সি # 4.0 ডাইমমিক কীওয়ার্ডটি ব্যবহার করে), কারণ জেনেরিক TQueryযুক্তি সংকলনের সময় উপলব্ধ না হওয়ায় এই মুহুর্তে হ্যান্ডলার উদাহরণটি কাস্ট করা অসম্ভব । তবে, Handleপদ্ধতিটির নাম পরিবর্তন না করা বা অন্যান্য যুক্তি না পাওয়া পর্যন্ত এই কলটি কখনই ব্যর্থ হবে না এবং আপনি যদি চান, এই শ্রেণীর জন্য একটি ইউনিট পরীক্ষা লেখা খুব সহজ। প্রতিবিম্ব ব্যবহার করে সামান্য ড্রপ দেবে, তবে সত্যিই উদ্বিগ্ন হওয়ার মতো কিছু নয়।


আপনার উদ্বেগের একটি উত্তর দিতে:

সুতরাং আমি এমন বিকল্পগুলির সন্ধান করছি যা পুরো ক্যোয়ারিকে encapsulate করে, তবে এখনও যথেষ্ট নমনীয় যে আপনি কেবল কমান্ড ক্লাসগুলির বিস্ফোরণের জন্য স্প্যাগেটি রিপোজিটরিগুলি অদলবদল করছেন না।

এই নকশাটি ব্যবহারের একটি পরিণতি হ'ল সিস্টেমে প্রচুর ছোট ক্লাস থাকবে তবে প্রচুর ছোট / কেন্দ্রীভূত ক্লাস (স্পষ্ট নাম সহ) রাখা ভাল জিনিস। এই পদ্ধতির স্পষ্টতই আরও ভাল তবে একই পদ্ধতিতে বিভিন্ন প্যারামিটারগুলির সাথে অনেকগুলি একটি রিপোজিটরিতে বিভিন্ন পরামিতি রয়েছে, কারণ আপনি তাদের একটি ক্লাস শ্রেণিতে গ্রুপ করতে পারেন। সুতরাং আপনি এখনও একটি সংগ্রহস্থলের পদ্ধতির তুলনায় অনেক কম ক্যোয়ারী ক্লাস পান।


4
দেখে মনে হচ্ছে আপনি পুরষ্কারটি পেয়েছেন। আমি ধারণাগুলি পছন্দ করি, আমি কেবল আশা করছিলাম যে কেউ সত্যই আলাদা কিছু উপস্থাপন করবেন। অভিনন্দন.
এরিক ফানকেনবাশ

4
@ ফুরিচুরি, একটি একক শ্রেণিতে আসলেই কি 5 টি প্রশ্নের প্রয়োজন? সম্ভবত আপনি এটিকে অনেক দায়িত্ব নিয়ে ক্লাস হিসাবে দেখতে পারেন look বিকল্পভাবে, যদি প্রশ্নগুলি একত্রিত করা হয় তবে সম্ভবত তাদের আসলে একক কোয়েরি হওয়া উচিত। এগুলি অবশ্যই পরামর্শ মাত্র।
স্যাম

4
@ স্টাকেক্স আপনি একদম ঠিক বলেছেন যে আমার প্রাথমিক উদাহরণে ইন্টারফেসের জেনেরিক TResultপ্যারামিটারটি কার্যকর IQueryনয়। যাইহোক, আমার আপডেট হওয়া প্রতিক্রিয়াতে TResultপরামিতিটি রানটাইমের সময় সমাধানের Processপদ্ধতি দ্বারা ব্যবহৃত হয় । IQueryProcessorIQueryHandler
ডেভিড.এস

4
আমার একটি খুব একইরকম বাস্তবায়ন সহ একটি ব্লগ রয়েছে যা আমাকে সঠিক পথে নিয়ে যাওয়ার বিষয়টি তৈরি করে, এটি লিঙ্কটি jupaol.blogspot.mx/2012/11/… এবং আমি এটি কিছু সময়ের জন্য পিআরডি অ্যাপ্লিকেশনগুলিতে ব্যবহার করছি, তবে এই পদ্ধতির সাথে আমার সমস্যা হয়েছে। প্রশ্নগুলি শৃঙ্খলাবদ্ধ ও পুনরায় ব্যবহার করা যাক বলুন যে আমার কাছে আরও কয়েকটি জটিল প্রশ্ন রয়েছে যা আরও জটিল প্রশ্নগুলি তৈরি করার জন্য একত্রিত হওয়া দরকার , আমি কেবল কোডটি নকল করেছিলাম তবে আমি একটি মচ আরও ভাল এবং ক্লিনার পদ্ধতির সন্ধান করছি। কোন ধারনা?
Jupaol

4
@Cere আমি প্রসারিত পদ্ধতিগুলিতে ফিরে আসা IQueryableএবং সংগ্রহটি গণনা না করে তা নিশ্চিত করার জন্য আমার প্রশ্নগুলি শেষ করে দিয়েছি , তারপরে QueryHandlerআমি কেবল জিজ্ঞাসাগুলি / চেইন থেকে কল করেছি। এটি আমাকে আমার প্রশ্নগুলির ইউনিট পরীক্ষা করার এবং সেগুলি শৃঙ্খলাবদ্ধ করার নমনীয়তা দিয়েছে। আমার উপরে আমার একটি আবেদনকারী পরিষেবা রয়েছে QueryHandlerএবং আমার নিয়ামক হ্যান্ডলারের পরিবর্তে পরিষেবাটির সাথে সরাসরি কথা বলার দায়িত্বে আছেন
Jupaol

4

এটির সাথে আমার আচরণের পদ্ধতিটি আসলে সরলবাদী এবং ORM অজ্ঞাবলিক। একটি সংগ্রহস্থলের জন্য আমার মতামতটি হ'ল: সংগ্রহস্থলের কাজ অ্যাপটিকে প্রসঙ্গের জন্য প্রয়োজনীয় মডেল সরবরাহ করা, সুতরাং অ্যাপটি কেবল রেপোকে যা চায় তার জন্য জিজ্ঞাসা করে তবে কীভাবে তা পাবে তা তা বলে না।

আমি একটি মানদণ্ড (হ্যাঁ, ডিডিডি শৈলী) দিয়ে রিপোজিটরি পদ্ধতি সরবরাহ করি, যা কোপো তৈরি করতে রেপো ব্যবহার করবে (বা যা প্রয়োজন - এটি ওয়েবসার্চ অনুরোধ হতে পারে)। যোগদান এবং গোষ্ঠীগুলি ইমো হ'ল কীভাবে এবং কীভাবে কোন মানদণ্ড হওয়া উচিত সেখানে কোনও বিধি তৈরির জন্য ভিত্তি হওয়া উচিত তার বিশদ।

মডেল = অ্যাপ্লিকেশন দ্বারা চূড়ান্ত বস্তু বা ডেটা কাঠামো প্রয়োজন e

public class MyCriteria
{
   public Guid Id {get;set;}
   public string Name {get;set;}
    //etc
 }

 public interface Repository
  {
       MyModel GetModel(Expression<Func<MyCriteria,bool>> criteria);
   }

সম্ভবত আপনি যদি ওআরএম মানদণ্ড (নিবার্নেট) চান তবে সরাসরি এটি ব্যবহার করতে পারেন। সংগ্রহস্থল বাস্তবায়নের অন্তর্নিহিত স্টোরেজ বা ডিএও দিয়ে মানদণ্ডটি কীভাবে ব্যবহার করতে হবে তা জানা উচিত।

আমি আপনার ডোমেন এবং মডেলের প্রয়োজনীয়তাগুলি জানি না তবে সবচেয়ে ভাল উপায় যদি অ্যাপটি নিজেই কোয়েরিটি তৈরি করে। মডেলটি এতটাই পরিবর্তন করে যে আপনি কোনও স্থিতিশীল কোনও সংজ্ঞা দিতে পারবেন না?

এই সমাধানটির স্পষ্টতই কিছু অতিরিক্ত কোডের প্রয়োজন তবে এটি বাকী একটি ওআরএম বা স্টোরেজ অ্যাক্সেস করার জন্য যা কিছু ব্যবহার করছেন তা মিলবে না। সংগ্রহস্থলটি একটি মুখোমুখি হিসাবে কাজ করার জন্য কাজ করে এবং এটি পরিষ্কার হয় এবং 'মানদণ্ডের অনুবাদ' কোডটি পুনরায় ব্যবহারযোগ্য


এটি সংগ্রহস্থল বৃদ্ধির সমস্যাগুলি সমাধান করে না এবং বিভিন্ন ধরণের ডেটা ফেরত দেওয়ার পদ্ধতিগুলির ক্রমবর্ধমান তালিকা রয়েছে। আমি বুঝতে পেরেছি যে আপনি এটির সাথে সমস্যাটি দেখতে পাচ্ছেন না (অনেক লোক তা দেখেন না) তবে অন্যরা এটি অন্যরকম দেখতে পান (আমি প্রস্তাব করি যে নিবন্ধটি আমি লিঙ্ক করেছি সেগুলি পড়তে হবে, অনুরূপ মতামতযুক্ত আরও অনেক লোক রয়েছে)।
এরিক ফানকেনবাশ

4
আমি এটি সম্বোধন করি, কারণ মানদণ্ড প্রচুর পদ্ধতি অপ্রয়োজনীয় করে তোলে। অবশ্যই, তাদের সবার নয়, আপনার প্রয়োজনীয় টিং সম্পর্কে কিছু না জেনে আমি বেশি কিছু বলতে পারি না। আমি ইমপ্রেসের নীচে আছি যদিও আপনি সরাসরি ডিবিকে জিজ্ঞাসা করতে চান তবে সম্ভবত কোনও সংগ্রহস্থল ঠিক পথে চলছে। আপনার যদি সম্পর্কিত রিটার্শিয়াল সোটারেজের সাথে কাজ করার প্রয়োজন হয় তবে সরাসরি এর জন্য যান, কোনও ভাণ্ডারের দরকার নেই। এবং একটি নোট হিসাবে, কত লোক এই পোস্টটির সাথে আয়েন্দিকে উদ্ধৃত করেছে তা বিরক্তিকর। আমি এটির সাথে একমত নই এবং আমি মনে করি যে অনেক ডিভস কেবলমাত্র প্যাটার্নটিকে ভুল উপায়ে ব্যবহার করছে।
মাইকএসডব্লিউ

4
এটি সমস্যাটি কিছুটা কমিয়ে দিতে পারে, তবে যথেষ্ট পরিমাণে প্রয়োগের ফলে এটি দৈত্য ভান্ডারগুলি তৈরি করতে পারে। আমি মূল যুক্তিতে সরাসরি হাইবারনেট ব্যবহারের আইয়েন্দির সমাধানের সাথে একমত নই, তবে নিয়ন্ত্রণের সংগ্রহের বৃদ্ধির অযৌক্তিকতা সম্পর্কে আমি তার সাথে একমত নই। আমি সরাসরি ডাটাবেসটি জিজ্ঞাসা করতে চাই না, তবে আমি কেবল একটি সংগ্রহস্থল থেকে কোয়েরি অবজেক্টগুলির বিস্ফোরণে স্থানান্তরিত করতে চাই না।
এরিক ফানকেনবাশ

2

আমি এটি করেছি, এটি সমর্থন করেছি এবং এটি পূর্বাবস্থায় ফেলেছি।

প্রধান সমস্যাটি হ'ল আপনি এটি যেভাবেই করেন না কেন, যুক্ত বিমূর্ততা আপনাকে স্বাধীনতা দেয় না। এটি সংজ্ঞা অনুসারে ফাঁস হবে। সংক্ষেপে, আপনি কেবল আপনার কোডটি দেখতে সুন্দর লাগানোর জন্য একটি পুরো স্তর আবিষ্কার করছেন ... তবে এটি রক্ষণাবেক্ষণ হ্রাস করে না, পাঠযোগ্যতার উন্নতি করে না বা কোনও ধরণের মডেল অজ্ঞেয়বাদকে লাভ করে না।

মজাদার অংশটি হ'ল অলিভিয়ের প্রতিক্রিয়ার জবাবে আপনি আপনার নিজের প্রশ্নের উত্তর দিয়েছিলেন: "এটি লিনক থেকে প্রাপ্ত সমস্ত সুবিধা ছাড়াই লিনকের কার্যকারিতাটি নকল করে দিচ্ছে"।

নিজেকে জিজ্ঞাসা করুন: এটা কিভাবে হতে পারে না?


ভাল, আমি অবশ্যই লিনককে আপনার ব্যবসায়িক স্তরের সাথে সংহত করার সমস্যাগুলি অবশ্যই অনুভব করেছি। এটি খুব শক্তিশালী, তবে আমরা যখন ডেটা মডেল পরিবর্তন করি তখন এটি একটি দুঃস্বপ্ন। সংগ্রহস্থলগুলির সাহায্যে জিনিসগুলির উন্নতি হয়েছে, কারণ আমি ব্যবসায়ের স্তরকে খুব বেশি প্রভাবিত না করে স্থানীয়করণে পরিবর্তন আনতে পারি (পরিবর্তনের ক্ষেত্রে আপনাকে যখন ব্যবসায়ের স্তরটিও পরিবর্তন করতে হবে তা বাদে)। কিন্তু, সংগ্রহস্থলগুলি এই ফোলা স্তরগুলিতে পরিণত হয় যা এসআরপিকে ব্যাপকভাবে লঙ্ঘন করে। আমি আপনার বিষয়টি বুঝতে পারি, তবে এটি আসলে কোনও সমস্যার সমাধান করে না।
এরিক ফানকেনবাশ

যদি আপনার ডেটা স্তরটি লিনকিউ ব্যবহার করে এবং ডেটা মডেল পরিবর্তনের জন্য আপনার ব্যবসায়ের স্তরের পরিবর্তন দরকার ... আপনি সঠিকভাবে স্তর রাখছেন না।
স্টু

আমি ভেবেছিলাম আপনি বলছেন আপনি আর সেই স্তরটি যুক্ত করেন নি। আপনি যখন বলেন যে যুক্ত বিমূর্ততা আপনাকে কোনও উপকারে আসে না, তার থেকে বোঝা যায় যে আপনি আয়েন্দির সাথে এনওয়াইবারনেট অধিবেশন (বা ইএফ প্রসঙ্গে) সরাসরি আপনার ব্যবসায়ের স্তরে পাস করার বিষয়ে একমত হন।
এরিক ফানকেনবাশ

1

আপনি একটি সাবলীল ইন্টারফেস ব্যবহার করতে পারেন। প্রাথমিক ধারণাটি হ'ল কোনও শ্রেণির পদ্ধতিগুলি কিছু ক্রিয়া সম্পাদনের পরে এই খুব ক্লাসের বর্তমান অবস্থাটি ফিরিয়ে দেয়। এটি আপনাকে পদ্ধতি কলগুলি চেইন করতে দেয়।

উপযুক্ত শ্রেণিবদ্ধ স্তরক্রম তৈরি করে, আপনি অ্যাক্সেসযোগ্য পদ্ধতির লজিক্যাল প্রবাহ তৈরি করতে পারেন।

public class FinalQuery
{
    protected string _table;
    protected string[] _selectFields;
    protected string _where;
    protected string[] _groupBy;
    protected string _having;
    protected string[] _orderByDescending;
    protected string[] _orderBy;

    protected FinalQuery()
    {
    }

    public override string ToString()
    {
        var sb = new StringBuilder("SELECT ");
        AppendFields(sb, _selectFields);
        sb.AppendLine();

        sb.Append("FROM ");
        sb.Append("[").Append(_table).AppendLine("]");

        if (_where != null) {
            sb.Append("WHERE").AppendLine(_where);
        }

        if (_groupBy != null) {
            sb.Append("GROUP BY ");
            AppendFields(sb, _groupBy);
            sb.AppendLine();
        }

        if (_having != null) {
            sb.Append("HAVING").AppendLine(_having);
        }

        if (_orderBy != null) {
            sb.Append("ORDER BY ");
            AppendFields(sb, _orderBy);
            sb.AppendLine();
        } else if (_orderByDescending != null) {
            sb.Append("ORDER BY ");
            AppendFields(sb, _orderByDescending);
            sb.Append(" DESC").AppendLine();
        }

        return sb.ToString();
    }

    private static void AppendFields(StringBuilder sb, string[] fields)
    {
        foreach (string field in fields) {
            sb.Append(field).Append(", ");
        }
        sb.Length -= 2;
    }
}

public class GroupedQuery : FinalQuery
{
    protected GroupedQuery()
    {
    }

    public GroupedQuery Having(string condition)
    {
        if (_groupBy == null) {
            throw new InvalidOperationException("HAVING clause without GROUP BY clause");
        }
        if (_having == null) {
            _having = " (" + condition + ")";
        } else {
            _having += " AND (" + condition + ")";
        }
        return this;
    }

    public FinalQuery OrderBy(params string[] fields)
    {
        _orderBy = fields;
        return this;
    }

    public FinalQuery OrderByDescending(params string[] fields)
    {
        _orderByDescending = fields;
        return this;
    }
}

public class Query : GroupedQuery
{
    public Query(string table, params string[] selectFields)
    {
        _table = table;
        _selectFields = selectFields;
    }

    public Query Where(string condition)
    {
        if (_where == null) {
            _where = " (" + condition + ")";
        } else {
            _where += " AND (" + condition + ")";
        }
        return this;
    }

    public GroupedQuery GroupBy(params string[] fields)
    {
        _groupBy = fields;
        return this;
    }
}

আপনি এটিকে এভাবে ডাকবেন

string query = new Query("myTable", "name", "SUM(amount) AS total")
    .Where("name LIKE 'A%'")
    .GroupBy("name")
    .Having("COUNT(*) > 2")
    .OrderBy("name")
    .ToString();

আপনি কেবলমাত্র একটি নতুন উদাহরণ তৈরি করতে পারেন Query। অন্যান্য ক্লাসে সুরক্ষিত কনস্ট্রাক্টর রয়েছে। শ্রেণিবিন্যাসের মূল বিষয়টি হল "অক্ষম" পদ্ধতিগুলি। উদাহরণস্বরূপ, GroupByপদ্ধতিটি GroupedQueryএমন একটি ফেরত দেয় যা এর বেস শ্রেণি Queryএবং কোনও Whereপদ্ধতি নেই (যেখানে পদ্ধতিটি ঘোষণা করা হয় Query)। অতএব Whereপরে কল করা সম্ভব নয় GroupBy

এটি যদিও নিখুঁত নয়। এই শ্রেণীর শ্রেণিবিন্যাসের সাথে আপনি ক্রমাগতভাবে সদস্যদের আড়াল করতে পারেন, তবে নতুনকে প্রদর্শন করতে পারেন না। অতএব Havingযখন এটি ডাকা হয় তখন একটি ব্যতিক্রম ছুঁড়ে দেয় GroupBy

নোট করুন যে Whereকয়েকবার কল করা সম্ভব । এটি ANDবিদ্যমান শর্তগুলির সাথে একটি নতুন শর্ত যুক্ত করে । এটি একক শর্ত থেকে প্রোগ্রামগতভাবে ফিল্টারগুলি তৈরি করা সহজ করে তোলে। একই সঙ্গে সম্ভব Having

ক্ষেত্রের তালিকা গ্রহণের পদ্ধতিগুলির একটি প্যারামিটার রয়েছে params string[] fields। এটি আপনাকে একক ক্ষেত্রের নাম বা একটি স্ট্রিং অ্যারে পাস করার অনুমতি দেয়।


সাবলীল ইন্টারফেসগুলি খুব নমনীয় এবং প্যারামিটারগুলির বিভিন্ন সংমিশ্রণ সহ আপনাকে প্রচুর পরিমাণে ওভারলোড তৈরি করতে হবে না। আমার উদাহরণটি স্ট্রিংগুলির সাথে কাজ করে তবে এপ্রোচটি অন্য ধরণের ক্ষেত্রেও বাড়ানো যেতে পারে। আপনি বিশেষ ক্ষেত্রে বা কাস্টম ধরণের গ্রহণযোগ্য পদ্ধতিগুলির জন্য পূর্বনির্ধারিত পদ্ধতিগুলিও ঘোষণা করতে পারেন। আপনি পছন্দ করতে পদ্ধতি যোগ করতে পারিনি ExecuteReaderবা ExceuteScalar<T>। এটি আপনাকে এই জাতীয় প্রশ্নের সংজ্ঞা দেবে

var reader = new Query<Employee>(new MonthlyReportFields{ IncludeSalary = true })
    .Where(new CurrentMonthCondition())
    .Where(new DivisionCondition{ DivisionType = DivisionType.Production})
    .OrderBy(new StandardMonthlyReportSorting())
    .ExecuteReader();

এমনকি এসকিউএল কমান্ডগুলি এইভাবে নির্মিত কমান্ডের প্যারামিটার থাকতে পারে এবং এসকিউএল ইঞ্জেকশন সমস্যাগুলি এড়াতে পারে এবং একই সাথে কমান্ডগুলিকে ডাটাবেস সার্ভার দ্বারা ক্যাশে করার অনুমতি দেয়। এটি কোনও ও / আর-ম্যাপারের পরিবর্তে নয় তবে এমন পরিস্থিতিতে সহায়তা করতে পারে যেখানে আপনি অন্যথায় সরল স্ট্রিং কনটেন্টেশন ব্যবহার করে আদেশগুলি তৈরি করতে পারেন।


4
হুম .. আকর্ষণীয়, তবে আপনার সমাধানে এসকিউএল ইঞ্জেকশন সম্ভাবনার সমস্যা রয়েছে বলে মনে হচ্ছে এবং প্রাক-সংকলিত সম্পাদনের জন্য প্রস্তুত বিবৃতিগুলি সত্যই তৈরি করে না (এভাবে আরও ধীরে ধীরে সম্পাদন করা হচ্ছে)। সম্ভবত এই সমস্যাগুলি সমাধানের জন্য এটি রূপান্তর করা যেতে পারে তবে আমরা নন-টাইপ নিরাপদ ডেটাसेट ফলাফলের সাথে আটকে আছি এবং কী নয়। আমি একটি ওআরএম ভিত্তিক সমাধান পছন্দ করব এবং সম্ভবত আমার তা স্পষ্ট করে উল্লেখ করা উচিত। এটি লিনক থেকে প্রাপ্ত সমস্ত সুবিধা ছাড়াই মূলত লিনকের কার্যকারিতা ডুপ্লিকেট করছে।
এরিক ফানকেনবাশ

আমি এই সমস্যাগুলি সম্পর্কে সচেতন। এটি কেবলমাত্র একটি দ্রুত এবং নোংরা সমাধান, কীভাবে সাবলীল ইন্টারফেস তৈরি করা যায় তা দেখায়। একটি বাস্তব বিশ্বের সমাধানে আপনি সম্ভবত আপনার প্রয়োজনীয়তার সাথে অভিযোজিত একটি সাবলীল ইন্টারফেসে আপনার বিদ্যমান পদ্ধতির "বেক" করবেন।
অলিভিয়ার জ্যাকট-ডেসকোম্বেস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.